意外と知らないメールの送受信周りのこと
こんにちは、いっせいです
先日ActionMailerのテストをしていて解決方法を探っていく中で新しく知ったことがあったのでご紹介したいと思います。
きっかけの出来事
きっかけはメールの本文中に意図する文言が含まれているかどうかを検証するテストがたまに失敗するということがあったことです。
ざっくり以下のようなコードとテストを書いていました。
(イメージです)
class SampleMailer < ApplicationMailer
def send_mail
body = build_body
mail from: "[Carely] <no-reply@mail.carely.io>", subject: "title" do |format|
format.html { render 'sample_mailer/send_mail' }
end
end
end
describe SampleMailer do
context "failed test" do
let!(:mail) do
SampleMailer.send_mail
end
it "期待する文字が本文が含まれている" do
expect(mail.body.encoded).to include("hogehoge")
end
end
end
結論からいうとテストがたまに失敗する原因は mail.body.encoded
と encoded
メソッド使っていたのがよくなかったのですが、そこに気づくまでに初めて知ったことが2つありました。
失敗するときには以下のようにテストが落ちていました
(イメージです)
expected "<html><body>=E5=90=88=E8=B3=87=E4=BC=9A=
+=E7=A4=BE=E6=B2=B3=E9=87=8E=E6=B0=B4=E7=94=A3<br/>itjtwuj2up=E6=A7=98<br/=
+><br/>=E3=81=8A=E4=B8=96=E8=A9=B1=E3=81=AB=E3=81=AA=E3=81=</body></html>" to include "hogehoge"
Diff:
@@ -1,2 +1,418 @@
-hoghoge
<html>
<body>
<div>
+ =E5=90=88=E8=B3=87=E4=BC=9A=
+=E7=A4=BE=E6=B2=B3=E9=87=8E=E6=B0=B4=E7=94=A3<br/>itjtwuj2up=E6=A7=98<br/=
+><br/>=E3=81=8A=E4=B8=96=E8=A9=B1=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=A6=E3=81=
+=8A=E3=82=8A=E3=81=BE=E3=81=99=E3=80=82<br/>Carely=E5=81=A5=E5=BA=B7=E8=A8=
+=BA=E6=96=AD=E3=82=B5=E3=83=9D=E3=83=BC=E3=83=88=E3=83=87=E3=82=B9=E3=82=AF=
+ </div>
+</body>
+</html>
日本語が知らない形式で文字化けしていたのです。
Quoted Printable というエンコード方式
ASCII_8BIT(BINARY)でエンコードされているのか?
URLエンコードされているのか?
似ているけど、どちらもイコールで始まらないよな?
URLエンコードなら <
なども変換されるよな。。。?
といろいろ調べていた時、文字化けした本文をこちらのサイトでデコードしてみることにしました。
ただ一つ正しくデコードできたエンコードがありました。
それが Quoted-Printable でした
BASE64と同じく非ASCII文字をASCII文字で表現する方式だそうです。
知らなかった....
またメールのエンコード・デコードはヘッダーの
Content-Transfer-Encoding
で設定されるということも知りました。
RFC も読もう
しかしテストコードは毎回上記のように失敗するわけでなく、数回に一度失敗していました。
mail.body
は Mail::Message
オブジェクトだったので
mail gem の中身を覗いてみました。
(使用しているバージョンは2.6.6)
encode
メソッドからデコードに使うエンコードを判定している箇所を読み進めていくと興味深いことがかいてありました。
https://github.com/mikel/mail/blob/2.6.6/lib/mail/body.rb#L157
https://github.com/mikel/mail/blob/2.6.6/lib/mail/body.rb#L144
https://github.com/mikel/mail/blob/2.6.6/lib/mail/encodings/8bit.rb#L29-L32
# Per RFC 2821 4.5.3.1, SMTP lines may not be longer than 1000 octets including the <CRLF>.
def self.compatible_input?(str)
!str.lines.find { |line| line.length > 998 }
end
コメントに「SMTPでは改行を含む一行は1000文字を超えてはいけない」とありました。
https://www.ietf.org/rfc/rfc2821.txt
これも初めて知りました。
SMTPのRFCを読んだことがなかったので初めて知りました。
今回のトラブルを直接解決するものではありませんが、やはり仕様をしっておくのは大切ですね。
ちなみに
今回の箇所ではメール本文をHTMLで組んでいたので見た目は改行していても一行で書かれている箇所があり、
テストデータによっては1000文字を超えてしまうことがありました。
これによって encode
メソッドはQuoted-Printable
でエンコードしていたのです。
そもそも本文の中身を検証するのにencoded
を使っていたのがよくなかったのでto_s
を使うことで解決をしました
https://github.com/mikel/mail#encodings
おわりに
トラブルシューティングはいろいろなことを教えてくれますねw
みなさんもお使いの技術に関するRFCや仕様書などを見てみると新しい発見があるかもしれませんよ!
ではまた!