{"id":984,"date":"2016-10-15T07:41:41","date_gmt":"2016-10-15T14:41:41","guid":{"rendered":"http:\/\/blog.ls-al.com\/?p=984"},"modified":"2016-10-15T07:43:21","modified_gmt":"2016-10-15T14:43:21","slug":"amazon-ses-submission","status":"publish","type":"post","link":"https:\/\/blog.ls-al.com\/amazon-ses-submission\/","title":{"rendered":"Amazon SES Submission"},"content":{"rendered":"
I have experimented using the Amazon API to submit email to SES. As opposed to using SMTP the API may provide some advantages in some situations.<\/p>\n
I jotted down some notes how I used the API and CommandPool since it took me a while to get it to work.<\/p>\n
Out of scope:<\/strong> Input file so I can pass to CURL for testing.<\/strong><\/p>\n Run it from curl.<\/strong><\/p>\n Script to accept emails in JSON and use the CommandPool API for asynchronous promises.<\/strong><\/p>\n Test from a web page as opposed to CURL command line.<\/strong><\/p>\n I have experimented using the Amazon API to submit email to SES. As opposed to using SMTP the API may<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19],"tags":[68],"class_list":["post-984","post","type-post","status-publish","format-standard","hentry","category-php","tag-amazon-ses"],"_links":{"self":[{"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/posts\/984","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/comments?post=984"}],"version-history":[{"count":0,"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/posts\/984\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/media?parent=984"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/categories?post=984"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ls-al.com\/wp-json\/wp\/v2\/tags?post=984"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}
\n1. SES and DKIM setup.
\n2. Requesting\/Upping 1 email\/sec SES rate limits.
\n3. SES sandbox and TO\/FROM approvals.
\n4. Environment setup ie PHP 7\/Nginx.
\n5. KEY\/SECRET obfuscation from script ie store in hidden home folder.
\n6. API v1 and v2 versus v3 changes.<\/p>\n\r\n$ cat emails.inp \r\n[\r\n{"email":"success@simulator.amazonses.com"},{"email":"success@simulator.amazonses.com"},{"email":"success@simulator.amazonses.com"},{"email":"success@simulator.amazonses.com"},{"email":"success@simulator.amazonses.com"},{"email":"success@simulator.amazonses.com"},{"email":"success@simulator.amazonses.com"},{"email":"success@simulator.amazonses.com"},{"email":"success@simulator.amazonses.com"},{"email":"success@simulator.amazonses.com"}\r\n]\r\n<\/pre>\n
\r\n$ curl -H "Content-Type: application\/json" -X POST --data-binary @emails.inp http:\/\/myserver.com\/ses\/amazon-commandpool_v2.php\r\nAbout to send item 0 to: success@simulator.amazonses.com\r\nAbout to send item 1 to: success@simulator.amazonses.com\r\nAbout to send item 2 to: success@simulator.amazonses.com\r\nAbout to send item 3 to: success@simulator.amazonses.com\r\nAbout to send item 4 to: success@simulator.amazonses.com\r\nCompleted 0: 01000157c88196ba-e4a8d71a-c2a3-4d58-951a-e43639f29e05-000000 and result was: 200 \r\nAbout to send item 5 to: success@simulator.amazonses.com\r\nCompleted 2: 01000157c88196b8-4b4bb9c4-a5af-4cd3-ad0c-808817910d12-000000 and result was: 200 \r\nAbout to send item 6 to: success@simulator.amazonses.com\r\nCompleted 1: 01000157c88196b9-33fb8303-f7b5-451e-acd3-619b239053e6-000000 and result was: 200 \r\nAbout to send item 7 to: success@simulator.amazonses.com\r\nCompleted 3: 01000157c88196b6-9a5d8224-441a-4f12-8b80-137716e7e11c-000000 and result was: 200 \r\nAbout to send item 8 to: success@simulator.amazonses.com\r\nCompleted 4: 01000157c88196ba-c8cebab9-4397-490c-b425-10dcda4b99d4-000000 and result was: 200 \r\nAbout to send item 9 to: success@simulator.amazonses.com\r\nCompleted 5: 01000157c881972b-3e243162-1eb4-4ad3-8c39-16ce31e10144-000000 and result was: 200 \r\nCompleted 8: 01000157c881975b-64270cf4-9666-408b-b16f-510a6cb4ff70-000000 and result was: 200 \r\nCompleted 7: 01000157c881974d-a2f67bfb-6200-44be-9164-16d099ca6dcf-000000 and result was: 200 \r\nCompleted 6: 01000157c8819734-8b3c6347-4a10-44b3-bd59-4e75fc7a2e9f-000000 and result was: 200 \r\nCompleted 9: 01000157c88197bc-574976c4-22e0-41ef-a5b6-1e810b666b58-000000 and result was: 200 \r\n\r\nTotal Execution Time: 0.44663500785828 Sec(s)\r\n<\/pre>\n
\r\n# cat amazon-commandpool_v2.php\r\n<?php $time_start = microtime(true); if(strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') != 0){ \/\/throw new Exception('Request method must be POST!'); exit('Request method must be POST!'); } \/\/Make sure that the content type of the POST request has been set to application\/json $contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : ''; if(strcasecmp($contentType, 'application\/json') != 0){ \/\/throw new Exception('Content type must be: application\/json'); exit('Content type must be: application\/json'); } \/\/Receive the RAW post data. $content = trim(file_get_contents("php:\/\/input")); \/\/Attempt to decode the incoming RAW post data from JSON. $decoded = json_decode($content, true); \/\/If json_decode failed, the JSON is invalid. if(!is_array($decoded)){ \/\/throw new Exception('Received content contained invalid JSON!'); exit('Received content contained invalid JSON!'); } require '\/sites1\/myserver.com\/web\/ses\/aws-autoloader.php'; use Aws\\Exception\\AwsException; use Aws\\Ses\\SesClient; use Aws\\CommandPool; use Aws\\CommandInterface; use Aws\\ResultInterface; use GuzzleHttp\\Promise\\PromiseInterface; define('REGION','us-east-1'); $client = SesClient::factory([ 'version'=> 'latest',\r\n 'region' => REGION,\r\n 'credentials' => [\r\n 'key' => 'MYKEY',\r\n 'secret' => 'MYSECRET',\r\n ]\r\n]);\r\n\r\ndefine('SENDER', 'myfromaddress@mydomain.com');\r\n\/\/define('RECIPIENT', array('success@simulator.amazonses.com','success@simulator.amazonses.com'));\r\n\/\/define('RECIPIENT', array('success@simulator.amazonses.com'));\r\ndefine('SUBJECT','Amazon SES test (AWS SDK for PHP)');\r\ndefine('BODY','This email was sent with Amazon SES using the AWS SDK for PHP.');\r\n\r\n\/\/$addresses = RECIPIENT;\r\n$addresses = array_column($decoded, 'email');\r\n\r\n$commandGenerator = function ($addresses) use ($client) {\r\n foreach ($addresses as $address) {\r\n \/\/ Yield a command that will be executed by the pool.\r\n\t$request = array();\r\n\t$request['Source'] = SENDER;\r\n\t$request['Destination']['ToAddresses'] = array($address);\r\n\t$request['Message']['Subject']['Data'] = SUBJECT;\r\n\t$request['Message']['Body']['Text']['Data'] = BODY;\r\n yield $client->getCommand('SendEmail', \r\n $request\r\n );\r\n }\r\n};\r\n\r\n$commands = $commandGenerator($addresses);\r\n\r\n$pool = new CommandPool($client, $commands, [\r\n 'concurrency' => 5,\r\n 'before' => function (CommandInterface $cmd, $iterKey) {\r\n\t$a = $cmd->toArray();\r\n echo "About to send item {$iterKey} to: " \r\n . $a['Destination']['ToAddresses'][0] . "\\n";\r\n \/\/. print_r($cmd->toArray(), true) . "\\n";\r\n },\r\n 'fulfilled' => function (\r\n ResultInterface $result,\r\n $iterKey,\r\n PromiseInterface $aggregatePromise\r\n ) {\r\n echo "Completed {$iterKey}: {$result['MessageId']} and result was: {$result['@metadata']['statusCode']} \\n";\r\n \/\/echo "Completed {$iterKey}: {$result}\\n";\r\n },\r\n 'rejected' => function (\r\n AwsException $reason,\r\n $iterKey,\r\n PromiseInterface $aggregatePromise\r\n ) {\r\n echo "Failed {$iterKey}: {$reason}\\n";\r\n },\r\n\r\n]);\r\n\r\n\/\/ Initiate the pool transfers\r\n$promise = $pool->promise();\r\n\r\n\/\/ Force the pool to complete synchronously\r\n$promise->wait();\r\n\r\n\/\/ Or you can chain then calls off of the pool\r\n\/\/$promise->then(function() { echo "Done\\n" };\r\n\r\n$time_end = microtime(true);\r\n\/\/dividing with 60 will give the execution time in minutes other wise seconds\r\n\/\/$execution_time = ($time_end - $time_start)\/60;\r\n$execution_time = ($time_end - $time_start);\r\n\/\/execution time of the script\r\necho "\\nTotal Execution Time: ".$execution_time." Sec(s)\\n";\r\n<\/pre>\n
\r\n# cat amazon-commandpool_v2_test.php\r\n<?php \/\/API Url $url = 'http:\/\/myserver.com\/ses\/amazon-commandpool_v2.php'; \/\/Initiate cURL. $ch = curl_init($url); \/\/The JSON data. $jsonData = array( array('email' => 'success@simulator.amazonses.com'),\r\n array('email' => 'success@simulator.amazonses.com'),\r\n array('email' => 'success@simulator.amazonses.com')\r\n);\r\n \r\n\/\/Encode the array into JSON.\r\n$jsonDataEncoded = json_encode($jsonData);\r\n \r\n\/\/Tell cURL that we want to send a POST request.\r\ncurl_setopt($ch, CURLOPT_POST, 1);\r\n \r\n\/\/Attach our encoded JSON string to the POST fields.\r\ncurl_setopt($ch, CURLOPT_POSTFIELDS, $jsonDataEncoded);\r\n \r\n\/\/Set the content type to application\/json\r\ncurl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application\/json')); \r\n \r\n\/\/Execute the request\r\n$result = curl_exec($ch);\r\n?>\r\n\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"