Nginx Luaでmailを送信する方法
Nginx Luaでそのままmailを送信すると、lua coroutine: runtime error: attempt to yield across C-call boundary というエラーが出てしまいます。
このエラーを解消してメールを送信する方法をメモしておきます。
下のリンクの4番目のコメントを参考にしました。
stackoverflow.com
smtp.send uses LuaSocket's socket.protect function for handling internal errors. This function is implemented in C and doesn't allow yielding in the current releases (the version in git HEAD now allows yielding on Lua 5.2+, see discussion here). Apparently someone tries to yield from within it. In etc/dispatch.lua in the LuaSocket package (better use the git HEAD version) there is a replacement function for socket.protect that should allow yielding on all Lua versions (at the cost of an extra temporary coroutine). You can try replacing the C function with that Lua function like so:
- attempt to yield across C-call boundaryエラーが出るコード
- attempt to yield across C-call boundaryエラー内容
- attempt to yield across C-call boundaryエラー解消コード
- 最後に
attempt to yield across C-call boundaryエラーが出るコード
まずはエラーが出るコードをのせます。
local smtp = require("socket.smtp") local mime = require("mime") local function headerencode(src) return "=?UTF-8?B?" .. mime.b64(src) .. "?=" end from = "<fromアドレス>" rcpt = { "<toアドレス>" } mesgt = { headers = { from = headerencode("日本語名前") .. " <fromアドレス>", to = " <toアドレス>", subject = headerencode("日本語タイトル"), ["content-type"] = 'text/plain; charset="utf-8"' }, body = "日本語ボディ" } r, e = smtp.send{ from = from, rcpt = rcpt, source = smtp.message(mesgt), server = 'localhost', }
これをNginx Luaで呼び出すと下のエラーが出ます。
lua coroutine: runtime error: attempt to yield across C-call boundary
Nginxを使わないでLuaのみで呼び出すとエラーは出ません。
attempt to yield across C-call boundaryエラー内容
続いてエラーの内容を見ていきます。
lua coroutine: runtime error はコルーチン(マルチスレッド的な意味)でエラーが出ていますよ。ということ。
明示しなくてもNginxでsmtp.sendを呼び出すとコルーチンで呼ばれるようです。
attempt to yield across C-call boundaryというのはC言語で書かれた部分でエラーが出ていますよ。ということ。
なので大元の.soファイルを修正するか、呼び出し方を変える必要があります。
参考にしたリンク先では、呼び出し方を変えています。
下のコードでprotectを上書きしましょうということで、この通りにコピペして使用したところエラーが解消されました。
github.com
attempt to yield across C-call boundaryエラー解消コード
エラー解消したコードをのせておきます。
local socket = require("socket") local base = _G if string.sub(base._VERSION, -3) == "5.1" then local function _protect(co, status, ...) if not status then local msg = ... if base.type(msg) == 'table' then return nil, msg[1] else base.error(msg, 0) end end if coroutine.status(co) == "suspended" then return _protect(co, coroutine.resume(co, coroutine.yield(...))) else return ... end end function socket.protect(f) return function(...) local co = coroutine.create(f) return _protect(co, coroutine.resume(co, ...)) end end end local smtp = require("socket.smtp") local mime = require("mime") local function headerencode(src) return "=?UTF-8?B?" .. mime.b64(src) .. "?=" end from = "<fromアドレス>" rcpt = { "<toアドレス>" } mesgt = { headers = { from = headerencode("日本語名前") .. " <fromアドレス>", to = " <toアドレス>", subject = headerencode("日本語タイトル"), ["content-type"] = 'text/plain; charset="utf-8"' }, body = "日本語ボディ" } r, e = smtp.send{ from = from, rcpt = rcpt, source = smtp.message(mesgt), server = 'localhost', }
最後に
これでNginx Luaでmailを出せました。
過去に書いたSendGridの記事と合わせると、SendGridで受信したmailをLuaで送信するようなことができそうです。
yamano3201.hatenablog.jp
yamano3201.hatenablog.jp
気が向いたら、また書きます。