Hello!
On Sun, Aug 26, 2012 at 3:26 AM, clubpetey wrote:
>
> I am trying to use LUA to send a email via SMTP. I've load the smtp modules from the luasockets package, but during the send() function I recieve the following error:
>
> 2012/08/26 10:00:57 [error] 2733#0: *5 lua handler aborted: runtime error: attempt to yield across metamethod/C-call boundary
> stack traceback:
> [C]: in function 'send'
> /usr/local/nginx/conf/lua/refer-friend.lua:60: in function while sending to client, client: 127.0.0.1,
>
> I've already changed the messages socket "create" function to ngx.socket.tcp as I read that ngx.socket is compatible with luasockets, I believe the error is due to the smtp library calling socket.protect() I've read in other places that protect() can yield. Here's the code of the send function:
>
> send = socket.protect(function(mailt)
> local s = open(mailt.server, mailt.port, mailt.create)
> local ext = s:greet(mailt.domain)
> s:auth(mailt.user, mailt.password, ext)
> s:send(mailt)
> s:quit()
> return s:close()
> end)
>
> Is this a bug? or expected behavior? If it's expected, is there a way around it?
>
Okay, I've just coded up a complete code sample that enforces the
socket.smtp library shipped with LuaSocket 2.0.2 to use ngx_lua's TCP
cosocket API instead of LuaSocket's own.
The nginx.conf snippet is like this:
location = /t {
content_by_lua_file html/send.lua;
}
And then in the html/send.lua file, we first extend ngx_lua's
ngx.socket API to the extend that socket.smtp will be happy enough:
local socket = ngx.socket
socket._VERSION = "ngx_lua cosocket"
socket.protect = function (f)
local rm = table.remove
return function (...)
local rets = {pcall(f, ...)}
if rets[1] then
rm(rets, 1);
return unpack(rets)
else
local err = rets[2]
if type(err) == "table" then
return nil, err[1]
else
return error(err)
end
end
end
end
socket.sink = function (name, sock)
if name ~= 'keep-open' then
return error(name .. " not supported")
end
return setmetatable({}, {
__call = function (self, chunk, err)
if chunk then return sock:send(chunk)
else return 1 end
end
})
end
socket.newtry = function (f)
return function (...)
local args = {...}
if not args[1] then
if f then
pcall(f)
end
return error({args[2]})
end
return ...
end
end
socket.try = socket.newtry()
socket.skip = function (d, ...)
local args = {...}
local rm = table.remove
for i = 1, d do
rm(args, 1)
end
return unpack(args)
end
And then we make Lua believe that the "socket" module is already
loaded (and it is actually our ngx.socket instead of LuaSocket's):
package.loaded.socket = ngx.socket
Then we load LuaSocket's socket.smtp module and use it as usual:
local smtp = require("socket.smtp")
local mime = require("mime")
local ltn12 = require("ltn12")
source = smtp.message{
headers = {
from = "agentzh <age...@gmail.com>",
to = "foo <foo@bar.com>",
subject = "Hello, guy!"
},
body = {
preamble = "preamble",
[1] = { body = mime.eol(0, "Hello, how is going?") },
}
}
r, e = smtp.send{
from = "<sicrano@example.com>",
rcpt = "<fulano@example.com>",
source = source,
server = "127.0.0.1",
port = 25,
}
if not r then
ngx.say("failed to send: ", e)
end
ngx.say("done!")
And then using an HTTP client like "curl" to access the location /t
defined above:
$ curl localhost/t
done!
And now I can get the message on my local SMTP server listening on the
port 25, as follows:
From: <sicrano@example.com>
To: <fulano@example.com>
Message:
mime-version: 1.0
content-type: multipart/mixed; boundary="2608201217115879420==00001"
from: agentzh <age...@gmail.com>
date: Mon, 27 Aug 2012 00:11:58 -0000
x-mailer: ngx_lua cosocket
to: foo <foo@bar.com>
subject: Hello, guy!
preamble
--2608201217115879420==00001
content-type: text/plain; charset="iso-8859-1"
Hello, how is going?
--2608201217115879420==00001--
So it works as expected. But I'd rather implement a lua-resty-smtp
library from scratch atop ngx_lua's cosocket API instead of using the
socket.smtp library which is not implemented in the simplest and
fastest way.
Best regards,
-agentzh