読者です 読者をやめる 読者になる 読者になる

プログラマでありたい

おっさんになっても、プログラマでありつづけたい

Amazon Simple Email Service(SES)のメール送信テスト用機能(Amazon SES Mailbox Simulator)を試してみた

 AWSのサービス群の中で、地味だけど有難いサービスの一つにAmazon Simple Email Service(SES)があります。他のサービスに負けずに、どんどん便利な機能が追加されていっています。その中の一つが2012年10月に追加されたメール送信テスト用機能です。その名も、Amazon SES Mailbox Simulator。これは何かというと、かならず所定の結果を返してくれるメールアドレスです。
 使い道として2つ考えられます。まずは、Amazon自身が語っているように、不正送信のテスト等でテスト実施者のアカウントの評価(レピュテーション)を下げない為です。次に、メール送信事態の自動テスト(ユニットテスト)をやりやすくする効果があります。一般的にシステム外へのテストは、困難を伴います。特にメール等の場合、相手先があってのサービスです。それを期待した結果を返してくれるアドレスがあれば、非常に有難いです。

 宛先の種類としては、以下の5つがあります。

種類 宛先 備考
正常メール success@simulator.amazonses.com 正常に配信されたものとして扱われる
Unknown User bounce@simulator.amazonses.com SMTP 550 ("Unknown User")のレスポンスコードで拒否される
不在メッセージ(OOTO) ooto@simulator.amazonses.com Mailbox SimulatorはAmazon SESに不在メッセージ(OOTO)を返信する。
スパム扱い complaint@simulator.amazonses.com “スパム”だと設定し、ISPがAmazon SESにクレーム応答を送信するといったケースをシミュレートする
送信ブロック blacklist@simulator.amazonses.com Amazon SESが送信をブロックし、"Address Blacklisted" エラーメッセージを含んだMessageRejectedエラーを返す


 それでは、少し試してみましょう。以前、RubyでSESを使うコードを書いたので、ほぼそのままでテスト。

# -*- encoding: utf-8 -*-
require 'rubygems'
require 'aws-sdk'
require 'nkf'

ses = AWS::SimpleEmailService.new(
  :access_key_id => ENV['AWS_ACCESS_ID'],
  :secret_access_key => ENV['AWS_SECRET_KEY'])


#ISO-2022-jp
body_text = NKF.nkf('-jw','本文Test')
subject = NKF.nkf('-Mw', 'タイトルTest')

res = ses.send_email(
  :subject => subject,
  :to => 'bounce@simulator.amazonses.com',
  :from => 'hoge@example.com',
  :body_text => body_text,
  :body_text_charset => 'ISO-2022-JP'
)
p res

これの結果は、無情にもnil。あれと思ってドキュメントを見てみました。

Instance Method Summary (collapse)

  • (EmailAddressCollection) email_addresses

Returns a collection that represents all of the verified email addresses for your account.

  • (IdentityCollection) identities
  • (Hash) quotas

Returns a hash of SES quotas and limits.

  • (nil) send_email(options = {})

