Amazon SES Submission
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.
I jotted down some notes how I used the API and CommandPool since it took me a while to get it to work.
Out of scope:
1. SES and DKIM setup.
2. Requesting/Upping 1 email/sec SES rate limits.
3. SES sandbox and TO/FROM approvals.
4. Environment setup ie PHP 7/Nginx.
5. KEY/SECRET obfuscation from script ie store in hidden home folder.
6. API v1 and v2 versus v3 changes.
Input file so I can pass to CURL for testing.
$ cat emails.inp [ {"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"} ]
Run it from curl.
$ curl -H "Content-Type: application/json" -X POST --data-binary @emails.inp http://myserver.com/ses/amazon-commandpool_v2.php About to send item 0 to: success@simulator.amazonses.com About to send item 1 to: success@simulator.amazonses.com About to send item 2 to: success@simulator.amazonses.com About to send item 3 to: success@simulator.amazonses.com About to send item 4 to: success@simulator.amazonses.com Completed 0: 01000157c88196ba-e4a8d71a-c2a3-4d58-951a-e43639f29e05-000000 and result was: 200 About to send item 5 to: success@simulator.amazonses.com Completed 2: 01000157c88196b8-4b4bb9c4-a5af-4cd3-ad0c-808817910d12-000000 and result was: 200 About to send item 6 to: success@simulator.amazonses.com Completed 1: 01000157c88196b9-33fb8303-f7b5-451e-acd3-619b239053e6-000000 and result was: 200 About to send item 7 to: success@simulator.amazonses.com Completed 3: 01000157c88196b6-9a5d8224-441a-4f12-8b80-137716e7e11c-000000 and result was: 200 About to send item 8 to: success@simulator.amazonses.com Completed 4: 01000157c88196ba-c8cebab9-4397-490c-b425-10dcda4b99d4-000000 and result was: 200 About to send item 9 to: success@simulator.amazonses.com Completed 5: 01000157c881972b-3e243162-1eb4-4ad3-8c39-16ce31e10144-000000 and result was: 200 Completed 8: 01000157c881975b-64270cf4-9666-408b-b16f-510a6cb4ff70-000000 and result was: 200 Completed 7: 01000157c881974d-a2f67bfb-6200-44be-9164-16d099ca6dcf-000000 and result was: 200 Completed 6: 01000157c8819734-8b3c6347-4a10-44b3-bd59-4e75fc7a2e9f-000000 and result was: 200 Completed 9: 01000157c88197bc-574976c4-22e0-41ef-a5b6-1e810b666b58-000000 and result was: 200 Total Execution Time: 0.44663500785828 Sec(s)
Script to accept emails in JSON and use the CommandPool API for asynchronous promises.
# cat amazon-commandpool_v2.php <?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', 'region' => REGION, 'credentials' => [ 'key' => 'MYKEY', 'secret' => 'MYSECRET', ] ]); define('SENDER', 'myfromaddress@mydomain.com'); //define('RECIPIENT', array('success@simulator.amazonses.com','success@simulator.amazonses.com')); //define('RECIPIENT', array('success@simulator.amazonses.com')); define('SUBJECT','Amazon SES test (AWS SDK for PHP)'); define('BODY','This email was sent with Amazon SES using the AWS SDK for PHP.'); //$addresses = RECIPIENT; $addresses = array_column($decoded, 'email'); $commandGenerator = function ($addresses) use ($client) { foreach ($addresses as $address) { // Yield a command that will be executed by the pool. $request = array(); $request['Source'] = SENDER; $request['Destination']['ToAddresses'] = array($address); $request['Message']['Subject']['Data'] = SUBJECT; $request['Message']['Body']['Text']['Data'] = BODY; yield $client->getCommand('SendEmail', $request ); } }; $commands = $commandGenerator($addresses); $pool = new CommandPool($client, $commands, [ 'concurrency' => 5, 'before' => function (CommandInterface $cmd, $iterKey) { $a = $cmd->toArray(); echo "About to send item {$iterKey} to: " . $a['Destination']['ToAddresses'][0] . "\n"; //. print_r($cmd->toArray(), true) . "\n"; }, 'fulfilled' => function ( ResultInterface $result, $iterKey, PromiseInterface $aggregatePromise ) { echo "Completed {$iterKey}: {$result['MessageId']} and result was: {$result['@metadata']['statusCode']} \n"; //echo "Completed {$iterKey}: {$result}\n"; }, 'rejected' => function ( AwsException $reason, $iterKey, PromiseInterface $aggregatePromise ) { echo "Failed {$iterKey}: {$reason}\n"; }, ]); // Initiate the pool transfers $promise = $pool->promise(); // Force the pool to complete synchronously $promise->wait(); // Or you can chain then calls off of the pool //$promise->then(function() { echo "Done\n" }; $time_end = microtime(true); //dividing with 60 will give the execution time in minutes other wise seconds //$execution_time = ($time_end - $time_start)/60; $execution_time = ($time_end - $time_start); //execution time of the script echo "\nTotal Execution Time: ".$execution_time." Sec(s)\n";
Test from a web page as opposed to CURL command line.
# cat amazon-commandpool_v2_test.php <?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'), array('email' => 'success@simulator.amazonses.com'), array('email' => 'success@simulator.amazonses.com') ); //Encode the array into JSON. $jsonDataEncoded = json_encode($jsonData); //Tell cURL that we want to send a POST request. curl_setopt($ch, CURLOPT_POST, 1); //Attach our encoded JSON string to the POST fields. curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonDataEncoded); //Set the content type to application/json curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); //Execute the request $result = curl_exec($ch); ?>