Sends an email.

  • (nil) send_raw_email(raw_message, options = {}) (also: #deliver, #deliver!)

Sends a raw email (email message, with header and content specified).

  • (Array of Hashes) statistics

Returns an array of email statistics.

nilを返すっぽいです。


 一応、ソースも見てみました。

    def send_email options = {}

        require_each(options, :subject, :from)
        require_one_of(options, :to, :cc, :bcc)
        require_one_of(options, :body_text, :body_html)

      # these three options can be passed strings or arrays of strings,
      # but the service requires them in a list (array)
      [:to, :cc, :bcc, :reply_to].each do |key|
        if options[key]
          options[key] = [options[key]].flatten
        end
      end

      accepted_options = {
        :subject           => %w(message subject data),
        :subject_charset   => %w(message subject charset),
        :to                => %w(destination to_addresses),
        :cc                => %w(destination cc_addresses),
        :bcc               => %w(destination bcc_addresses),
        :from              => %w(source),
        :from              => %w(source),
        :reply_to          => %w(reply_to_addresses),
        :return_path       => %w(return_path),
        :body_text         => %w(message body text data),
        :body_text_charset => %w(message body text charset),
        :body_html         => %w(message body html data),
        :body_html_charset => %w(message body html charset),
      }

      client.send_email(nest_options(options, accepted_options))
      nil

    end

nilを返すっぽいです。


仕方がないので、Java版を試してみました。Javaの使い方は忘却の彼方なので、Glacierの時に設定したモノを参考に

import java.io.IOException;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailService;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient;
import com.amazonaws.services.simpleemail.model.Body;
import com.amazonaws.services.simpleemail.model.Content;
import com.amazonaws.services.simpleemail.model.Destination;
import com.amazonaws.services.simpleemail.model.Message;
import com.amazonaws.services.simpleemail.model.SendEmailRequest;
import com.amazonaws.services.simpleemail.model.SendEmailResult;

public class Ses {
    
    public static void main(String[] args) throws IOException {
        
        AWSCredentials credentials = new PropertiesCredentials(
        		Sample.class.getResourceAsStream("AwsCredentials.properties"));
        AmazonSimpleEmailService sesClient = new AmazonSimpleEmailServiceClient(credentials);
        SendEmailRequest sendRequest = new SendEmailRequest();

        String toAddress = "bounce@simulator.amazonses.com";
        String fromAddress ="hoge@example.com";
        sendRequest.setDestination(new Destination().withToAddresses(toAddress));
        Message message = new Message();
        Content subject = new Content().withData("Subject Test");
        message.setSubject(subject);
        Content text = new Content().withData("Body Test");
        Body body = new Body().withText(text);
        message.setBody(body);
        sendRequest.setMessage(message);
        sendRequest.setSource(fromAddress);

       
        SendEmailResult result = new SendEmailResult();
        result = sesClient.sendEmail(sendRequest);
        System.out.println(result.getMessageId());
    }
}

 しかし、Java版はSendEmailResultクラスはあるものの、メッセージIDしか保持しておりません。何か根本的に勘違いしている模様です。使用例をググってみると、suz-labさんが使い方の解説をしてくれていました。どうやらSNSに通知してくれる模様です。
 考えてみれば当たり前ですね。メールの送信は非同期の処理なのに、即座に応答コードが返ってくると期待するのが間違いでした。(というより何を思って返り値に入ってくると思ったのか、ちょっと前の自分を問い詰めたい。)


 と言う事で気を取り直して、管理コンソールのSESのVerified Sendersの所からテストに使うアドレスに設定を行います。メールアドレスを選んで、Feedback DetailsでEdit Configurationを行います。SNS Configurationで任意のSNSのトピックを指定します。
[:large]


 そして、テスト送信。無事にメッセージが来ました。

{"notificationType":"Bounce","bounce":{"reportingMTA":"dns; a194-13.smtp-out.amazonses.com","bounceType":"Permanent","bouncedRecipients":[{"emailAddress":"bounce@simulator.amazonses.com","status":"5.0.0","diagnosticCode":"smtp; 5.1.0 - Unknown address error 550-'5.1.1 user unknown' (delivery attempts: 0)","action":"failed"}],"bounceSubType":"General","timestamp":"2012-12-16T08:28:30.000Z","feedbackId":"0000013ba2d42884-92eefaa4-475a-11e2-bb75-6b3d4723cd04-000000"},"mail":{"timestamp":"2012-12-16T08:28:30.000Z","source":"hoge@example.com","messageId":"0000013ba2d42676-c36edf1f-645c-4b8a-9b70-948ba98291e4-000000","destination":["bounce@simulator.amazonses.com"]}}


 自動テストに使えると思ったので、とんでもない方向違いをしていましたが、使い方は一応解りました。一方でSNS対応ということで、プログラマブルな検証が可能なので有難いですね。もう少し探ってみます。Let's try!


See Also:
RubyからAmazon SESでメールを送る
Amazon Glacierのサンプルソース アップロード編
AWSで大量メール配信するなら、Amazon SESで決まり


参照:
suz-lab - blog: "Amazon SES Mailbox Simulator"を試してみた
【AWS発表】Mailbox Simulator - Amazon Simple Email Serviceにメール送信テスト用機能が追加! - Amazon Web Services ブログ
【AWS発表】Simple Email Service (SES) にプログラム可能なフィードバック通知機能が追加 - Amazon Web Services ブログ
【AWS発表】 Amazon Simple Email Service (SES)がSMTPをサポート - Amazon Web Services ブログ
【AWS発表】Amazon Simple Email Service (SES)でドメイン一括検証が可能に