├── NEWS ├── AUTHORS ├── ChangeLog ├── Makefile.am ├── src ├── c │ ├── dns │ │ ├── libdns │ │ │ └── Makefile.am │ │ ├── Makefile.am │ │ ├── hosts.c │ │ └── resolv_conf.c │ ├── misc.h │ ├── Makefile.am │ └── ratchet.h └── lua │ ├── bus │ ├── init.lua │ ├── samestate_transaction.lua │ ├── server_transaction.lua │ ├── client.lua │ ├── samestate.lua │ ├── client_transaction.lua │ └── server.lua │ ├── smtp │ ├── smtp_reply.lua │ ├── smtp_extensions.lua │ ├── data_sender.lua │ ├── data_reader.lua │ ├── smtp_io.lua │ └── client.lua │ ├── Makefile.am │ ├── http │ ├── common.lua │ ├── client.lua │ └── server.lua │ └── socketpad │ └── init.lua ├── test ├── test_5sec_timer.lua ├── test_timerfd.lua ├── test_callable_object.lua ├── test_pause_unpause.lua ├── test_event_timeout.lua ├── test_wait_all.lua ├── test_pcall_kernel_loop.lua ├── test_socketpair.lua ├── test_listen_connect.lua ├── test_thread_alarm.lua ├── test_zmq_send_recv.lua ├── test_thread_space.lua ├── test_shutdown.lua ├── test_thread_kill.lua ├── test_exec.lua ├── test_message_bus_local.lua ├── test_unix_sockets.lua ├── test_send_recv.lua ├── test_socket_byteorder.lua ├── test_sockopt.lua ├── test_multi_protocol.lua ├── test_socketpad.lua ├── test_socket_multi_recv.lua ├── test_ssl_send_recv.lua ├── test_message_bus_sockets.lua ├── Makefile.am ├── test_http_get.lua ├── test_smtp.lua ├── test_smtp_bigmessage.lua ├── test_smtp_tls.lua └── test_smtp_starttls.lua ├── .gitignore ├── COPYING ├── LICENSE ├── doc ├── bus.transaction.luadoc ├── dns.luadoc ├── http.client.luadoc ├── http.server.luadoc ├── timerfd.luadoc ├── error.luadoc ├── smtp.server.luadoc ├── ratchet.luadoc ├── ssl.session.luadoc ├── zmqsocket.luadoc ├── bus.luadoc ├── socketpad.luadoc ├── ssl.luadoc ├── exec.luadoc ├── thread.luadoc ├── html │ ├── modules │ │ ├── ratchet.bus.transaction.html │ │ ├── ratchet.dns.html │ │ ├── ratchet.http.client.html │ │ ├── ratchet.error.html │ │ ├── ratchet.http.server.html │ │ └── ratchet.smtp.server.html │ └── luadoc.css └── smtp.client.luadoc ├── m4 └── ax_check_openssl.m4 └── README /NEWS: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Ian Good 2 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2011-02-17 Ian Good 2 | 3 | Tagging version 0.4.2 4 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | SUBDIRS = src/c src/lua test 3 | EXTRA_DIST = doc 4 | -------------------------------------------------------------------------------- /src/c/dns/libdns/Makefile.am: -------------------------------------------------------------------------------- 1 | if HAVE_DNS 2 | noinst_LTLIBRARIES = libdns.la 3 | libdns_la_CFLAGS = -std=gnu99 -Wall -Wno-unused-function -Wno-unused-parameter -fstack-protector 4 | libdns_la_SOURCES = dns.h dns.c 5 | endif 6 | -------------------------------------------------------------------------------- /test/test_5sec_timer.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1() 4 | ratchet.timer(5.0) 5 | end 6 | 7 | local r = ratchet.new(function () 8 | ratchet.attach(ctx1) 9 | end) 10 | 11 | r:loop() 12 | 13 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 14 | -------------------------------------------------------------------------------- /src/c/dns/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | noinst_LTLIBRARIES = libdns.la 3 | 4 | libdns_la_LIBADD = libdns/libdns.la 5 | libdns_la_CFLAGS = -I.. -std=gnu99 -Wall -Wno-unused-function -Wno-unused-label 6 | libdns_la_LDFLAGS = 7 | 8 | libdns_la_SOURCES = dns.c \ 9 | resolv_conf.c \ 10 | hosts.c 11 | 12 | SUBDIRS = libdns 13 | 14 | -------------------------------------------------------------------------------- /test/test_timerfd.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1 () 4 | local tfd = ratchet.timerfd.new() 5 | tfd:settime(1.0, 1.0) 6 | 7 | while true do 8 | local fired = tfd:read() 9 | print("fire!") 10 | end 11 | end 12 | 13 | local r = ratchet.new(function () 14 | ratchet.thread.attach(ctx1) 15 | end) 16 | r:loop() 17 | 18 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 19 | -------------------------------------------------------------------------------- /test/test_callable_object.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | local function call_it(self) 4 | assert(self.data == "beep beep") 5 | run = true 6 | end 7 | 8 | local obj = {data = "beep beep"} 9 | setmetatable(obj, {__call = call_it}) 10 | 11 | local r = ratchet.new(function () 12 | ratchet.thread.attach(obj) 13 | end) 14 | r:loop() 15 | 16 | assert(run, "object did not get called") 17 | 18 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | missing 2 | ltmain.sh 3 | install-sh 4 | depcomp 5 | libtool 6 | stamp-h1 7 | configure 8 | config.sub 9 | config.h.in~ 10 | config.h 11 | config.log 12 | config.status 13 | config.guess 14 | compile 15 | aclocal.m4 16 | autom4te.cache/ 17 | m4/libtool.m4 18 | m4/ltoptions.m4 19 | m4/ltsugar.m4 20 | m4/ltversion.m4 21 | m4/lt~obsolete.m4 22 | INSTALL 23 | Makefile 24 | test/cert.pem 25 | test/ratchet 26 | .deps/ 27 | .libs/ 28 | *.in 29 | *.la 30 | *.lo 31 | *.o 32 | -------------------------------------------------------------------------------- /test/test_pause_unpause.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | local function ctx2() 4 | ratchet.thread.unpause(t1, "beep beep") 5 | local ret = ratchet.thread.pause() 6 | assert(ret == "boop boop") 7 | end 8 | 9 | local function ctx1() 10 | local ret = ratchet.thread.pause() 11 | assert(ret == "beep beep") 12 | ratchet.thread.unpause(t2, "boop boop") 13 | end 14 | 15 | local r = ratchet.new(function () 16 | t1 = ratchet.thread.attach(ctx1) 17 | t2 = ratchet.thread.attach(ctx2) 18 | end) 19 | 20 | r:loop() 21 | 22 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 23 | -------------------------------------------------------------------------------- /test/test_event_timeout.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1(host, port) 4 | local rec = ratchet.socket.prepare_tcp(host, port) 5 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 6 | socket:setsockopt("SO_REUSEADDR", true) 7 | socket:bind(rec.addr) 8 | socket:listen() 9 | 10 | socket:set_timeout(0.0) 11 | local worked, err = pcall(socket.accept, socket) 12 | assert(not worked and ratchet.error.is(err, "ETIMEDOUT"), "accept failed to timeout") 13 | end 14 | 15 | kernel = ratchet.new(function () 16 | ratchet.thread.attach(ctx1, "localhost", 10025) 17 | end) 18 | kernel:loop() 19 | 20 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 21 | -------------------------------------------------------------------------------- /src/lua/bus/init.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | 4 | ratchet.bus = {} 5 | 6 | local samestate = require "ratchet.bus.samestate" 7 | local server = require "ratchet.bus.server" 8 | local client = require "ratchet.bus.client" 9 | 10 | -- {{{ ratchet.bus.new_local() 11 | function ratchet.bus.new_local(...) 12 | return samestate.new(...) 13 | end 14 | -- }}} 15 | 16 | -- {{{ ratchet.bus.new_server() 17 | function ratchet.bus.new_server(...) 18 | return server.new(...) 19 | end 20 | -- }}} 21 | 22 | -- {{{ ratchet.bus.new_client() 23 | function ratchet.bus.new_client(...) 24 | return client.new(...) 25 | end 26 | -- }}} 27 | 28 | return ratchet.bus 29 | 30 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 31 | -------------------------------------------------------------------------------- /test/test_wait_all.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | count = 0 4 | 5 | local function ctx1(n) 6 | count = count + n 7 | end 8 | 9 | local function ctx2() 10 | local t1 = ratchet.thread.attach(ctx1, 1) 11 | local t2 = ratchet.thread.attach(ctx1, 2) 12 | local t3 = ratchet.thread.attach(ctx1, 3) 13 | local t4 = ratchet.thread.attach(ctx1, 4) 14 | local t5 = ratchet.thread.attach(ctx1, 5) 15 | 16 | ratchet.thread.wait_all({t1, t2, t3, t4, t5}) 17 | assert(count == 15) 18 | count = count + 6 19 | end 20 | 21 | local r = ratchet.new(function () 22 | ratchet.thread.attach(ctx2) 23 | end) 24 | r:loop() 25 | 26 | assert(count == 21, count.." != 21") 27 | 28 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 29 | -------------------------------------------------------------------------------- /test/test_pcall_kernel_loop.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function error_thread() 4 | error("uh oh") 5 | end 6 | 7 | function waiting_thread(e) 8 | ratchet.thread.wait_all({e}) 9 | set_after_error = true 10 | end 11 | 12 | kernel = ratchet.new(function () 13 | local e = ratchet.thread.attach(error_thread) 14 | ratchet.thread.attach(waiting_thread, e) 15 | end) 16 | 17 | local worked, err = pcall(kernel.loop, kernel) 18 | 19 | assert(not worked) 20 | assert(err:match('uh oh'), "error did not match as expected.") 21 | assert(not set_after_error) 22 | assert(1 == kernel:get_num_threads()) 23 | 24 | kernel:loop() 25 | 26 | assert(set_after_error == true) 27 | assert(0 == kernel:get_num_threads()) 28 | 29 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 30 | -------------------------------------------------------------------------------- /test/test_socketpair.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1() 4 | local socket_a, socket_b = ratchet.socket.new_pair() 5 | 6 | ratchet.thread.attach(ctx2, socket_b) 7 | 8 | socket_a:send("hello") 9 | local data = socket_a:recv(5) 10 | assert(data == "world") 11 | 12 | local data = socket_a:recv() 13 | assert(data == "foo") 14 | socket_a:send("bar") 15 | end 16 | 17 | function ctx2(socket_b) 18 | local data = socket_b:recv(5) 19 | assert(data == "hello") 20 | socket_b:send("world") 21 | 22 | socket_b:send("foo") 23 | local data = socket_b:recv() 24 | assert(data == "bar") 25 | end 26 | 27 | kernel = ratchet.new(function () 28 | ratchet.thread.attach(ctx1) 29 | end) 30 | kernel:loop() 31 | 32 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 33 | -------------------------------------------------------------------------------- /src/lua/smtp/smtp_reply.lua: -------------------------------------------------------------------------------- 1 | 2 | local smtp_reply = {} 3 | smtp_reply.__index = smtp_reply 4 | 5 | -- {{{ smtp_reply.new() 6 | function smtp_reply.new(command) 7 | local self = {} 8 | setmetatable(self, smtp_reply) 9 | 10 | self.command = command or "" 11 | 12 | return self 13 | end 14 | -- }}} 15 | 16 | -- {{{ smtp_reply:recv() 17 | function smtp_reply:recv(io) 18 | self.code, self.message = io:recv_reply() 19 | end 20 | -- }}} 21 | 22 | -- {{{ smtp_reply:error() 23 | function smtp_reply:error(description) 24 | return { 25 | command = self.command, 26 | code = tostring(self.code), 27 | message = self.message, 28 | description = description, 29 | } 30 | end 31 | -- }}} 32 | 33 | return smtp_reply 34 | 35 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 36 | -------------------------------------------------------------------------------- /test/test_listen_connect.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1(host, port) 4 | local rec = ratchet.socket.prepare_tcp(host, port, "AF_INET") 5 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 6 | socket:setsockopt("SO_REUSEADDR", true) 7 | socket:bind(rec.addr) 8 | socket:listen() 9 | 10 | connect_client("localhost", port) 11 | 12 | local client, from = socket:accept() 13 | 14 | local ptr = ratchet.dns.query(tostring(from), "ptr") 15 | assert(ptr and ptr[1] == "localhost.") 16 | end 17 | 18 | function connect_client(host, port) 19 | local rec = ratchet.socket.prepare_tcp(host, port) 20 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 21 | socket:connect(rec.addr) 22 | end 23 | 24 | kernel = ratchet.new(function () 25 | ratchet.thread.attach(ctx1, "*", 10025) 26 | end) 27 | kernel:loop() 28 | 29 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 30 | -------------------------------------------------------------------------------- /test/test_thread_alarm.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | 4 | function ctx1() 5 | ratchet.thread.alarm(0.0, function () 6 | ran_alarm_function = true 7 | end) 8 | ratchet.thread.timer(1.0) 9 | end 10 | 11 | function ctx2() 12 | ratchet.thread.alarm(0.0, function () 13 | local err = ratchet.error.new("Test cleanup error", "CLEANUP") 14 | error(err) 15 | end) 16 | ratchet.thread.timer(1.0) 17 | end 18 | 19 | kernel = ratchet.new(function () 20 | ratchet.thread.attach(ctx1) 21 | ratchet.thread.attach(ctx2) 22 | 23 | end, function (err, thread) 24 | if ratchet.error.is(err, "ALARM") then 25 | got_alarm_error = true 26 | elseif ratchet.error.is(err, "CLEANUP") then 27 | got_cleanup_error = true 28 | end 29 | end) 30 | kernel:loop() 31 | 32 | assert(ran_alarm_function) 33 | assert(got_alarm_error) 34 | assert(got_cleanup_error) 35 | 36 | -- vim:et:fdm=marker:sts=4:sw=4:ts=4 37 | -------------------------------------------------------------------------------- /test/test_zmq_send_recv.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1(where) 4 | local rec = ratchet.zmqsocket.prepare_uri(where) 5 | local socket = ratchet.zmqsocket.new(rec.type) 6 | socket:bind(rec.endpoint) 7 | 8 | ratchet.thread.attach(ctx2, "rep:tcp://127.0.0.1:10025") 9 | 10 | -- Portion being tested. 11 | -- 12 | socket:send("hello") 13 | local data = socket:recv_all() 14 | assert(data == "world") 15 | end 16 | 17 | function ctx2(where) 18 | local rec = ratchet.zmqsocket.prepare_uri(where) 19 | local socket = ratchet.zmqsocket.new(rec.type) 20 | socket:connect(rec.endpoint) 21 | 22 | -- Portion being tested. 23 | -- 24 | local data = socket:recv() 25 | assert(data == "hello") 26 | socket:send("wo", true) 27 | socket:send("rld") 28 | end 29 | 30 | kernel = ratchet.new(function () 31 | ratchet.thread.attach(ctx1, "req:tcp://127.0.0.1:10025") 32 | end) 33 | kernel:loop() 34 | 35 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 36 | -------------------------------------------------------------------------------- /test/test_thread_space.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | local function check_ctx1_space(r) 4 | assert(ratchet.thread.space().stuff == "data") 5 | assert(not ratchet.thread.space().other_stuff) 6 | end 7 | 8 | function ctx1(r) 9 | ratchet.thread.space().stuff = "data" 10 | check_ctx1_space(r) 11 | end 12 | 13 | local function check_ctx2_space(r) 14 | assert(ratchet.thread.space().other_stuff == "other data") 15 | assert(not ratchet.thread.space().stuff) 16 | end 17 | 18 | function ctx2(r) 19 | ratchet.thread.space().other_stuff = "other data" 20 | check_ctx2_space(r) 21 | end 22 | 23 | local function check_ctx3_space(r) 24 | assert(ratchet.thread.space().premade_stuff == "important") 25 | end 26 | 27 | function ctx3(r) 28 | ratchet.thread.space({premade_stuff = "important"}) 29 | check_ctx3_space(r) 30 | end 31 | 32 | local r = ratchet.new(function () 33 | ratchet.thread.attach(ctx1, r) 34 | ratchet.thread.attach(ctx2, r) 35 | ratchet.thread.attach(ctx3, r) 36 | end) 37 | r:loop() 38 | 39 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 40 | -------------------------------------------------------------------------------- /test/test_shutdown.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1(host, port) 4 | local rec = ratchet.socket.prepare_tcp(host, port) 5 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 6 | socket:setsockopt("SO_REUSEADDR", true) 7 | socket:bind(rec.addr) 8 | socket:listen() 9 | 10 | ratchet.thread.attach(ctx2, host, port) 11 | 12 | local client = socket:accept() 13 | 14 | -- Portion being tested. 15 | -- 16 | client:shutdown("write") 17 | local data = client:recv() 18 | assert(data == "ooga") 19 | end 20 | 21 | function ctx2(host, port) 22 | local rec = ratchet.socket.prepare_tcp(host, port) 23 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 24 | socket:connect(rec.addr) 25 | 26 | -- Portion being tested. 27 | -- 28 | local data = socket:recv() 29 | assert(data == "") 30 | socket:send("ooga") 31 | end 32 | 33 | kernel = ratchet.new(function () 34 | ratchet.thread.attach(ctx1, "localhost", 10025) 35 | end) 36 | kernel:loop() 37 | 38 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 39 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Ian C. Good 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/lua/bus/samestate_transaction.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | 4 | local samestate_transaction = {} 5 | samestate_transaction.__index = samestate_transaction 6 | 7 | -- {{{ samestate_transaction.new() 8 | function samestate_transaction.new(request) 9 | local self = {} 10 | setmetatable(self, samestate_transaction) 11 | 12 | self.request = request 13 | 14 | return self 15 | end 16 | -- }}} 17 | 18 | -- {{{ samestate_transaction:send_response() 19 | function samestate_transaction:send_response(res) 20 | self.response = res 21 | 22 | if self.waiting_thread then 23 | ratchet.thread.unpause(self.waiting_thread, res) 24 | self.waiting_thread = nil 25 | end 26 | end 27 | -- }}} 28 | 29 | -- {{{ samestate_transaction:recv_response() 30 | function samestate_transaction:recv_response() 31 | if self.response then 32 | return self.response 33 | else 34 | self.waiting_thread = ratchet.thread.self() 35 | return ratchet.thread.pause() 36 | end 37 | end 38 | -- }}} 39 | 40 | return samestate_transaction 41 | 42 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 43 | -------------------------------------------------------------------------------- /test/test_thread_kill.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1(host, port) 4 | local rec = ratchet.socket.prepare_tcp(host, port) 5 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 6 | socket:setsockopt("SO_REUSEADDR", true) 7 | socket:bind(rec.addr) 8 | socket:listen() 9 | 10 | local paused = { 11 | ratchet.thread.attach(ctx3), 12 | ratchet.thread.attach(ctx3), 13 | ratchet.thread.attach(ctx3), 14 | ratchet.thread.attach(ctx3), 15 | ratchet.thread.attach(ctx3), 16 | } 17 | local self = ratchet.thread.self() 18 | ratchet.thread.attach(ctx2, self, paused) 19 | 20 | local client = socket:accept() 21 | end 22 | 23 | function ctx2(thread, paused) 24 | ratchet.thread.kill(thread) 25 | ratchet.thread.kill_all(paused) 26 | end 27 | 28 | function ctx3() 29 | ratchet.thread.pause() 30 | end 31 | 32 | kernel = ratchet.new(function () 33 | ratchet.thread.attach(ctx1, "localhost", 10025) 34 | end) 35 | kernel:loop() 36 | assert(0 == kernel:get_num_threads()) 37 | 38 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Ian C. Good 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /src/lua/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | http_sources = http/client.lua \ 3 | http/server.lua \ 4 | http/common.lua 5 | 6 | smtp_sources = smtp/client.lua \ 7 | smtp/server.lua \ 8 | smtp/data_reader.lua \ 9 | smtp/data_sender.lua \ 10 | smtp/smtp_extensions.lua \ 11 | smtp/smtp_io.lua \ 12 | smtp/smtp_reply.lua 13 | 14 | bus_sources = bus/init.lua \ 15 | bus/samestate.lua \ 16 | bus/samestate_transaction.lua \ 17 | bus/client.lua \ 18 | bus/client_transaction.lua \ 19 | bus/server.lua \ 20 | bus/server_transaction.lua 21 | 22 | socketpad_sources = socketpad/init.lua 23 | 24 | if ENABLE_HTTP 25 | httpdir = @LUA_LPATH@/ratchet/http 26 | dist_http_DATA = $(http_sources) 27 | endif 28 | 29 | if ENABLE_SMTP 30 | smtpdir = @LUA_LPATH@/ratchet/smtp 31 | dist_smtp_DATA = $(smtp_sources) 32 | endif 33 | 34 | if ENABLE_BUS 35 | busdir = @LUA_LPATH@/ratchet/bus 36 | dist_bus_DATA = $(bus_sources) 37 | endif 38 | 39 | if ENABLE_SOCKETPAD 40 | socketpaddir = @LUA_LPATH@/ratchet/socketpad 41 | dist_socketpad_DATA = $(socketpad_sources) 42 | endif 43 | 44 | -------------------------------------------------------------------------------- /src/c/misc.h: -------------------------------------------------------------------------------- 1 | #ifndef __RATCHET_MISC_H 2 | #define __RATCHET_MISC_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define stackdump(L) fstackdump_ln (L, stdout, __FILE__, __LINE__) 11 | #define fstackdump(L, out) fstackdump_ln (L, out, __FILE__, __LINE__) 12 | 13 | int strmatch (lua_State *L, int index, const char *match); 14 | int strequal (lua_State *L, int index, const char *s2); 15 | double fromtimeval (struct timeval *tv); 16 | int gettimeval (double secs, struct timeval *tv); 17 | int gettimeval_arg (lua_State *L, int index, struct timeval *tv); 18 | int gettimeval_opt (lua_State *L, int index, struct timeval *tv); 19 | double fromtimespec (struct timespec *tv); 20 | int gettimespec (double secs, struct timespec *tv); 21 | int gettimespec_arg (lua_State *L, int index, struct timespec *tv); 22 | int gettimespec_opt (lua_State *L, int index, struct timespec *tv); 23 | int get_signal (lua_State *L, int index, int def); 24 | int set_nonblocking (int fd); 25 | int set_closeonexec (int fd); 26 | void fstackdump_ln (lua_State *L, FILE *out, const char *file, int line); 27 | 28 | #endif 29 | // vim:fdm=marker:ai:ts=4:sw=4:noet: 30 | -------------------------------------------------------------------------------- /doc/bus.transaction.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- After a request has been sent or received by the client- or server-side, 3 | -- respectively, of a message bus, the first returned value is a transaction 4 | -- object, defined by this module. A transaction object cannot be created 5 | -- directly, it is presented as a module for documentation purposes only. 6 | module("ratchet.bus.transaction") 7 | 8 | --- Sends a response back to the client-side of a message bus, after a 9 | -- request was received fro that client. Information about the client's 10 | -- connection is available in the "from" index of the transaction object, if 11 | -- the bus is socket-based. This function is available from transactions 12 | -- returned by recv_request(). 13 | -- @param self a transaction object returned by recv_request(). 14 | -- @param response a response object to send. 15 | function send_response(self, response) 16 | 17 | --- Receives a response from the server-side of a message bus. This function is 18 | -- available from transactions returned by recv_response(). 19 | -- @param self a transaction object returned by send_request(). 20 | -- @return the response from the server-side message bus. 21 | function recv_response(self) 22 | 23 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 24 | -------------------------------------------------------------------------------- /test/test_exec.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | tests = 0 4 | 5 | function hello_world() 6 | local p = ratchet.exec.new({"echo", "hello", "world"}) 7 | p:start() 8 | p:stdin():close() 9 | local line = p:stdout():read() 10 | line = line:gsub("%\r?%\n$", "") 11 | assert("hello world" == line) 12 | assert(0 == p:wait()) 13 | 14 | tests = tests + 2 15 | end 16 | 17 | function cat_test() 18 | local p = ratchet.exec.new({"cat"}) 19 | p:start() 20 | p:stdin():write("test\n") 21 | p:stdin():close() 22 | local line = p:stdout():read() 23 | line = line:gsub("%\r?%\n$", "") 24 | assert("test" == line) 25 | assert(0 == p:wait()) 26 | 27 | tests = tests + 1 28 | end 29 | 30 | function communicate_test() 31 | local p = ratchet.exec.new({"cat"}) 32 | local out, err, status = p:communicate("testing") 33 | assert("testing" == out) 34 | assert("" == err) 35 | assert(0 == status) 36 | 37 | tests = tests + 4 38 | end 39 | 40 | kernel = ratchet.new(function () 41 | ratchet.thread.attach(hello_world) 42 | ratchet.thread.attach(hello_world) 43 | ratchet.thread.attach(cat_test) 44 | ratchet.thread.attach(communicate_test) 45 | end) 46 | kernel:loop() 47 | 48 | assert(tests == 9) 49 | 50 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 51 | -------------------------------------------------------------------------------- /test/test_message_bus_local.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | require "ratchet.bus" 3 | 4 | function server_bus(bus) 5 | local test_response_1 = {data = "important answer!"} 6 | local test_response_2 = "yes" 7 | 8 | local transaction_1, request_1 = bus:recv_request() 9 | ratchet.thread.attach(client_bus_2, bus) 10 | local transaction_2, request_2 = bus:recv_request() 11 | 12 | assert("important question!" == request_1.data) 13 | assert(1337 == request_2) 14 | 15 | transaction_1:send_response(test_response_1) 16 | transaction_2:send_response(test_response_2) 17 | end 18 | 19 | function client_bus_1(bus) 20 | local test_request = {data = "important question!"} 21 | 22 | local transaction = bus:send_request(test_request) 23 | local response = transaction:recv_response() 24 | assert("important answer!" == response.data) 25 | end 26 | 27 | function client_bus_2(bus) 28 | local test_request = 1337 29 | 30 | local transaction = bus:send_request(test_request) 31 | local response = transaction:recv_response() 32 | assert("yes" == response) 33 | end 34 | 35 | kernel = ratchet.new(function () 36 | local bus = ratchet.bus.new_local(kernel) 37 | 38 | ratchet.thread.attach(server_bus, bus) 39 | ratchet.thread.attach(client_bus_1, bus) 40 | end) 41 | kernel:loop() 42 | 43 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 44 | -------------------------------------------------------------------------------- /test/test_unix_sockets.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1(file) 4 | local rec = ratchet.socket.prepare_unix(file) 5 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 6 | socket:setsockopt("SO_REUSEADDR", true) 7 | socket:bind(rec.addr) 8 | socket:listen() 9 | 10 | ratchet.thread.attach(ctx3, file) 11 | 12 | local client = socket:accept() 13 | ratchet.thread.attach(ctx2, client) 14 | end 15 | 16 | function ctx2(socket) 17 | -- Portion being tested. 18 | -- 19 | socket:send("hello") 20 | local data = socket:recv(5) 21 | assert(data == "world") 22 | 23 | local data = socket:recv() 24 | assert(data == "foo") 25 | socket:send("bar") 26 | end 27 | 28 | function ctx3(file) 29 | local rec = ratchet.socket.prepare_unix(file) 30 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 31 | socket:connect(rec.addr) 32 | 33 | -- Portion being tested. 34 | -- 35 | local data = socket:recv(5) 36 | assert(data == "hello") 37 | socket:send("world") 38 | 39 | socket:send("foo") 40 | local data = socket:recv() 41 | assert(data == "bar") 42 | end 43 | 44 | kernel = ratchet.new(function () 45 | ratchet.thread.attach(ctx1, "/tmp/ratchet-tests.sock") 46 | end) 47 | kernel:loop() 48 | 49 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 50 | -------------------------------------------------------------------------------- /test/test_send_recv.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1(host, port) 4 | local rec = ratchet.socket.prepare_tcp(host, port) 5 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 6 | socket:setsockopt("SO_REUSEADDR", true) 7 | socket:bind(rec.addr) 8 | socket:listen() 9 | 10 | ratchet.thread.attach(ctx3, host, port) 11 | 12 | local client = socket:accept() 13 | ratchet.thread.attach(ctx2, client) 14 | end 15 | 16 | function ctx2(socket) 17 | -- Portion being tested. 18 | -- 19 | socket:send("hello") 20 | local data = socket:recv(5) 21 | assert(data == "world") 22 | 23 | local data = socket:recv() 24 | assert(data == "foo") 25 | socket:send("bar") 26 | end 27 | 28 | function ctx3(host, port) 29 | local rec = ratchet.socket.prepare_tcp(host, port) 30 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 31 | socket:connect(rec.addr) 32 | 33 | -- Portion being tested. 34 | -- 35 | local data = socket:recv(5) 36 | assert(data == "hello") 37 | socket:send("world") 38 | 39 | socket:send("foo") 40 | local data = socket:recv() 41 | assert(data == "bar") 42 | end 43 | 44 | kernel = ratchet.new(function () 45 | ratchet.thread.attach(ctx1, "localhost", 10025) 46 | end) 47 | kernel:loop() 48 | 49 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 50 | -------------------------------------------------------------------------------- /src/c/Makefile.am: -------------------------------------------------------------------------------- 1 | allsources = ratchet.h ratchet.c \ 2 | misc.h misc.c \ 3 | error.c exec.c 4 | 5 | if HAVE_SOCKET 6 | allsources += sockopt.c socket.c 7 | endif 8 | 9 | if HAVE_ZMQ 10 | allsources += zmq.c 11 | endif 12 | 13 | if HAVE_TIMERFD 14 | allsources += timerfd.c 15 | endif 16 | 17 | if HAVE_OPENSSL 18 | allsources += ssl.c 19 | endif 20 | 21 | if ENABLE_DEVEL 22 | include_HEADERS = ratchet.h 23 | 24 | lib_LTLIBRARIES = libratchet.la 25 | else 26 | noinst_LTLIBRARIES = libratchet.la 27 | endif 28 | 29 | if HAVE_DNS 30 | libratchet_la_LIBADD = dns/libdns.la 31 | endif 32 | libratchet_la_CFLAGS = -std=gnu99 -Wall -Wno-unused-function -Wno-unused-label -Idns 33 | libratchet_la_LDFLAGS = 34 | if HAVE_OPENSSL 35 | libratchet_la_CFLAGS += $(OPENSSL_INCLUDES) 36 | libratchet_la_LDFLAGS += $(OPENSSL_LDFLAGS) $(OPENSSL_LIBS) 37 | endif 38 | libratchet_la_SOURCES = $(allsources) 39 | 40 | if ENABLE_MODULE 41 | if ENABLE_DEVEL 42 | install-exec-hook: 43 | cd @MODULE_PATH@ && \ 44 | $(LN_S) -f $(libdir)/libratchet.so ratchet.so 45 | uninstall-local: 46 | cd @MODULE_PATH@ && \ 47 | rm -f ratchet.so 48 | else 49 | lualibdir = @MODULE_PATH@ 50 | lualib_LTLIBRARIES = ratchet.la 51 | ratchet_la_LDFLAGS = -module -avoid-version -shared 52 | ratchet_la_LIBADD = libratchet.la 53 | ratchet_la_SOURCES = 54 | endif 55 | endif 56 | 57 | SUBDIRS = 58 | 59 | if HAVE_DNS 60 | SUBDIRS += dns 61 | endif 62 | 63 | -------------------------------------------------------------------------------- /test/test_socket_byteorder.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1(host, port) 4 | local rec = ratchet.socket.prepare_tcp(host, port) 5 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 6 | socket:setsockopt("SO_REUSEADDR", true) 7 | socket:bind(rec.addr) 8 | socket:listen() 9 | 10 | ratchet.thread.attach(ctx3, host, port) 11 | 12 | local client = socket:accept() 13 | ratchet.thread.attach(ctx2, client) 14 | end 15 | 16 | function ctx2(socket) 17 | -- Portion being tested. 18 | -- 19 | local bytestr = socket:recv() 20 | assert(4 == #bytestr) 21 | assert(1333337 == ratchet.socket.ntoh(bytestr)) 22 | 23 | bytestr = ratchet.socket.hton(73) 24 | socket:send(bytestr) 25 | end 26 | 27 | function ctx3(host, port) 28 | local rec = ratchet.socket.prepare_tcp(host, port) 29 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 30 | socket:connect(rec.addr) 31 | 32 | -- Portion being tested. 33 | -- 34 | local bytestr = ratchet.socket.hton(1333337) 35 | socket:send(bytestr) 36 | 37 | bytestr = socket:recv() 38 | assert(4 == #bytestr) 39 | assert(73 == ratchet.socket.ntoh(bytestr)) 40 | end 41 | 42 | kernel = ratchet.new(function () 43 | ratchet.thread.attach(ctx1, "localhost", 10025) 44 | end) 45 | kernel:loop() 46 | 47 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 48 | -------------------------------------------------------------------------------- /src/lua/bus/server_transaction.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | 4 | local server_transaction = {} 5 | server_transaction.__index = server_transaction 6 | 7 | -- {{{ server_transaction.new() 8 | function server_transaction.new(request, response_to_bus, socket_buffer, from) 9 | local self = {} 10 | setmetatable(self, server_transaction) 11 | 12 | self.request = request 13 | self.socket_buffer = socket_buffer 14 | self.response_to_bus = response_to_bus 15 | self.from = from 16 | 17 | return self 18 | end 19 | -- }}} 20 | 21 | -- {{{ send_part() 22 | local function send_part(pad, part) 23 | local part_size = ratchet.socket.hton(#part) 24 | 25 | pad:send(part_size, true) 26 | pad:send(part) 27 | end 28 | -- }}} 29 | 30 | -- {{{ server_transaction:send_response() 31 | function server_transaction:send_response(response) 32 | local part_1, attachments = self.response_to_bus(response) 33 | 34 | local num_parts = 1 + (attachments and #attachments or 0) 35 | self.socket_buffer:send(ratchet.socket.hton16(num_parts), true) 36 | 37 | send_part(self.socket_buffer, part_1) 38 | if attachments then 39 | for i = 1, #attachments do 40 | send_part(self.socket_buffer, attachments[i]) 41 | end 42 | end 43 | 44 | self.socket_buffer:close() 45 | end 46 | -- }}} 47 | 48 | return server_transaction 49 | 50 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 51 | -------------------------------------------------------------------------------- /test/test_sockopt.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1(host, port) 4 | local rec = ratchet.socket.prepare_tcp(host, port) 5 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 6 | socket:setsockopt("SO_REUSEADDR", true) 7 | socket:bind(rec.addr) 8 | socket:listen() 9 | 10 | assert(socket:getsockopt("SO_ACCEPTCONN") == true, "SO_ACCEPTCONN != true") 11 | 12 | assert(socket:getsockopt("SO_REUSEADDR") == true, "SO_REUSEADDR != true") 13 | socket:setsockopt("SO_REUSEADDR", false) 14 | assert(socket:getsockopt("SO_REUSEADDR") == false, "SO_REUSEADDR != false") 15 | socket:setsockopt("SO_REUSEADDR", true) 16 | end 17 | 18 | function ctx2(host, port) 19 | local rec = ratchet.socket.prepare_tcp(host, port) 20 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 21 | 22 | assert(socket:getsockopt("SO_ACCEPTCONN") == false, "SO_ACCEPTCONN != false") 23 | 24 | -- Linux kernel doubles whatever value you set to SO_SND/RCVBUF. 25 | socket:setsockopt("SO_SNDBUF", 1024) 26 | assert(socket:getsockopt("SO_SNDBUF") == 2048, "SO_SNDBUF (" .. socket:getsockopt("SO_SNDBUF") .. ") != 2048") 27 | end 28 | 29 | kernel = ratchet.new(function () 30 | ratchet.thread.attach(ctx1, "localhost", 10025) 31 | ratchet.thread.attach(ctx2, "localhost", 10025) 32 | end) 33 | kernel:loop() 34 | 35 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 36 | -------------------------------------------------------------------------------- /src/lua/bus/client.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | require "ratchet.socketpad" 4 | local client_transaction = require "ratchet.bus.client_transaction" 5 | 6 | local client = {} 7 | client.__index = client 8 | 9 | -- {{{ client.new() 10 | function client.new(socket, request_to_bus, response_from_bus, socket_from) 11 | local self = {} 12 | setmetatable(self, client) 13 | 14 | self.socket_buffer = ratchet.socketpad.new(socket, socket_from) 15 | self.request_to_bus = request_to_bus or tostring 16 | self.response_from_bus = response_from_bus or tostring 17 | 18 | return self 19 | end 20 | -- }}} 21 | 22 | -- {{{ send_part() 23 | local function send_part(pad, part) 24 | local part_size = ratchet.socket.hton(#part) 25 | 26 | pad:send(part_size, true) 27 | pad:send(part) 28 | end 29 | -- }}} 30 | 31 | -- {{{ client:send_request() 32 | function client:send_request(request) 33 | local part_1, attachments = self.request_to_bus(request) 34 | 35 | local num_parts = 1 + (attachments and #attachments or 0) 36 | self.socket_buffer:send(ratchet.socket.hton16(num_parts), true) 37 | 38 | send_part(self.socket_buffer, part_1) 39 | if attachments then 40 | for i = 1, #attachments do 41 | send_part(self.socket_buffer, attachments[i]) 42 | end 43 | end 44 | 45 | return client_transaction.new(request, self.response_from_bus, self.socket_buffer) 46 | end 47 | -- }}} 48 | 49 | return client 50 | 51 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 52 | -------------------------------------------------------------------------------- /src/lua/http/common.lua: -------------------------------------------------------------------------------- 1 | 2 | require "package" 3 | 4 | local common = {} 5 | 6 | -- {{{ headers_metatable 7 | local headers_metatable = { 8 | 9 | -- For any non-existent header, return an empty table. 10 | __index = function (headers, key) 11 | local check_lower = rawget(headers, key:lower()) 12 | if check_lower then 13 | return check_lower 14 | else 15 | return {} 16 | end 17 | end, 18 | 19 | } 20 | -- }}} 21 | 22 | -- {{{ common.build_header_string() 23 | function common.build_header_string(headers) 24 | local ret = "" 25 | for name, value in pairs(headers) do 26 | for i, each in ipairs(value) do 27 | ret = ret .. name .. ": " .. tostring(each) .. "\r\n" 28 | end 29 | end 30 | return ret 31 | end 32 | -- }}} 33 | 34 | -- {{{ common.parse_header_string() 35 | function common.parse_header_string(data, start) 36 | local headers = {} 37 | repeat 38 | local name, value 39 | name, value, start = data:match("^(.-):%s+(.-)\r\n()", start) 40 | if name then 41 | local key = name:lower() 42 | if not headers[key] then 43 | headers[key] = {value} 44 | else 45 | table.insert(headers[key], value) 46 | end 47 | end 48 | until not name 49 | 50 | setmetatable(headers, headers_metatable) 51 | return headers, start 52 | end 53 | -- }}} 54 | 55 | return common 56 | 57 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 58 | -------------------------------------------------------------------------------- /src/lua/bus/samestate.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | local samestate_transaction = require "ratchet.bus.samestate_transaction" 4 | 5 | local samestate = {} 6 | samestate.__index = samestate 7 | 8 | -- {{{ samestate.new() 9 | function samestate.new() 10 | local self = {} 11 | setmetatable(self, samestate) 12 | 13 | self.queue = {} 14 | 15 | return self, self 16 | end 17 | -- }}} 18 | 19 | -- {{{ wait_for_transaction() 20 | local function wait_for_transaction(self) 21 | if not self.queue[1] then 22 | self.waiting_thread = ratchet.thread.self() 23 | return ratchet.thread.pause() 24 | else 25 | return table.remove(self.queue, 1) 26 | end 27 | end 28 | -- }}} 29 | 30 | -- {{{ samestate:recv_request() 31 | function samestate:recv_request() 32 | local transaction = wait_for_transaction(self) 33 | return transaction, transaction and transaction.request 34 | end 35 | -- }}} 36 | 37 | -- {{{ send_or_queue_transaction() 38 | local function send_or_queue_transaction(self, data) 39 | if self.waiting_thread then 40 | ratchet.thread.unpause(self.waiting_thread, data) 41 | self.waiting_thread = nil 42 | else 43 | table.insert(self.queue, data) 44 | end 45 | end 46 | -- }}} 47 | 48 | -- {{{ samestate:send_request() 49 | function samestate:send_request(req) 50 | local transaction = samestate_transaction.new(req) 51 | 52 | send_or_queue_transaction(self, transaction) 53 | return transaction 54 | end 55 | -- }}} 56 | 57 | return samestate 58 | 59 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 60 | -------------------------------------------------------------------------------- /doc/dns.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- Provides a system for querying DNS for information such as A, AAAA, PTR, MX, 3 | -- and TXT records. While there is a class-based system internally, that API is 4 | -- currently hidden by namespace functions that call everything necessary. 5 | module "ratchet.dns" 6 | 7 | --- Queries for results for one type of query for the given data. Until the 8 | -- results come in, this function will pause the calling thread. The return 9 | -- value is a table whose contents are defined by the type of query. See manual 10 | -- for complete details. 11 | -- @param data The hostname, IP, or special-case to query against. 12 | -- @param type The type of query, e.g. "a" or "mx". 13 | -- @return Table with results, or nil followed by an error message. 14 | function query(data, type) 15 | 16 | --- Queries for results for many types of query for the given data. Until all 17 | -- results come in, this function will pause the calling thread. This call 18 | -- attempts to perform these lookups in parallel, with a file descriptor for 19 | -- each query type. The return value is a table keyed on either the query 20 | -- types or the query type suffixed with "_error". The values will either 21 | -- be a table of results or an error message, respectively. See manual for 22 | -- complete details. 23 | -- @param data The hostname, IP, or special-case to query against. 24 | -- @param types Table with types of queries, e.g. "a" or "mx". 25 | -- @return Table with results, or nil followed by an error message. 26 | function query_all(data, types) 27 | 28 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 29 | -------------------------------------------------------------------------------- /src/lua/bus/client_transaction.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | 4 | ratchet.bus.client_transaction = {} 5 | ratchet.bus.client_transaction.__index = ratchet.bus.client_transaction 6 | 7 | -- {{{ ratchet.bus.client_transaction.new() 8 | function ratchet.bus.client_transaction.new(request, response_from_bus, socket_buffer) 9 | local self = {} 10 | setmetatable(self, ratchet.bus.client_transaction) 11 | 12 | self.request = request 13 | self.socket_buffer = socket_buffer 14 | self.response_from_bus = response_from_bus 15 | 16 | return self 17 | end 18 | -- }}} 19 | 20 | -- {{{ recv_part() 21 | local function recv_part(pad, parts, i) 22 | local size, incomplete = ratchet.socket.ntoh(pad:recv(4)) 23 | if incomplete then 24 | pad:close() 25 | return 26 | end 27 | 28 | local data, incomplete = pad:recv(size) 29 | if incomplete then 30 | pad:close() 31 | return 32 | end 33 | 34 | parts[i] = data 35 | end 36 | -- }}} 37 | 38 | -- {{{ ratchet.bus.client_transaction:recv_response() 39 | function ratchet.bus.client_transaction:recv_response() 40 | local pad = self.socket_buffer 41 | local num_parts, incomplete = ratchet.socket.ntoh16(pad:recv(2)) 42 | if incomplete then 43 | pad:close() 44 | return 45 | end 46 | 47 | local parts = {} 48 | for i = 1, num_parts do 49 | recv_part(pad, parts, i) 50 | end 51 | pad:close() 52 | 53 | return self.response_from_bus(table.remove(parts, 1), parts, pad.from) 54 | end 55 | -- }}} 56 | 57 | return ratchet.bus.client_transaction 58 | 59 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 60 | -------------------------------------------------------------------------------- /doc/http.client.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- This library provides client-side access to an HTTP service, commonly used 3 | -- in many different cases across the Web. Currently the client only supports 4 | -- HTTP/1.0, with no current plans to support 1.1 or higher, as it is only 5 | -- provided for convenience and as an example of using the ratchet libraries. 6 | module "ratchet.http.client" 7 | 8 | --- Creates a new HTTP client object. 9 | -- @param socket Used as the underlying socket for the query, under the 10 | -- assumption that encryption has been established as needed 11 | -- and no other I/O has taken place. 12 | -- @return a new http.client object. 13 | function new(socket) 14 | 15 | --- Sends an HTTP query, and returns the results. 16 | -- @param self the http.client object. 17 | -- @param command The HTTP command to use, such as "PUT" or "GET". 18 | -- @param uri The URI to query. 19 | -- @param headers A table whose keys are header names and values are arrays 20 | -- of strings. Each string in the value array will result in a 21 | -- new header of the given name. 22 | -- @param data Additional data to send in the body of the query. It is usually 23 | -- important to specify a "Content-Length" header with the length 24 | -- of this string. 25 | -- @return In order: the return code of the query, the descriptive return 26 | -- message, additional headers returned (in the same format as the 27 | -- headers parameter), and a string of data returned by the query (if 28 | -- any). 29 | function query(self, command, uri, headers, data) 30 | 31 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 32 | -------------------------------------------------------------------------------- /doc/http.server.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- This library provides server-side capabilities for an HTTP service. 3 | -- Currently the server only supports HTTP/1.0, with no current plans to 4 | -- support 1.1 or higher, as it is only provided for convenience and as an 5 | -- example of using the ratchet libraries. 6 | module "ratchet.http.server" 7 | 8 | --- Creates a new HTTP server object. A new object should be created with this 9 | -- function for each socket created by accept()'ing a new connection on a 10 | -- listening socket. 11 | -- @param socket Used as the underlying socket for the query, under the 12 | -- assumption that encryption has been established as needed 13 | -- and no other I/O has taken place. 14 | -- @param handlers Table whose keys correspond to supported HTTP commands. The 15 | -- command is called as a method of this table, e.g. 16 | -- handlers:GET(...), with the arguments being the URI, 17 | -- headers, data, and source address. 18 | -- @param from Passed as the last argument to a handler function, typically 19 | -- used to send the source address of the connection (the second 20 | -- value returned by accept()). 21 | -- @return a new http.server object. 22 | function new(socket, handlers, from) 23 | 24 | --- Receives an HTTP query, calls the corresponding handler, and returns the 25 | -- results. 26 | -- @param self The http.server object. 27 | function handle(self) 28 | 29 | --- An alias to the handle() method, allowing the http.server object to be 30 | -- callable as if it were a function. 31 | -- @param self the http.server object. 32 | function __call(self) 33 | 34 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 35 | -------------------------------------------------------------------------------- /src/c/ratchet.h: -------------------------------------------------------------------------------- 1 | #ifndef __RATCHET_H 2 | #define __RATCHET_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | const char *ratchet_version (void); 9 | 10 | int luaopen_ratchet (lua_State *L); 11 | int luaopen_ratchet_error (lua_State *L); 12 | int luaopen_ratchet_socket (lua_State *L); 13 | int luaopen_ratchet_ssl (lua_State *L); 14 | int luaopen_ratchet_timerfd (lua_State *L); 15 | int luaopen_ratchet_zmqsocket (lua_State *L); 16 | int luaopen_ratchet_dns (lua_State *L); 17 | int luaopen_ratchet_dns_hosts (lua_State *L); 18 | int luaopen_ratchet_dns_resolv_conf (lua_State *L); 19 | int luaopen_ratchet_exec (lua_State *L); 20 | 21 | /* Error handling convenience functions. */ 22 | #define ratchet_error_errno(L, f, s) ratchet_error_errno_ln (L, f, s, __FILE__, __LINE__) 23 | #define ratchet_error_top(L, f, c) ratchet_error_top_ln (L, f, c, __FILE__, __LINE__) 24 | #define ratchet_error_str(L, f, c, ...) ratchet_error_str_ln (L, f, c, __FILE__, __LINE__, __VA_ARGS__) 25 | 26 | void ratchet_error_push_constructor (lua_State *L); 27 | void ratchet_error_push_code (lua_State *L, int e); 28 | int ratchet_error_errno_ln (lua_State *L, const char *function, const char *syscall, const char *file, int line); 29 | int ratchet_error_top_ln (lua_State *L, const char *function, const char *code, const char *file, int line); 30 | int ratchet_error_str_ln (lua_State *L, const char *function, const char *code, const char *file, int line, const char *description, ...); 31 | 32 | #define RATCHET_YIELD_GET ((void *) 1) 33 | #define RATCHET_YIELD_WRITE ((void *) 2) 34 | #define RATCHET_YIELD_READ ((void *) 3) 35 | #define RATCHET_YIELD_TIMEOUT ((void *) 4) 36 | #define RATCHET_YIELD_WAITALL ((void *) 5) 37 | #define RATCHET_YIELD_MULTIRW ((void *) 6) 38 | #define RATCHET_YIELD_PAUSE ((void *) 7) 39 | #define RATCHET_YIELD_SIGNAL ((void *) 8) 40 | 41 | #endif 42 | // vim:fdm=marker:ai:ts=4:sw=4:noet: 43 | -------------------------------------------------------------------------------- /test/test_multi_protocol.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function tcpctx1(host, port) 4 | local rec = ratchet.socket.prepare_tcp(host, port) 5 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 6 | socket:setsockopt("SO_REUSEADDR", true) 7 | socket:bind(rec.addr) 8 | socket:listen() 9 | 10 | ratchet.thread.attach(tcpctx2, host, port) 11 | 12 | local client = socket:accept() 13 | 14 | -- Portion being tested. 15 | -- 16 | client:send("hi") 17 | local data = client:recv() 18 | assert(data == "there") 19 | end 20 | 21 | function tcpctx2(host, port) 22 | local rec = ratchet.socket.prepare_tcp(host, port) 23 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 24 | socket:connect(rec.addr) 25 | 26 | -- Portion being tested. 27 | -- 28 | local data = socket:recv() 29 | assert(data == "hi") 30 | socket:send("there") 31 | end 32 | 33 | function zmqctx1(where) 34 | local rec = ratchet.zmqsocket.prepare_uri(where) 35 | local socket = ratchet.zmqsocket.new(rec.type) 36 | socket:bind(rec.endpoint) 37 | 38 | ratchet.thread.attach(zmqctx2, "rep:tcp://127.0.0.1:10026") 39 | 40 | -- Portion being tested. 41 | -- 42 | socket:send("hello") 43 | local data = socket:recv_all() 44 | assert(data == "world") 45 | end 46 | 47 | function zmqctx2(where) 48 | local rec = ratchet.zmqsocket.prepare_uri(where) 49 | local socket = ratchet.zmqsocket.new(rec.type) 50 | socket:connect(rec.endpoint) 51 | 52 | -- Portion being tested. 53 | -- 54 | local data = socket:recv() 55 | assert(data == "hello") 56 | socket:send("wo", true) 57 | socket:send("rld") 58 | end 59 | 60 | kernel = ratchet.new(function () 61 | ratchet.thread.attach(tcpctx1, "localhost", 10025) 62 | ratchet.thread.attach(zmqctx1, "req:tcp://*:10026") 63 | end) 64 | kernel:loop() 65 | 66 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 67 | -------------------------------------------------------------------------------- /src/lua/smtp/smtp_extensions.lua: -------------------------------------------------------------------------------- 1 | 2 | local smtp_extensions = {} 3 | smtp_extensions.__index = smtp_extensions 4 | 5 | -- {{{ smtp_extensions.new() 6 | function smtp_extensions.new() 7 | local self = {} 8 | setmetatable(self, smtp_extensions) 9 | 10 | self.extensions = {} 11 | 12 | return self 13 | end 14 | -- }}} 15 | 16 | -- {{{ smtp_extensions:reset() 17 | function smtp_extensions:reset() 18 | self.extensions = {} 19 | end 20 | -- }}} 21 | 22 | -- {{{ smtp_extensions:has() 23 | function smtp_extensions:has(ext) 24 | return self.extensions[ext:upper()] 25 | end 26 | -- }}} 27 | 28 | -- {{{ smtp_extensions:add() 29 | function smtp_extensions:add(ext, param) 30 | if param then 31 | self.extensions[ext:upper()] = param 32 | else 33 | self.extensions[ext:upper()] = true 34 | end 35 | end 36 | -- }}} 37 | 38 | -- {{{ smtp_extensions:drop() 39 | function smtp_extensions:drop(ext) 40 | self.extensions[ext:upper()] = nil 41 | end 42 | -- }}} 43 | 44 | -- {{{ smtp_extensions:parse_string() 45 | function smtp_extensions:parse_string(str) 46 | local pattern = "^%s*(%w[%w%-]*)%s*(.-)%s*$" 47 | local header 48 | str = str .. "\r\n" -- incoming strings will not have a final endline. 49 | for line in str:gmatch("(.-)%\r?%\n") do 50 | if not header then 51 | header = line 52 | else 53 | self:add(line:match(pattern)) 54 | end 55 | end 56 | 57 | return header or str 58 | end 59 | -- }}} 60 | 61 | -- {{{ smtp_extensions:build_string() 62 | function smtp_extensions:build_string(header) 63 | local lines = {header} 64 | for k, v in pairs(self.extensions) do 65 | if v == true then 66 | table.insert(lines, k) 67 | else 68 | table.insert(lines, k.." "..tostring(v)) 69 | end 70 | end 71 | return table.concat(lines, "\r\n") 72 | end 73 | -- }}} 74 | 75 | return smtp_extensions 76 | 77 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 78 | -------------------------------------------------------------------------------- /src/lua/smtp/data_sender.lua: -------------------------------------------------------------------------------- 1 | 2 | local data_sender = {} 3 | data_sender.__index = data_sender 4 | 5 | -- {{{ data_sender.new() 6 | function data_sender.new(data, iter_size) 7 | local self = {} 8 | setmetatable(self, data_sender) 9 | 10 | self.data = data 11 | self.iter_size = iter_size or 1024 12 | 13 | return self 14 | end 15 | -- }}} 16 | 17 | -- {{{ iterator_func() 18 | local function iterator_func(invariant, i) 19 | local message = invariant.full_message 20 | local len = invariant.iter_size 21 | local last_part = invariant.last_part 22 | 23 | -- If we're done iterator, jump out. 24 | if invariant.done then 25 | return 26 | end 27 | 28 | local piece = message:sub(i, i+len-1) 29 | if piece == "" then 30 | -- We are done iterating over the message, but need to return 31 | -- the ".\r\n" to end DATA command. 32 | invariant.done = true 33 | local end_marker = ".\r\n" 34 | if #last_part > 0 and last_part ~= "\r\n" then 35 | end_marker = "\r\n" .. end_marker 36 | end 37 | return i, end_marker 38 | end 39 | 40 | piece = last_part .. piece 41 | 42 | piece = piece:gsub("%\r", "") 43 | piece = piece:gsub("%\n", "\r\n") 44 | piece = piece:gsub("%\n%.", "\n..") 45 | 46 | local delta = (2 * len) - (#piece - #last_part) 47 | piece = piece:sub(1+#last_part, len+#last_part) 48 | invariant.last_part = piece:sub(-2) 49 | 50 | return i + delta, piece 51 | end 52 | -- }}} 53 | 54 | -- {{{ data_sender:iter() 55 | function data_sender:iter() 56 | local invariant = {iter_size = self.iter_size, 57 | last_part = "", 58 | done = false, 59 | full_message = self.data} 60 | 61 | return iterator_func, invariant, 1 62 | end 63 | -- }}} 64 | 65 | -- {{{ data_sender:send() 66 | function data_sender:send(io) 67 | for i, piece in self:iter() do 68 | io:buffered_send(piece) 69 | end 70 | end 71 | -- }}} 72 | 73 | return data_sender 74 | 75 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 76 | -------------------------------------------------------------------------------- /test/test_socketpad.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | require "ratchet.socketpad" 3 | 4 | function ctx1(host, port) 5 | local rec = ratchet.socket.prepare_tcp(host, port) 6 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 7 | socket:setsockopt("SO_REUSEADDR", true) 8 | socket:bind(rec.addr) 9 | socket:listen() 10 | 11 | ratchet.thread.attach(ctx3, host, port) 12 | 13 | local client = socket:accept() 14 | ratchet.thread.attach(ctx2, client) 15 | end 16 | 17 | function ctx2(socket) 18 | local pad = ratchet.socketpad.new(socket) 19 | 20 | -- Portion being tested. 21 | -- 22 | local data = socket:recv() 23 | assert('line one\r\nline two\r\n' == data) 24 | socket:send('break') 25 | 26 | local data1 = pad:recv(3) 27 | assert(3 == #pad:peek()) 28 | local data2 = pad:recv(3) 29 | assert(0 == #pad:peek()) 30 | assert('abc' == data1) 31 | assert('123' == data2) 32 | socket:send('break') 33 | 34 | local data1 = pad:recv('\r\n') 35 | assert(8 == #pad:peek()) 36 | local data2 = pad:recv('\r\n') 37 | assert(0 == #pad:peek()) 38 | assert('line 1\r\n' == data1) 39 | assert('line 2\r\n' == data2) 40 | socket:send('break') 41 | 42 | local data, incomplete = pad:recv('\r\n') 43 | assert(data == 'half') 44 | assert(incomplete) 45 | pad:close() 46 | end 47 | 48 | function ctx3(host, port) 49 | local rec = ratchet.socket.prepare_tcp(host, port) 50 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 51 | socket:connect(rec.addr) 52 | 53 | local pad = ratchet.socketpad.new(socket) 54 | 55 | -- Portion being tested. 56 | -- 57 | pad:send('line one\r\n', true) 58 | pad:send('line two\r\n') 59 | assert('break' == socket:recv()) 60 | 61 | pad:send('abc123') 62 | assert('break' == socket:recv()) 63 | 64 | pad:send('line 1\r\nline 2\r\n') 65 | assert('break' == socket:recv()) 66 | 67 | pad:send('half') 68 | pad:close() 69 | end 70 | 71 | kernel = ratchet.new(function () 72 | ratchet.thread.attach(ctx1, "localhost", 10025) 73 | end) 74 | kernel:loop() 75 | 76 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 77 | -------------------------------------------------------------------------------- /doc/timerfd.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- The timerfd library provides an advanced timer interface using the system 3 | -- library of the same name. Timers from this library can be set to fire 4 | -- repeatedly and provide counts if several fires have taken place since last 5 | -- check. These functions can fail, see error handling section in manual for 6 | -- details. 7 | module "ratchet.timerfd" 8 | 9 | --- Returns a new timerfd object. 10 | -- @param clock either "monotonic" or "realtime" based on the desired clock 11 | -- type. See man page timerfd_create(2) for details. The default 12 | -- is "monotonic". 13 | -- @return a new timerfd object. 14 | function new(clock) 15 | 16 | --- Returns the file descriptor for the internal timerfd object. 17 | -- @param self the timerfd object. 18 | -- @return the file descriptor. 19 | function get_fd(self) 20 | 21 | --- Arms (or disarms) the timerfd object. This function corresponds to the 22 | -- timerfd_settime() system call. If wait_seconds is not given or nil, the 23 | -- timer is disarmed. 24 | -- @param self the timerfd object. 25 | -- @param wait_seconds seconds until first fire of timerfd. 26 | -- @param interval_seconds seconds until subsequent fires. 27 | -- @param flag either "relative" or "absolute", default "relative". 28 | function settime(self, wait_seconds, interval_seconds, flag) 29 | 30 | --- Returns information about the current timer, corresponding to the 31 | -- timerfd_gettime() system call. This would be the number of seconds until 32 | -- the next fire of the timer. 33 | -- @param self the timerfd object. 34 | -- @return seconds until next timer firing. 35 | function gettime(self) 36 | 37 | --- Pauses the current thread until the next firing of the timer. If the 38 | -- timer has fired since the last call to read(), it will return immediately. 39 | -- @param self the timerfd object. 40 | -- @return the number of fires since last call to read(). 41 | function read(self) 42 | 43 | --- Closes the timerfd internal file descriptor. This is called automatically 44 | -- when the timerfd object is collected, for convenience. 45 | -- @param self the timerfd object. 46 | function close(self) 47 | 48 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 49 | -------------------------------------------------------------------------------- /test/test_socket_multi_recv.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | function ctx1(host, port) 4 | local rec = ratchet.socket.prepare_tcp(host, port) 5 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 6 | socket:setsockopt("SO_REUSEADDR", true) 7 | socket:bind(rec.addr) 8 | socket:listen() 9 | 10 | ratchet.thread.attach(client1, host, port) 11 | ratchet.thread.attach(client2, host, port) 12 | 13 | local c1 = socket:accept() 14 | local c2 = socket:accept() 15 | 16 | local dataA, socketA = assert(ratchet.socket.multi_recv {c1, c2}) 17 | assert((dataA == "data1" and socketA == c1) or (dataA == "data2" and socketB == c2)) 18 | local dataB, socketB = assert(ratchet.socket.multi_recv {c1, c2}) 19 | assert((dataB == "data1" and socketB == c1) or (dataB == "data2" and socketB == c2)) 20 | assert(dataA ~= dataB) 21 | 22 | c1:send("sync") 23 | c2:send("sync") 24 | 25 | local dataC, socketC = assert(ratchet.socket.multi_recv {c1, c2}) 26 | assert(dataC == "data3" and socketC == c1) 27 | 28 | c1:send("sync") 29 | c2:send("sync") 30 | 31 | assert("" == ratchet.socket.multi_recv {c1, c2}) 32 | assert("" == ratchet.socket.multi_recv {c1, c2}) 33 | c1:close() 34 | c2:close() 35 | end 36 | 37 | function client1(host, port) 38 | local rec = ratchet.socket.prepare_tcp(host, port) 39 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 40 | socket:connect(rec.addr) 41 | 42 | socket:send("data1") 43 | assert("sync" == socket:recv()) 44 | 45 | socket:send("data3") 46 | assert("sync" == socket:recv()) 47 | 48 | socket:close() 49 | end 50 | 51 | function client2(host, port) 52 | local rec = ratchet.socket.prepare_tcp(host, port) 53 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 54 | socket:connect(rec.addr) 55 | 56 | socket:send("data2") 57 | assert("sync" == socket:recv()) 58 | 59 | assert("sync" == socket:recv()) 60 | 61 | socket:close() 62 | end 63 | 64 | kernel = ratchet.new(function () 65 | ratchet.thread.attach(ctx1, "localhost", 10025) 66 | end) 67 | kernel:loop() 68 | 69 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 70 | -------------------------------------------------------------------------------- /doc/error.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- A ratchet.error object is thrown in most cases when ratchet functions error. 3 | -- This object exposes certain information to present to the user or to make 4 | -- programmatic decisions based on. Converting these objects to a string 5 | -- presents human-readable information about the error. 6 | module("ratchet.error") 7 | 8 | --- This function allows manual creation of error objects. Usually it is created 9 | -- automatically by ratchet functions when an error occurs. The names of the 10 | -- parameters to this function are also the indices to access the information 11 | -- on a created ratchet.error object. 12 | -- @param descripion human-readable description of the error. 13 | -- @param code a string code that can identify the error message. 14 | -- @param func optional string identifying the function throwing the error. 15 | -- @param file optional C file where the error was thrown. 16 | -- @param line optional C file line number where the error was thrown. 17 | -- @param syscall optional string identifying system call causing the error. 18 | -- @param errno optional integer identifying the system error code. 19 | -- @return a new ratchet.error object. 20 | function new(description, code, func, file, line, syscall, errno) 21 | 22 | --- Checks is the code assigned to an error message matches the given code. This 23 | -- function is also exposed as a method of ratchet.error objects. 24 | -- @param error the ratchet.error object to check. 25 | -- @param code the code to compare against. 26 | -- @return true if the error matches the code, false otherwise. 27 | function is(error, code) 28 | 29 | --- Returns a human-readable error message, with no traceback information. 30 | -- @param self the ratchet.error object. 31 | -- @return an error string. 32 | function get_string(self) 33 | 34 | --- This meta-method converts the error object into a Lua traceback string 35 | -- including a human-readable error message. If the error occured in a thread, 36 | -- the traceback is taken from within that thread. 37 | -- @param self the ratchet.error object. 38 | -- @return a Lua traceback string. 39 | -- @see debug.traceback() 40 | -- @see tostring() 41 | function __tostring(self) 42 | 43 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 44 | -------------------------------------------------------------------------------- /test/test_ssl_send_recv.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | 3 | counter = 0 4 | 5 | function ctx1(host, port) 6 | local rec = ratchet.socket.prepare_tcp(host, port) 7 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 8 | socket:setsockopt("SO_REUSEADDR", true) 9 | socket:bind(rec.addr) 10 | socket:listen() 11 | 12 | ratchet.thread.attach(ctx2, host, port) 13 | 14 | local client = socket:accept() 15 | 16 | -- Portion being tested. 17 | -- 18 | local data = client:recv() 19 | assert(data == "not encrypted") 20 | client:send("yet") 21 | 22 | local enc = client:encrypt(ssl1) 23 | enc:server_handshake() 24 | 25 | assert("AES256-SHA" == enc:get_cipher()) 26 | 27 | client:send("hello") 28 | local data = client:recv(5) 29 | assert(data == "world") 30 | 31 | local data = client:recv() 32 | assert(data == "foo") 33 | client:send("bar") 34 | 35 | enc:shutdown() 36 | client:close() 37 | 38 | counter = counter + 1 39 | end 40 | 41 | function ctx2(host, port) 42 | local rec = ratchet.socket.prepare_tcp(host, port) 43 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 44 | socket:connect(rec.addr) 45 | 46 | -- Portion being tested. 47 | -- 48 | socket:send("not encrypted") 49 | local data = socket:recv() 50 | assert(data == "yet") 51 | 52 | local enc = socket:encrypt(ssl2) 53 | enc:client_handshake() 54 | 55 | local got_cert, verified, host_matched = enc:verify_certificate(rec.host) 56 | assert(got_cert and verified and host_matched) 57 | 58 | assert("AES256-SHA" == enc:get_cipher()) 59 | assert("CN=localhost" == enc:get_rfc2253()) 60 | 61 | local data = socket:recv(5) 62 | assert(data == "hello") 63 | socket:send("world") 64 | 65 | socket:send("foo") 66 | local data = socket:recv() 67 | assert(data == "bar") 68 | 69 | enc:shutdown() 70 | socket:close() 71 | 72 | counter = counter + 2 73 | end 74 | 75 | ssl1 = ratchet.ssl.new(ratchet.ssl.SSLv3_server) 76 | ssl1:load_certs("cert.pem") 77 | 78 | ssl2 = ratchet.ssl.new(ratchet.ssl.SSLv3_client) 79 | ssl2:load_cas(nil, "cert.pem") 80 | 81 | kernel = ratchet.new(function () 82 | ratchet.thread.attach(ctx1, "localhost", 10025) 83 | end) 84 | kernel:loop() 85 | 86 | assert(counter == 3) 87 | 88 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 89 | -------------------------------------------------------------------------------- /doc/smtp.server.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- This library provides server-side capabilities for an SMTP server. Currently 3 | -- the server will advertize the 8BITMIME, PIPELINING, and ENHANCEDSTATUSCODES 4 | -- extensions and is capable of handling arbitrary, custom commands as well. It 5 | -- is provided for convenience and as an example of using the ratchet 6 | -- libraries. 7 | module "ratchet.smtp.server" 8 | 9 | --- Creates a new SMTP server object. A new object should be created with this 10 | -- function for each socket created by accept()'ing a new connection on a 11 | -- listening socket. 12 | -- @param socket Used as the underlying socket for the session, under the 13 | -- assumption that no I/O has taken place, except possibly 14 | -- encryption if not using STARTTLS. 15 | -- @param handlers Table whose keys correspond to supported SMTP commands. The 16 | -- command is called as a method of this table, e.g. 17 | -- handlers:MAIL(...), with the first argument being a reply 18 | -- table that the method can use to set the "code" and 19 | -- "message" keys to control the response. See the manual for 20 | -- more details. 21 | -- @param tls_context If given as a ratchet.ssl context object, the STARTTLS 22 | -- extension will be enabled allowing clients to encrypt the 23 | -- session. If given as boolean true, it is assumed that the 24 | -- socket was previously encrypted and the session is 25 | -- secure. 26 | -- @param tls_immediately If enabled, instead of providing the STARTTLS 27 | -- extension, the connection would be immediately 28 | -- encrypted with tls_context before sending the banner. 29 | -- @return a new smtp.server object. 30 | function new(socket, handlers, tls_context, tls_immediately) 31 | 32 | --- Sends a banner and starts listening for and responding to SMTP commands from 33 | -- the client. This function continues until the client sends a QUIT command. 34 | -- @param self the smtp.server object. 35 | function handle(self) 36 | 37 | --- An alias to the handle() method, allowing the smtp.server object to be 38 | -- callable as if it were a function. 39 | -- @param self the smtp.server object. 40 | function __call(self) 41 | 42 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 43 | -------------------------------------------------------------------------------- /src/lua/http/client.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | local common = require "ratchet.http.common" 4 | 5 | ratchet.http = ratchet.http or {} 6 | ratchet.http.client = {} 7 | ratchet.http.client.__index = ratchet.http.client 8 | 9 | -- {{{ ratchet.http.client.new() 10 | function ratchet.http.client.new(socket) 11 | local self = {} 12 | setmetatable(self, ratchet.http.client) 13 | 14 | self.socket = socket 15 | 16 | return self 17 | end 18 | -- }}} 19 | 20 | -- {{{ build_request_and_headers() 21 | local function build_request_and_headers(command, uri, headers) 22 | local ret = command:upper() .. " " .. uri .. " HTTP/1.0\r\n" 23 | if headers and #headers then 24 | ret = ret .. common.build_header_string(headers) 25 | end 26 | ret = ret .. "\r\n" 27 | return ret 28 | end 29 | -- }}} 30 | 31 | -- {{{ send_request() 32 | local function send_request(self, command, uri, headers, data) 33 | local request = build_request_and_headers(command, uri, headers, data) 34 | local remaining = request 35 | if data then 36 | remaining = remaining .. data 37 | end 38 | repeat 39 | remaining = self.socket:send(remaining) 40 | until not remaining 41 | self.socket:shutdown("write") 42 | end 43 | -- }}} 44 | 45 | -- {{{ parse_response() 46 | local function parse_response(socket) 47 | local full_reply = "" 48 | repeat 49 | local data = socket:recv() 50 | if #data > 0 then 51 | full_reply = full_reply .. data 52 | end 53 | until data == "" 54 | 55 | local enc = socket:get_encryption() 56 | if enc then 57 | enc:shutdown() 58 | end 59 | socket:close() 60 | 61 | local code, reason, lineend = full_reply:match("^HTTP%/%d%.%d (%d%d%d) (.-)\r\n()") 62 | local headers, data 63 | 64 | if not code then 65 | return 66 | end 67 | 68 | headers, lineend = common.parse_header_string(full_reply, lineend) 69 | 70 | lineend = full_reply:match("\r\n\r\n()", lineend) 71 | if lineend then 72 | data = full_reply:sub(lineend) 73 | end 74 | 75 | return tonumber(code), reason, headers, data 76 | end 77 | -- }}} 78 | 79 | -- {{{ ratchet.http.client:query() 80 | function ratchet.http.client:query(command, uri, headers, data) 81 | send_request(self, command, uri, headers, data) 82 | return parse_response(self.socket) 83 | end 84 | -- }}} 85 | 86 | return ratchet.http.client 87 | 88 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 89 | -------------------------------------------------------------------------------- /doc/ratchet.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- The purpose of the ratchet library is to provide a generic socket control 3 | -- mechanism for large numbers of sockets without using threads or losing the ease 4 | -- of synchronous socket programming. Along with networking, the library provides 5 | -- a sort of kernel to allow side-by-side execution of different streams of logic. 6 | module "ratchet" 7 | 8 | --- Constructs and returns a new ratchet object. This also creates a new libevent 9 | -- control structure that is freed when the object is collected. 10 | -- @param entry called initially as the entry-point ratchet thread. 11 | -- @param errh called after an error in a ratchet thread before the stack is 12 | -- unwound. It is given two arguments, the error and the thread. 13 | -- @return a new ratchet object. 14 | function new(entry, errh) 15 | 16 | --- Returns the polling method used behind-the-scenes by libevent. 17 | -- @param self the ratchet object. 18 | -- @return a string identifying the kernel event mechanism (kqueue, epoll, etc.). 19 | function get_method(self) 20 | 21 | --- Returns the number of active threads the ratchet object is managing. This 22 | -- includes threads that are currently running and threads that are paused, but 23 | -- not any that have completed or errored. 24 | -- @param self the ratchet object. 25 | -- @return the number of active threads. 26 | function get_num_threads(self) 27 | 28 | --- Processes thread events in a loop. This function simply runs loop_once() with 29 | -- blocking until it returns false. 30 | -- @param self the ratchet object. 31 | function loop(self) 32 | 33 | --- Executes one iteration of event processing. This may include starting a new 34 | -- thread or resuming one that was paused manually or by an event. This function 35 | -- may block unless noblock is given true. 36 | -- @param self the ratchet object. 37 | -- @param noblock given as true, this function will not block waiting for events. 38 | -- @return false if all threads have completed execution, true otherwise. 39 | function loop_once(self, noblock) 40 | 41 | --- Gets the table (see ratchet.thread.space()) associated with the given thread. 42 | -- @param self the ratchet object. 43 | -- @param thread the thread to get the table for. 44 | -- @param default if given, this table (or any object) will be set and 45 | -- returned if no other space has already been set for the 46 | -- thread. 47 | function get_space(self, thread, default) 48 | 49 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 50 | -------------------------------------------------------------------------------- /test/test_message_bus_sockets.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | require "ratchet.bus" 3 | 4 | -- {{{ request_to_bus() 5 | local function request_to_bus(obj) 6 | return tostring(obj.id), {tostring(obj.stuff)} 7 | end 8 | -- }}} 9 | 10 | -- {{{ request_from_bus() 11 | local function request_from_bus(data, attachments) 12 | return {id = data, stuff = attachments[1]} 13 | end 14 | -- }}} 15 | 16 | -- {{{ response_to_bus() 17 | function response_to_bus(obj) 18 | return tostring(obj) 19 | end 20 | -- }}} 21 | 22 | -- {{{ response_from_bus() 23 | function response_from_bus(data) 24 | return tonumber(data) 25 | end 26 | -- }}} 27 | 28 | function ctx1(host, port) 29 | local s_rec = ratchet.socket.prepare_tcp(host, port) 30 | local s_socket = ratchet.socket.new(s_rec.family, s_rec.socktype, s_rec.protocol) 31 | s_socket:setsockopt("SO_REUSEADDR", true) 32 | s_socket:bind(s_rec.addr) 33 | s_socket:listen() 34 | 35 | local c1_rec = ratchet.socket.prepare_tcp(host, port) 36 | local c1_socket = ratchet.socket.new(c1_rec.family, c1_rec.socktype, c1_rec.protocol) 37 | c1_socket:connect(c1_rec.addr) 38 | 39 | local c2_rec = ratchet.socket.prepare_tcp(host, port) 40 | local c2_socket = ratchet.socket.new(c2_rec.family, c2_rec.socktype, c2_rec.protocol) 41 | c2_socket:connect(c2_rec.addr) 42 | 43 | ratchet.thread.attach(server_bus, s_socket, c2_socket) 44 | ratchet.thread.attach(client_bus_1, c1_socket) 45 | end 46 | 47 | function server_bus(socket, c2_socket) 48 | local bus = ratchet.bus.new_server(socket, request_from_bus, response_to_bus) 49 | 50 | local transaction1, request1 = bus:recv_request() 51 | assert(request1.id == "operation falcon") 52 | assert(request1.stuff == "important") 53 | 54 | ratchet.thread.attach(client_bus_2, c2_socket) 55 | 56 | local transaction2, request2 = bus:recv_request() 57 | assert(request2.id == "operation condor") 58 | assert(request2.stuff == "confidential") 59 | 60 | transaction1:send_response(1337) 61 | transaction2:send_response(7357) 62 | end 63 | 64 | function client_bus_1(socket) 65 | local bus = ratchet.bus.new_client(socket, request_to_bus, response_from_bus) 66 | 67 | local test_request = {id = "operation falcon", stuff = "important"} 68 | 69 | local transaction = bus:send_request(test_request) 70 | local response = transaction:recv_response() 71 | assert(1337 == response) 72 | end 73 | 74 | function client_bus_2(socket) 75 | local bus = ratchet.bus.new_client(socket, request_to_bus, response_from_bus) 76 | 77 | local test_request = {id = "operation condor", stuff = "confidential"} 78 | 79 | local transaction = bus:send_request(test_request) 80 | local response = transaction:recv_response() 81 | assert(7357 == response) 82 | end 83 | 84 | kernel = ratchet.new(function () 85 | ratchet.thread.attach(ctx1, "localhost", 10025) 86 | end) 87 | kernel:loop() 88 | 89 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 90 | -------------------------------------------------------------------------------- /test/Makefile.am: -------------------------------------------------------------------------------- 1 | if HAVE_LUA_INTERPRETER 2 | 3 | if ENABLE_MODULE 4 | TESTS_ENVIRONMENT = LUA_CPATH="../src/c/.libs/?.so;;" LUA_PATH="./?.lua;./?/init.lua;;" @LUA@ 5 | else 6 | TESTS_ENVIRONMENT = LUA_PATH="./?.lua;./?/init.lua;;" @LUA@ -e \ 7 | 'package.preload["ratchet"] = package.loadlib("../src/c/.libs/libratchet.so", "luaopen_ratchet")' 8 | endif 9 | 10 | TESTS = test_exec.lua \ 11 | test_listen_connect.lua \ 12 | test_send_recv.lua \ 13 | test_pcall_kernel_loop.lua \ 14 | test_wait_all.lua \ 15 | test_thread_kill.lua \ 16 | test_thread_space.lua \ 17 | test_thread_alarm.lua \ 18 | test_shutdown.lua \ 19 | test_socketpair.lua \ 20 | test_socketpad.lua \ 21 | test_socket_byteorder.lua \ 22 | test_socket_multi_recv.lua \ 23 | test_message_bus_sockets.lua \ 24 | test_message_bus_local.lua \ 25 | test_unix_sockets.lua \ 26 | test_event_timeout.lua \ 27 | test_ssl_send_recv.lua \ 28 | test_zmq_send_recv.lua \ 29 | test_multi_protocol.lua \ 30 | test_pause_unpause.lua \ 31 | test_callable_object.lua \ 32 | test_http_get.lua \ 33 | test_smtp.lua \ 34 | test_smtp_bigmessage.lua \ 35 | test_smtp_starttls.lua \ 36 | test_smtp_tls.lua \ 37 | test_sockopt.lua 38 | XFAIL_TESTS = 39 | EXTRA_DIST = $(TESTS) 40 | 41 | ratchet-link: 42 | ln -nsf ../src/lua ./ratchet 43 | 44 | check_DATA = ratchet-link 45 | CLEANFILES = ratchet 46 | 47 | if HAVE_OPENSSL 48 | check_DATA += cert.pem 49 | CLEANFILES += cert.pem 50 | cert.pem: 51 | openssl req -x509 -nodes -subj '/CN=localhost' -newkey rsa:1024 -keyout $@ -out $@ > /dev/null 52 | else 53 | XFAIL_TESTS += test_ssl_send_recv.lua \ 54 | test_smtp_starttls.lua \ 55 | test_smtp_tls.lua 56 | endif 57 | 58 | if !HAVE_SOCKET 59 | XFAIL_TESTS += test_listen_connect.lua \ 60 | test_send_recv.lua \ 61 | test_shutdown.lua \ 62 | test_socketpair.lua \ 63 | test_socket_byteorder.lua \ 64 | test_socket_multi_read.lua \ 65 | test_unix_sockets.lua \ 66 | test_ssl_send_recv.lua \ 67 | test_event_timeout.lua \ 68 | test_multi_protocol.lua \ 69 | test_sockopt.lua 70 | endif 71 | 72 | if !ENABLE_SOCKETPAD 73 | XFAIL_TESTS += test_socketpad.lua 74 | endif 75 | 76 | if !HAVE_DNS 77 | XFAIL_TESTS += test_listen_connect.lua \ 78 | test_send_recv.lua \ 79 | test_shutdown.lua \ 80 | test_ssl_send_recv.lua \ 81 | test_event_timeout.lua \ 82 | test_multi_protocol.lua \ 83 | test_sockopt.lua 84 | endif 85 | 86 | if !HAVE_ZMQ 87 | XFAIL_TESTS += test_zmq_send_recv.lua \ 88 | test_multi_protocol.lua 89 | endif 90 | 91 | if !ENABLE_HTTP 92 | XFAIL_TESTS += test_http_get.lua 93 | endif 94 | 95 | if !ENABLE_BUS 96 | XFAIL_TESTS += test_message_bus_sockets.lua \ 97 | test_message_bus_local.lua 98 | endif 99 | 100 | if !ENABLE_SMTP 101 | XFAIL_TESTS += test_smtp.lua \ 102 | test_smtp_bigmessage.lua \ 103 | test_smtp_starttls.lua 104 | endif 105 | 106 | endif 107 | -------------------------------------------------------------------------------- /doc/ssl.session.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- The ssl.session library provides an SSL encryption session. This session 3 | -- will apply to one encrypted session, such as a socket. These objects are 4 | -- not created directly, but are returned by ssl:create_session(). These 5 | -- functions can fail, see error handling section in manual for details. 6 | module "ratchet.ssl.session" 7 | 8 | --- Returns the communication engine powering the encrypted session, generally 9 | -- a socket object. 10 | -- @param self the ssl session object. 11 | -- @return the communication engine object. 12 | function get_engine(self) 13 | 14 | --- Checks the remote peer's certificate against the trusted CAs, and optionally 15 | -- checks that Common-Name field of the certificate matches the given host. 16 | -- @param self the ssl session object. 17 | -- @param host optional host to compare against "Common Name" field(s). 18 | -- @return a boolean if the peer provided a certificate, a boolean if the 19 | -- peer's certificate verified, and a boolean if host is provided and 20 | -- matches the certificate's "Common Name" field(s). 21 | function verify_certificate(self, host) 22 | 23 | --- Gets the current SSL cipher. 24 | -- @param self the ssl session object. 25 | -- @return current cipher name. 26 | function get_cipher(self) 27 | 28 | --- Gets a RFC 2253 string representation of the remote peer certificate. 29 | -- @param self the ssl session object. 30 | -- @return see RFC 2253. 31 | function get_rfc2253(self) 32 | 33 | --- Initiates the encryption handshake for the server-side connection, e.g. the 34 | -- socket returned by accept(). 35 | -- @param self the ssl session object. 36 | function server_handshake(self) 37 | 38 | --- Initiates the encryption handshake for the client-side connection, e.g. the 39 | -- socket that ran connect(). 40 | -- @param self the ssl session object. 41 | function client_handshake(self) 42 | 43 | --- Initiates a clean shutdown of the encryption session. 44 | -- @param the ssl session object. 45 | function shutdown(self) 46 | 47 | --- Reads data on the encrypted session. This method is rarely called directly, 48 | -- as it is usually called by the communication engine itself. For example, 49 | -- with socket objects, calling recv() after encrypt() will actually call this 50 | -- method. The return value will be an empty string if the other side has 51 | -- shut down. 52 | -- @param self the ssl session object. 53 | -- @param maxlen optional maximum number of bytes to read. 54 | -- @return string of data received on the session, or nil on timeout. 55 | function read(self, maxlen) 56 | 57 | --- Writes data on the encrypted session. This method is rarely called directly, 58 | -- as it is usually called by the communication engine itself. For example, 59 | -- with socket objects, calling send() after encrypt() will actually call this 60 | -- method. 61 | -- @param self the ssl session object. 62 | -- @param data the data to send on the session. 63 | -- @return true if sent successfully, nil on timeout. 64 | function write(self, data) 65 | 66 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 67 | -------------------------------------------------------------------------------- /doc/zmqsocket.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- The zmqsocket library provides an implementation of the ZeroMQ socket 3 | -- library on top of the ratchet library. Any methods that pause execution 4 | -- MUST be called from within a thread attached to a ratchet object. These 5 | -- functions can fail, see error handling section in manual for details. 6 | module "ratchet.zmqsocket" 7 | 8 | --- Returns a new zmqsocket object. 9 | -- @param type string corresponding to ZeroMQ socket types, e.g. "PAIR". 10 | -- @return a new zmqsocket object. 11 | function new(type) 12 | 13 | --- URI schema handler for TCP connection strings. Strings must with "zmq", 14 | -- followed by a ":", followed by a ZeroMQ socket type (e.g. "PAIR"), 15 | -- followed by a ":", followed by something suitable to be passed to 16 | -- zmq_connect() or zmq_bind() as an endpoint. See the manual page for 17 | -- complete details. 18 | -- @param uri the URI connection string. 19 | -- @return table containing keys "type" and "endpoint" useful in creation 20 | -- and usage of zmqsocket objects. 21 | function prepare_uri(uri) 22 | 23 | --- Returns the internal socket file descriptor. 24 | -- @param self the zmqsocket object. 25 | -- @return a file descriptor. 26 | function get_fd(self) 27 | 28 | --- Gets the current timeout for all methods that pause the thread. 29 | -- @param self the zmqsocket object. 30 | -- @return the current timeout in seconds. 31 | function get_timeout(self) 32 | 33 | --- Sets the current timeout for all methods that pause the thread. 34 | -- @param self the zmqsocket object. 35 | -- @param seconds the new timeout in seconds. 36 | function set_timeout(self, seconds) 37 | 38 | --- Binds the socket to the given endpoint and accepts connections. 39 | -- @param self the zmqsocket object. 40 | -- @param endpoint connection string to bind to. 41 | function bind(self, endpoint) 42 | 43 | --- Connects the socket to the given endpoint. 44 | -- @param self the zmqsocket object. 45 | -- @param endpoint connection string to connect to. 46 | function connect(self, endpoint) 47 | 48 | --- Pauses the current thread until the socket is ready to send data and then 49 | -- sends it. 50 | -- @param self the zmqsocket object. 51 | -- @param data string with the data to send. 52 | -- @param more_coming optionally pass true if more message parts will be sent, 53 | -- last message part more_coming must be false or not 54 | -- given. 55 | function send(self, data, more_coming) 56 | 57 | --- Pauses the current thread until the socket has data to receive and returns 58 | -- it. This call only receives one message part, but tells if this message has 59 | -- more parts to follow. 60 | -- @param self the zmqsocket object. 61 | -- @return string containing data, followed by true if there are more parts of 62 | -- this message to receive. 63 | function recv(self) 64 | 65 | --- Loops calls to recv() until all message parts are received. Message parts 66 | -- are concatenated into one large string. 67 | -- @param self the zmqsocket object. 68 | -- @return string containing the data. 69 | function recv_all(self) 70 | 71 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 72 | -------------------------------------------------------------------------------- /doc/bus.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- This module is designed as a convenience to make requests in the form of a 3 | -- message queue, with simple responses being returned to the sender. Two 4 | -- different forms exist, allowing sharing either between threads in the same 5 | -- ratchet context or sharing across a socket. This can be especially useful 6 | -- as an abstraction when two systems may be running separately or running 7 | -- side-by-side in the same process. 8 | module("ratchet.bus") 9 | 10 | --- Creates a new, local message bus. Waiting for a request in a thread will 11 | -- pause that thread until another sends a request, and waiting for a response 12 | -- will pause until one is sent. Requests are queued if a thread is not 13 | -- actively waiting for them. The same bus object, created by this function, 14 | -- is used for both the client- and server-sides of the message bus. 15 | -- @return a new local message bus, returned twice. 16 | function new_local() 17 | 18 | --- Creates the server-side of a socket-based message bus. Receiving a request 19 | -- will wait for a new client connection and a request on that connection. 20 | -- The response is then sent back over the connection to the client-side of the 21 | -- bus. 22 | -- @param socket a socket already listening for new connections. 23 | -- @param request_from_bus an arbitrary function that accepts a string and 24 | -- converts it into a request object. 25 | -- @param response_to_bus an arbitrary function that converts a response object 26 | -- into a string. 27 | function new_server(socket, request_from_bus, response_to_bus) 28 | 29 | --- Creates the client-side of a socket-based message bus. After sending a 30 | -- request, the client-side will need wait to receive a response. 31 | -- @param socket a socket already connected to a server. 32 | -- @param request_to_bus an arbitrary function that converts a request object 33 | -- into a string. 34 | -- @param response_from_bus an arbitrary function that accepts a string and 35 | -- converts it into a response object. 36 | function new_client(socket, request_to_bus, response_from_bus) 37 | 38 | --- Receives a request from the server-side of a message bus. This function can 39 | -- be used by a local message bus object created with new_local() 40 | -- or a server-side socket message bus object created with 41 | -- new_server(). 42 | -- @param self the server-side of a message bus. 43 | -- @return a ratchet.bus.transaction object on which to send the 44 | -- response, followed by the request object received. 45 | function recv_request(self) 46 | 47 | --- Sends a request from the client-side of a message bus. This function can be 48 | -- used by a local message bus object created with new_local() or 49 | -- a client-side message bus object created with new_client(). 50 | -- @param self the client-side of a message bus. 51 | -- @param request a request object to send. 52 | -- @return a ratchet.bus.transaction object on which to receive 53 | -- the response. 54 | function send_request(self, request) 55 | 56 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 57 | -------------------------------------------------------------------------------- /doc/socketpad.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- The socketpad library provides a layer on top of the ratchet.socket library 3 | -- to manage buffered socket IO. This allows for more familiar usages and 4 | -- easier implementation of binary and ASCII socket protocols. These functions 5 | -- can fail, see error handling section in manual for details. 6 | module "ratchet.socketpad" 7 | 8 | --- Returns a new socketpad object, using the given socket as the underlying 9 | -- engine to send and receive buffered IO. The socket object is available in 10 | -- raw form as the "socket" field of the returned socketpad object. A table for 11 | -- storing misc information related to the socketpad is available in the "data" 12 | -- field of the returned socketpad object. 13 | -- @param socket A socket object to buffer. 14 | -- @return a new socketpad object. 15 | function new(socket) 16 | 17 | --- Adds the given data to the send-buffer, and possibly flushes it to the 18 | -- underlying socket. 19 | -- @param self the socketpad object. 20 | -- @param data a string of data to send. 21 | -- @param more flag specifying more data will be buffered, so the send-buffer 22 | -- will not be flushed until this parameter is false. 23 | function send(self, data) 24 | 25 | --- Attempts to receive a specific amount of data from the socket, receiving 26 | -- packets on the underlying socket until the criteria is met. 27 | -- @param self the socketpad object. 28 | -- @param stop if given as a number, this method will attempt to receive that 29 | -- many bytes on the socket and return exactly that many. If given 30 | -- as a string, the socket will receive until that string is seen, 31 | -- everything up-to and including that string is returned. 32 | -- @return string of data received on the socket, or nil followed by an error 33 | -- message. If the first return is not nil, the second returned value 34 | -- will be true if only part of the requested data was received before 35 | -- the connection was remotely closed. 36 | function recv(self) 37 | 38 | --- Returns all remaining data in the socket buffer, without actually checking 39 | -- for new data. This function empties the socket buffer. It is useful for 40 | -- checking for extra data that came along with previous calls to recv(). 41 | -- @param self the socketpad object. 42 | -- @return string of data cleared from the socket buffer. 43 | function recv_remaining(self) 44 | 45 | --- Peeks at the current, full contents of the socket buffer, without updating 46 | -- or changing them. 47 | -- @param self the socketpad object. 48 | -- @return string of data currently in the socket buffer. 49 | function peek(self) 50 | 51 | --- Calls for one single update of the socket buffer, and then returns the 52 | -- full contents of the buffer. It is useful when you know new data is waiting 53 | -- on the socket but are not ready to do a full recv() on a criteria. 54 | -- @param self the socketpad object. 55 | -- @return string of data in the socket buffer after a single update. 56 | function update_and_peek(self) 57 | 58 | --- Calls close() on the underlying socket, provided purely as convenience. 59 | -- @param self the socket object. 60 | function close(self) 61 | 62 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 63 | -------------------------------------------------------------------------------- /doc/ssl.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- The ssl library provides an SSL encryption context. This context can be used 3 | -- by sockets other other encrypted sessions, so that certain initialization 4 | -- only needs to happen once. 5 | module "ratchet.ssl" 6 | 7 | --- Creates a new SSL encryption context. 8 | -- @param method optional SSL method light userdata, defaults to SSLv3. 9 | -- @return a new ssl context object. 10 | function new(method) 11 | 12 | --- Creates a new SSL session object using the context. The session is 13 | -- initialized using BIO objects to abstract the communication layer. 14 | -- @param self the ssl context object. 15 | -- @param engine the communication engine object. 16 | -- @param rbio the abstract BIO object for reading. 17 | -- @param wbio the abstract BIO object for writing, defaults to rbio. 18 | -- @return a new ssl session object. 19 | function create_session(self, engine, rbio, wbio) 20 | 21 | --- Sets a new verification mode for the peer. If mode is "none", the peer is 22 | -- not asked to provide a certificate. If mode is "peer", the initial and 23 | -- default value, the peer should provide a certificate if one is available. A 24 | -- mode of "once" is like "peer" except the peer is only asked on the first 25 | -- handshake. A mode of "fail" will cause a handshake error if the peer does 26 | -- not provide a certificate. 27 | -- @param self the ssl context object. 28 | -- @param mode the new verify mode. 29 | function set_verify_mode(self, mode) 30 | 31 | --- Loads a certificate chain and private key, optionally using a password 32 | -- to decrypt the private key file. 33 | -- @param self the ssl context object. 34 | -- @param certchainfile file containing the certificate chain. 35 | -- @param privkeyfile file containing the, possibly encrypted, private key. 36 | -- @param password optional password to decrypt private key. 37 | function load_certs(self, certchainfile, privkeyfile, password) 38 | 39 | --- Loads the certificate authorities used to verify remote certificates. 40 | -- Either ca_path or ca_file must be given. 41 | -- @param self the ssl context object. 42 | -- @param ca_path path to a directory containing trusted CA files. 43 | -- @param ca_file path to a file containing trusted CA info. 44 | -- @param depth optional maximum depth for certificate chain, default 1. 45 | function load_cas(self, ca_path, ca_file, depth) 46 | 47 | --- Loads randomness, from a dependably random source. Typically this is 48 | -- "/dev/urandom". The default action, if randomfile is not given, is to 49 | -- check the RANDFILE environment variable or a ~/.rnd file if one exists. 50 | -- @param self the ssl context object. 51 | -- @param randomfile path to a file with random bytes. 52 | -- @param max_bytes maximum bytes to read from file, default 1 Mb. 53 | function load_randomness(self, randomfile, max_bytes) 54 | 55 | --- Loads DH parameters from a file. 56 | -- @param self the ssl context object. 57 | -- @param file path to a PEM file with DH parameters. 58 | function load_dh_params(self, file) 59 | 60 | --- Generates an ephemeral RSA key. 61 | -- @param self the ssl context object. 62 | -- @param bits optional bit-size to generate for, default 512. 63 | -- @param e exponent for generation, default RSA_F4. 64 | function generate_tmp_rsa(self, bits, e) 65 | 66 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 67 | -------------------------------------------------------------------------------- /doc/exec.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- The exec library provides relatively powerful command execution in an 3 | -- event-based manner. Commands run with these functions will be run in a 4 | -- separate OS process using a fork-exec. 5 | module "ratchet.exec" 6 | 7 | --- Creates a new execution object, running the command stored in table 8 | -- argv. The object returned by this command is not running until you 9 | -- call its start() method. 10 | -- @param argv Table array of command arguments, starting with the command 11 | -- itself. 12 | -- @return a new exec object. 13 | function new(argv) 14 | 15 | --- Starts the command process. After writing optional data, stdin is closed. 16 | -- Reads data from stdout and stderr until both are closed. Finally, calls 17 | -- wait() to finish the command process. Functions similarly to the 18 | -- Popen.communicate() method of the subprocess module in Python. 19 | -- @param self the exec object. 20 | -- @param data optional data to write to stdin before closing it. 21 | -- @return The concatenations of stdout and stderr, respectively, followed by 22 | -- the exit status. 23 | function communicate(self, data) 24 | 25 | --- Returns the table array of arguments that the exec object was created with. 26 | -- @param self the exec object. 27 | -- @return an argv table array. 28 | function get_argv(self) 29 | 30 | --- Starts the command process. No other methods may be called before this one. 31 | -- @param self the exec object. 32 | -- @return the operating system PID of the new process. 33 | function start(self) 34 | 35 | --- Returns the UNIX timestamp when the process command process was started, 36 | -- that is, when the start() method was called. 37 | -- @param self the exec object. 38 | -- @return timestamp when the process was started, or nil if unstarted. 39 | function get_start_time(self) 40 | 41 | --- Returns the standard input file, which has its own methods close() and 42 | -- write(). Data written with write() will show up in the command process's 43 | -- standard input. 44 | -- @param self the exec object. 45 | -- @return the process's standard input file object. 46 | function stdin(self) 47 | 48 | --- Returns the standard output file, which has its own methods close() and 49 | -- read(). Data written to the command process's standard output will be 50 | -- returned by this object's read() method. 51 | -- @param self the exec object. 52 | -- @return the process's standard output file object. 53 | function stdout(self) 54 | 55 | --- Returns the standard error file, which has its own methods close() and 56 | -- read(). Data written to the command process's standard error will be 57 | -- returned by this object's read() method. 58 | -- @param self the exec object. 59 | -- @return the process's standard error file object. 60 | function stderr(self) 61 | 62 | --- Waits for the command process to terminate. 63 | -- @param self the exec object. 64 | -- @param timeout number of seconds to wait (fractions are ok), default forever. 65 | -- @return The exit status integer. 66 | function wait(self, timeout) 67 | 68 | --- Sends a signal to the command process. 69 | -- @param self the exec object. 70 | -- @param signal The signal number of name (see signal(7)), defaulting to 71 | -- "SIGTERM". 72 | function kill(self, signal) 73 | 74 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 75 | -------------------------------------------------------------------------------- /test/test_http_get.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | require "ratchet.http.client" 3 | require "ratchet.http.server" 4 | 5 | debugging = false 6 | 7 | -- {{{ debug_print() 8 | local function debug_print(t, data) 9 | if t == "send" or t == "recv" then 10 | print(t .. ": [" .. data .. "]") 11 | else 12 | print(t) 13 | end 14 | end 15 | -- }}} 16 | 17 | -- {{{ server_handle_command() 18 | function server_handle_command(self, uri, headers, data, from) 19 | assert(uri == "/test/uri") 20 | assert(data == "command data") 21 | local header_values = headers["x-command-header"] 22 | assert(#header_values == 2) 23 | assert(header_values[1] == "command header value 1") 24 | assert(header_values[2] == "command header value 2") 25 | 26 | server_received_command = true 27 | return { 28 | code = 250, 29 | message = "Testing", 30 | headers = { 31 | ["X-Testing"] = {"check"}, 32 | }, 33 | data = "data!", 34 | } 35 | end 36 | -- }}} 37 | 38 | -- {{{ client_handle_response() 39 | function client_handle_response(code, reason, headers, data) 40 | assert(code == 250) 41 | assert(reason == "Testing") 42 | assert(data == "data!") 43 | local header_values = headers["x-testing"] 44 | assert(#header_values == 1) 45 | assert(header_values[1] == "check") 46 | local missing_header_values = headers['x-missing'] 47 | assert(type(missing_header_values) == "table") 48 | assert(#missing_header_values == 0) 49 | 50 | client_received_response = true 51 | end 52 | -- }}} 53 | 54 | -- {{{ server_ctx() 55 | function server_ctx(host, port) 56 | local rec = ratchet.socket.prepare_tcp(host, port, "AF_INET") 57 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 58 | if debugging then socket:set_tracer(debug_print) end 59 | socket:setsockopt("SO_REUSEADDR", true) 60 | socket:bind(rec.addr) 61 | socket:listen() 62 | 63 | ratchet.thread.attach(client_ctx, host, port) 64 | 65 | local client, from = socket:accept() 66 | if debugging then client:set_tracer(debug_print) end 67 | 68 | local server_handlers = { 69 | GET = server_handle_command, 70 | } 71 | local handler = ratchet.http.server.new(client, from, server_handlers) 72 | handler:handle() 73 | end 74 | -- }}} 75 | 76 | -- {{{ client_ctx() 77 | function client_ctx(host, port) 78 | local rec = ratchet.socket.prepare_tcp(host, port, "AF_INET") 79 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 80 | if debugging then socket:set_tracer(debug_print) end 81 | socket:connect(rec.addr) 82 | 83 | local handler = ratchet.http.client.new(socket) 84 | local headers = { 85 | ["X-Command-Header"] = { 86 | "command header value 1", 87 | "command header value 2", 88 | }, 89 | ["Content-Length"] = {12}, 90 | } 91 | client_handle_response(handler:query("GET", "/test/uri", headers, "command data")) 92 | end 93 | -- }}} 94 | 95 | kernel = ratchet.new(function () 96 | ratchet.thread.attach(server_ctx, "localhost", 10080) 97 | end) 98 | kernel:loop() 99 | 100 | assert(server_received_command) 101 | assert(client_received_response) 102 | 103 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 104 | -------------------------------------------------------------------------------- /src/lua/smtp/data_reader.lua: -------------------------------------------------------------------------------- 1 | 2 | local data_reader = {} 3 | data_reader.__index = data_reader 4 | 5 | -- {{{ data_reader.new() 6 | function data_reader.new(io, max_size) 7 | local self = {} 8 | setmetatable(self, data_reader) 9 | 10 | self.io = io 11 | self.size = 0 12 | self.max_size = max_size 13 | 14 | self.lines = {""} 15 | self.i = 1 16 | 17 | return self 18 | end 19 | -- }}} 20 | 21 | -- {{{ data_reader:from_recv_buffer() 22 | function data_reader:from_recv_buffer() 23 | self:add_lines(self.io.recv_buffer) 24 | self.io.recv_buffer = "" 25 | end 26 | -- }}} 27 | 28 | -- {{{ data_reader:strip_EOD_endline() 29 | function data_reader:strip_EOD_endline() 30 | if self.EOD > 1 then 31 | local last_line = self.lines[self.EOD-1] 32 | self.lines[self.EOD-1] = last_line:match("(.-)%\r?%\n") or last_line 33 | end 34 | end 35 | -- }}} 36 | 37 | -- {{{ data_reader:handle_finished_line() 38 | function data_reader:handle_finished_line() 39 | local i = self.i 40 | local line = self.lines[i] 41 | 42 | -- Move internal trackers ahead. 43 | self.i = self.i + 1 44 | self.lines[self.i] = "" 45 | 46 | -- Only handle lines within the data. 47 | if not self.EOD then 48 | -- Check for the End-Of-Data marker. 49 | if line:match("^%.%\r?%\n") then 50 | self.EOD = i 51 | self:strip_EOD_endline() 52 | return 53 | 54 | -- Remove an initial period on non-EOD lines as per RFC 821 4.5.2. 55 | elseif line:sub(1, 1) == "." then 56 | line = line:sub(2) 57 | self.lines[i] = line 58 | end 59 | end 60 | end 61 | -- }}} 62 | 63 | -- {{{ data_reader:add_lines() 64 | function data_reader:add_lines(piece) 65 | for line in piece:gmatch("(.-%\n)") do 66 | self.lines[self.i] = self.lines[self.i] .. line 67 | self:handle_finished_line() 68 | end 69 | self.lines[self.i] = self.lines[self.i] .. piece:match("([^%\n]*)$") 70 | end 71 | -- }}} 72 | 73 | -- {{{ data_reader:recv_piece() 74 | function data_reader:recv_piece() 75 | if self.EOD then 76 | return false 77 | end 78 | 79 | local piece = self.io.socket:recv() 80 | if piece == "" then 81 | local err = ratchet.error.new("Connection closed.", "ECONNCLOSED", "ratchet.smtp.data.recv()") 82 | error(err) 83 | end 84 | 85 | self.size = self.size + #piece 86 | if self.max_size and self.size > self.max_size then 87 | self.error = ratchet.error.new("Message breached size limit.", "MSGTOOBIG", "ratchet.smtp.data.recv()") 88 | self.EOD = self.i 89 | return false 90 | end 91 | 92 | self:add_lines(piece) 93 | return not self.EOD 94 | end 95 | -- }}} 96 | 97 | -- {{{ data_reader:return_all() 98 | function data_reader:return_all() 99 | if self.error then 100 | return nil, self.error 101 | end 102 | 103 | -- Save extra lines back on the recv_buffer. 104 | self.io.recv_buffer = table.concat(self.lines, "", self.EOD+1) 105 | 106 | -- Return the data as one big string. 107 | return table.concat(self.lines, "", 1, self.EOD-1) 108 | end 109 | -- }}} 110 | 111 | -- {{{ data_reader:recv() 112 | function data_reader:recv() 113 | self:from_recv_buffer() 114 | 115 | while self:recv_piece() do end 116 | 117 | return self:return_all() 118 | end 119 | -- }}} 120 | 121 | return data_reader 122 | 123 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 124 | -------------------------------------------------------------------------------- /src/lua/bus/server.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | require "ratchet.socketpad" 4 | local server_transaction = require "ratchet.bus.server_transaction" 5 | 6 | local server = {} 7 | server.__index = server 8 | 9 | -- {{{ server.new() 10 | function server.new(socket, request_from_bus, response_to_bus) 11 | local self = {} 12 | setmetatable(self, server) 13 | 14 | self.request_from_bus = request_from_bus or tostring 15 | self.response_to_bus = response_to_bus or tostring 16 | self.queue = {} 17 | 18 | self.sockets = {server = socket} 19 | 20 | return self 21 | end 22 | -- }}} 23 | 24 | -- {{{ build_socket_array() 25 | local function build_socket_array(sockets) 26 | local ret = {} 27 | for k, v in pairs(sockets) do 28 | if k == 'server' then 29 | table.insert(ret, v) 30 | else 31 | table.insert(ret, k) 32 | end 33 | end 34 | return ret 35 | end 36 | -- }}} 37 | 38 | -- {{{ receive_connections_and_data() 39 | local function receive_connections_and_data(self) 40 | local ready, err = ratchet.thread.block_on(build_socket_array(self.sockets)) 41 | if not ready then 42 | return nil, err 43 | end 44 | 45 | if ready == self.sockets.server then 46 | local pad = ratchet.socketpad.new(ready:accept()) 47 | self.sockets[pad.socket] = pad 48 | else 49 | local pad = self.sockets[ready] 50 | local _, closed = pad:update_and_peek() 51 | if not closed then 52 | self.updated = pad 53 | end 54 | end 55 | 56 | return true 57 | end 58 | -- }}} 59 | 60 | -- {{{ pop_num_parts() 61 | local function pop_num_parts(pad) 62 | local peek = pad:peek() 63 | if #peek >= 2 then 64 | pad.data.num_parts = ratchet.socket.ntoh16(pad:recv(2)) 65 | pad.data.parts = {} 66 | pad.data.part_lens = {} 67 | 68 | return true 69 | end 70 | end 71 | -- }}} 72 | 73 | -- {{{ pop_part_len() 74 | local function pop_part_len(pad, i) 75 | local peek = pad:peek() 76 | if #peek >= 4 then 77 | pad.data.part_lens[i] = ratchet.socket.ntoh(pad:recv(4)) 78 | return true 79 | end 80 | end 81 | -- }}} 82 | 83 | -- {{{ pop_part_data() 84 | local function pop_part_data(pad, i) 85 | local peek = pad:peek() 86 | local len = pad.data.part_lens[i] 87 | if #peek >= len then 88 | pad.data.parts[i] = pad:recv(len) 89 | return true 90 | end 91 | end 92 | -- }}} 93 | 94 | -- {{{ pop_full_request() 95 | local function pop_full_request(self, pad) 96 | if not pad.data.num_parts and not pop_num_parts(pad) then 97 | return 98 | end 99 | 100 | if pad.data.num_parts == 0 then 101 | return self.request_from_bus('', pad.data.parts, pad.from) 102 | end 103 | 104 | for i = 1, pad.data.num_parts do 105 | if not pad.data.part_lens[i] and not pop_part_len(pad, i) then 106 | return 107 | end 108 | 109 | if not pad.data.parts[i] and not pop_part_data(pad, i) then 110 | return 111 | end 112 | end 113 | 114 | return self.request_from_bus(table.remove(pad.data.parts, 1), pad.data.parts, pad.from) 115 | end 116 | -- }}} 117 | 118 | -- {{{ check_for_full_requests() 119 | local function check_for_full_requests(self) 120 | if self.updated then 121 | local request = pop_full_request(self, self.updated) 122 | 123 | if request then 124 | self.sockets[self.updated.socket] = nil 125 | 126 | local transaction = server_transaction.new( 127 | request, 128 | self.response_to_bus, 129 | self.updated, 130 | self.updated.from 131 | ) 132 | return transaction, request 133 | end 134 | end 135 | end 136 | -- }}} 137 | 138 | -- {{{ server:recv_request() 139 | function server:recv_request() 140 | local transaction, request 141 | 142 | repeat 143 | local okay, err = receive_connections_and_data(self) 144 | if not okay then 145 | return nil, err 146 | end 147 | 148 | transaction, request = check_for_full_requests(self) 149 | until transaction 150 | 151 | return transaction, request 152 | end 153 | -- }}} 154 | 155 | return server 156 | 157 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 158 | -------------------------------------------------------------------------------- /test/test_smtp.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | require "ratchet.smtp.client" 3 | require "ratchet.smtp.server" 4 | 5 | debugging = false 6 | 7 | -- {{{ debug_print() 8 | local function debug_print(t, data) 9 | if t == "send" or t == "recv" then 10 | print(t .. ": [" .. data .. "]") 11 | else 12 | print(t) 13 | end 14 | end 15 | -- }}} 16 | 17 | -- {{{ server_handlers 18 | 19 | server_handlers = {} 20 | 21 | function server_handlers:BANNER(reply) 22 | reply.code = "220" 23 | reply.message = "test banner" 24 | end 25 | 26 | function server_handlers:EHLO(reply, ehlo_as) 27 | reply.code = "250" 28 | reply.message = "hello " .. ehlo_as 29 | end 30 | 31 | function server_handlers:MAIL(reply, address) 32 | reply.code = "250" 33 | reply.message = "test sender okay: " .. address 34 | end 35 | 36 | function server_handlers:RCPT(reply, address) 37 | reply.code = "250" 38 | reply.message = "test recipient okay: " .. address 39 | end 40 | 41 | function server_handlers:DATA(reply) 42 | reply.code = "354" 43 | reply.message = "ready for test data" 44 | end 45 | 46 | function server_handlers:HAVE_DATA(reply, data) 47 | reply.code = "250" 48 | reply.message = "test data received" 49 | end 50 | 51 | function server_handlers:SLIMTA(arg) 52 | local reply = { 53 | code = "123", 54 | message = "custom command okay", 55 | } 56 | 57 | server_received_message = true 58 | return reply 59 | end 60 | 61 | function server_handlers:QUIT(reply) 62 | reply.code = "221" 63 | reply.message = "test connection quit" 64 | end 65 | 66 | -- }}} 67 | 68 | -- {{{ client_handler() 69 | function client_handler(client) 70 | local banner = client:get_banner() 71 | assert(banner.code == "220" and banner.message == "test banner") 72 | 73 | local ehlo = client:ehlo("test") 74 | assert(ehlo.code == "250") 75 | 76 | local mailfrom = client:mailfrom("sender@slimta.org") 77 | local rcptto1 = client:rcptto("rcpt1@slimta.org") 78 | local rcptto2 = client:rcptto("rcpt2@slimta.org") 79 | local data = client:data() 80 | 81 | assert(mailfrom.code == "250" and mailfrom.message == "2.1.0 test sender okay: sender@slimta.org") 82 | assert(rcptto1.code == "250" and rcptto1.message == "2.1.5 test recipient okay: rcpt1@slimta.org") 83 | assert(rcptto2.code == "250" and rcptto2.message == "2.1.5 test recipient okay: rcpt2@slimta.org") 84 | assert(data.code == "354" and data.message == "ready for test data") 85 | 86 | local send_data = client:send_data("arbitrary test message data") 87 | assert(send_data.code == "250" and send_data.message == "2.6.0 test data received") 88 | 89 | local custom = client:custom_command("SLIMTA", "test") 90 | assert(custom.code == "123" and custom.message == "custom command okay") 91 | 92 | local quit = client:quit() 93 | assert(quit.code == "221" and quit.message == "2.0.0 test connection quit") 94 | 95 | client_sent_message = true 96 | end 97 | -- }}} 98 | 99 | -- {{{ server_ctx() 100 | function server_ctx(host, port) 101 | local rec = ratchet.socket.prepare_tcp(host, port, "AF_INET") 102 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 103 | if debugging then socket:set_tracer(debug_print) end 104 | socket:setsockopt("SO_REUSEADDR", true) 105 | socket:bind(rec.addr) 106 | socket:listen() 107 | 108 | ratchet.thread.attach(client_ctx, host, port) 109 | 110 | local client, from = socket:accept() 111 | if debugging then client:set_tracer(debug_print) end 112 | 113 | local handler = ratchet.smtp.server.new(client, server_handlers) 114 | handler:handle() 115 | end 116 | -- }}} 117 | 118 | -- {{{ client_ctx() 119 | function client_ctx(host, port) 120 | local rec = ratchet.socket.prepare_tcp(host, port, "AF_INET") 121 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 122 | if debugging then socket:set_tracer(debug_print) end 123 | socket:connect(rec.addr) 124 | 125 | local handler = ratchet.smtp.client.new(socket) 126 | client_handler(handler) 127 | end 128 | -- }}} 129 | 130 | kernel = ratchet.new(function () 131 | ratchet.thread.attach(server_ctx, "localhost", 10025) 132 | end) 133 | kernel:loop() 134 | 135 | assert(server_received_message) 136 | assert(client_sent_message) 137 | 138 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 139 | -------------------------------------------------------------------------------- /src/c/dns/hosts.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Ian C. Good 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | 22 | #include "config.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "ratchet.h" 35 | #include "misc.h" 36 | #include "libdns/dns.h" 37 | 38 | /* {{{ load_hosts() */ 39 | static struct dns_hosts *load_hosts (lua_State *L, int index) 40 | { 41 | struct dns_hosts *hosts; 42 | const char *path; 43 | int error = 0, i; 44 | 45 | /* Check for nil or none. */ 46 | if (lua_isnoneornil (L, index)) 47 | { 48 | hosts = dns_hosts_local (&error); 49 | if (!hosts) 50 | { 51 | lua_pushfstring (L, "/etc/hosts: %s", dns_strerror (error)); 52 | return NULL; 53 | } 54 | return hosts; 55 | } 56 | else 57 | luaL_checktype (L, index, LUA_TTABLE); 58 | 59 | /* Create new hosts object. */ 60 | hosts = dns_hosts_open (&error); 61 | if (!hosts) 62 | { 63 | lua_pushfstring (L, "dns_hosts_open: %s", dns_strerror (error)); 64 | return NULL; 65 | } 66 | 67 | /* Load given /etc/hosts alternative(s). */ 68 | for (i=1; ; i++) 69 | { 70 | lua_rawgeti (L, index, i); 71 | if (lua_isnil (L, -1)) 72 | break; 73 | 74 | path = lua_tostring (L, -1); 75 | error = dns_hosts_loadpath (hosts, path); 76 | if (error) 77 | { 78 | lua_pushfstring (L, "%s: %s", path, dns_strerror (error)); 79 | return NULL; 80 | } 81 | 82 | lua_pop (L, 1); 83 | } 84 | lua_pop (L, 1); 85 | 86 | return hosts; 87 | } 88 | /* }}} */ 89 | 90 | /* {{{ myhosts_new() */ 91 | static int myhosts_new (lua_State *L) 92 | { 93 | lua_settop (L, 1); 94 | 95 | struct dns_hosts **new = (struct dns_hosts **) lua_newuserdata (L, sizeof (struct dns_hosts *)); 96 | *new = load_hosts (L, 1); 97 | if (!*new) 98 | return ratchet_error_top (L, "ratchet.dns.hosts.new()", NULL); 99 | 100 | luaL_getmetatable (L, "ratchet_dns_hosts_meta"); 101 | lua_setmetatable (L, -2); 102 | 103 | return 1; 104 | } 105 | /* }}} */ 106 | 107 | /* {{{ myhosts_gc() */ 108 | static int myhosts_gc (lua_State *L) 109 | { 110 | struct dns_hosts **hosts = (struct dns_hosts **) luaL_checkudata (L, 1, "ratchet_dns_hosts_meta"); 111 | dns_hosts_close (*hosts); 112 | *hosts = NULL; 113 | 114 | return 0; 115 | } 116 | /* }}} */ 117 | 118 | /* ---- Public Functions ---------------------------------------------------- */ 119 | 120 | /* {{{ luaopen_ratchet_dns_hosts() */ 121 | int luaopen_ratchet_dns_hosts (lua_State *L) 122 | { 123 | /* Static functions in the namespace. */ 124 | const luaL_Reg funcs[] = { 125 | {"new", myhosts_new}, 126 | {NULL} 127 | }; 128 | 129 | /* Meta-methods for object metatables. */ 130 | const luaL_Reg metameths[] = { 131 | {"__gc", myhosts_gc}, 132 | {NULL} 133 | }; 134 | 135 | /* Set up the class and metatables. */ 136 | luaL_newmetatable (L, "ratchet_dns_hosts_meta"); 137 | luaL_setfuncs (L, metameths, 0); 138 | lua_pop (L, 1); 139 | 140 | /* Set up the namespace and functions. */ 141 | luaL_newlib (L, funcs); 142 | lua_getfield (L, -1, "new"); 143 | lua_call (L, 0, 1); 144 | lua_setfield (L, LUA_REGISTRYINDEX, "ratchet_dns_hosts_default"); 145 | 146 | return 1; 147 | } 148 | /* }}} */ 149 | 150 | // vim:fdm=marker:ai:ts=4:sw=4:noet: 151 | -------------------------------------------------------------------------------- /doc/thread.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- This module provides as set of functions for creating and controlling threads 3 | -- from within the context of a running ratchet. These functions do not need to 4 | -- be passed a ratchet object, they will deduce it from the current thread. 5 | module "ratchet.thread" 6 | 7 | --- Queues a new thread for execution. The thread calls func and gives it any extra 8 | -- arguments as its parameters. The thread is not started by this method, instead 9 | -- it is started on next iteration of the loop. All threads attached with 10 | -- this method must be completed before loop() will finish. 11 | -- @param func the function to run in the thread. 12 | -- @param ... extra parameters to function. 13 | -- @return a Lua thread object. 14 | function attach(func, ...) 15 | 16 | --- Queues a new thread for execution. The thread calls func and gives it any extra 17 | -- arguments as its parameters. The thread is not started by this method, instead 18 | -- it is started on next iteration of the loop. Threads attached with this method 19 | -- as opposed to attach() will be destroyed when loop() finishes, whether 20 | -- completed or not. 21 | -- @param func the function to run in the thread. 22 | -- @param ... extra parameters to function. 23 | -- @return a Lua thread object. 24 | function attach_background(func, ...) 25 | 26 | --- Pauses the current thread until all threads listed in the given table have 27 | -- completed their execution either normally or by error. 28 | -- @param threads table containing threads to wait for. 29 | function wait_all(threads) 30 | 31 | --- Returns a table that is specific to the currently-running thread that can be 32 | -- used as scratch-space for thread-scope data. This table is not created until 33 | -- the first time this method is called for in thread. 34 | -- @param premade optionally pass in a pre-existing object to be stored as the 35 | -- thread's scratch space instead of the default a new table. 36 | -- @return a table specific to the current thread. 37 | function space() 38 | 39 | --- Returns the Lua thread object that is currently running, or nil for the main 40 | -- thread. 41 | -- @return thread object or nil. 42 | function self() 43 | 44 | --- Pauses the current thread for the given period. 45 | -- @param seconds resume thread after this many seconds elapse. 46 | function timer(seconds) 47 | 48 | --- Adds a background timer to the current thread. If the thread outruns the 49 | -- timer, the callback is run and a ratchet error of type "ALARM" is thrown. 50 | -- @param seconds trigger the alarm after this many seconds elapse. 51 | -- @param callback optional callback function to run before throwing error. 52 | function alarm(seconds, callback) 53 | 54 | --- Pauses the current thread indefinitely. The thread must be resumed by a call 55 | -- to unpause() from another thread. 56 | -- @return any extra arguments passed to unpause() from other thread. 57 | function pause() 58 | 59 | --- Unpauses the given thread. The paused thread will resume on next iteration 60 | -- of the main loop. Extra parameters to this function will be given to the 61 | -- paused thread as return values from pause(). 62 | -- @param thread the thread to unpause. 63 | -- @param ... extra parameters will be returned by pause(). 64 | function unpause(thread, ...) 65 | 66 | --- Blocks on multiple items, with a single timeout. This function will block 67 | -- until the first event on any one of the items. Items given in the read and 68 | -- write argument tables must have a get_fd() method that returns its file 69 | -- descriptor. 70 | -- @param reads Table of items to wait for read events on. 71 | -- @param writes Table of items to wait for write events on. 72 | -- @param timeout Returns nil if this many seconds elapse with no events. 73 | -- @return The item from the read or write table that triggered an event. 74 | function block_on(reads, writes, timeout) 75 | 76 | --- Blocks the current thread until the current process receives the given 77 | -- signal. 78 | -- @param signal A signal by number or name (see signal(7)). 79 | function sigwait(signal) 80 | 81 | --- Kills a given thread. If the thread is yielding on any events, those events 82 | -- are cleaned up and the thread is dropped. 83 | -- @param thread The thread to kill. 84 | function kill(thread) 85 | 86 | --- Kills all threads in the table sequence. Essentially this function is the 87 | -- same as kill() but run in a loop over a table. 88 | -- @param threads table array of threads to kill. 89 | function kill_all(threads) 90 | 91 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 92 | -------------------------------------------------------------------------------- /src/c/dns/resolv_conf.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Ian C. Good 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | 22 | #include "config.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "ratchet.h" 35 | #include "misc.h" 36 | #include "libdns/dns.h" 37 | 38 | /* {{{ load_resolv_conf() */ 39 | static struct dns_resolv_conf *load_resolv_conf (lua_State *L, int index) 40 | { 41 | struct dns_resolv_conf *resconf; 42 | const char *path; 43 | int error = 0, i; 44 | 45 | /* Check for nil or none. */ 46 | if (lua_isnoneornil (L, index)) 47 | { 48 | lua_createtable (L, 1, 0); 49 | lua_pushliteral (L, "/etc/resolv.conf"); 50 | lua_rawseti (L, -2, 1); 51 | lua_replace (L, index); 52 | } 53 | else 54 | luaL_checktype (L, index, LUA_TTABLE); 55 | 56 | /* Create new resconf object. */ 57 | resconf = dns_resconf_open (&error); 58 | if (!resconf) 59 | { 60 | lua_pushfstring (L, "dns_resconf_open: %s", dns_strerror (error)); 61 | return NULL; 62 | } 63 | 64 | /* Load /etc/resolv.conf or given alternative(s). */ 65 | for (i=1; ; i++) 66 | { 67 | lua_rawgeti (L, index, i); 68 | if (lua_isnil (L, -1)) 69 | break; 70 | 71 | path = lua_tostring (L, -1); 72 | error = dns_resconf_loadpath (resconf, path); 73 | if (error) 74 | { 75 | lua_pushfstring (L, "%s: %s", path, dns_strerror (error)); 76 | return NULL; 77 | } 78 | 79 | lua_pop (L, 1); 80 | } 81 | lua_pop (L, 1); 82 | 83 | return resconf; 84 | } 85 | /* }}} */ 86 | 87 | /* {{{ myresconf_new() */ 88 | static int myresconf_new (lua_State *L) 89 | { 90 | lua_settop (L, 1); 91 | 92 | struct dns_resolv_conf **new = (struct dns_resolv_conf **) lua_newuserdata (L, sizeof (struct dns_resolv_conf *)); 93 | *new = load_resolv_conf (L, 1); 94 | if (!*new) 95 | return ratchet_error_top (L, "ratchet.dns.resolv_conf.new()", NULL); 96 | 97 | luaL_getmetatable (L, "ratchet_dns_resolv_conf_meta"); 98 | lua_setmetatable (L, -2); 99 | 100 | return 1; 101 | } 102 | /* }}} */ 103 | 104 | /* {{{ myresconf_gc() */ 105 | static int myresconf_gc (lua_State *L) 106 | { 107 | struct dns_resolv_conf **resconf = (struct dns_resolv_conf **) luaL_checkudata (L, 1, "ratchet_dns_resolv_conf_meta"); 108 | dns_resconf_close (*resconf); 109 | *resconf = NULL; 110 | 111 | return 0; 112 | } 113 | /* }}} */ 114 | 115 | /* ---- Public Functions ---------------------------------------------------- */ 116 | 117 | /* {{{ luaopen_ratchet_dns_resolv_conf() */ 118 | int luaopen_ratchet_dns_resolv_conf (lua_State *L) 119 | { 120 | /* Static functions in the namespace. */ 121 | const luaL_Reg funcs[] = { 122 | {"new", myresconf_new}, 123 | {NULL} 124 | }; 125 | 126 | /* Meta-methods for object metatables. */ 127 | const luaL_Reg metameths[] = { 128 | {"__gc", myresconf_gc}, 129 | {NULL} 130 | }; 131 | 132 | /* Set up the class and metatables. */ 133 | luaL_newmetatable (L, "ratchet_dns_resolv_conf_meta"); 134 | luaL_setfuncs (L, metameths, 0); 135 | lua_pop (L, 1); 136 | 137 | /* Set up the namespace and functions. */ 138 | luaL_newlib (L, funcs); 139 | lua_getfield (L, -1, "new"); 140 | lua_call (L, 0, 1); 141 | lua_setfield (L, LUA_REGISTRYINDEX, "ratchet_dns_resolv_conf_default"); 142 | 143 | return 1; 144 | } 145 | /* }}} */ 146 | 147 | // vim:fdm=marker:ai:ts=4:sw=4:noet: 148 | -------------------------------------------------------------------------------- /test/test_smtp_bigmessage.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | require "ratchet.smtp.client" 3 | require "ratchet.smtp.server" 4 | 5 | debugging = false 6 | 7 | -- {{{ debug_print() 8 | local function debug_print(t, data) 9 | if t == "send" or t == "recv" then 10 | print(t .. ": [" .. data .. "]") 11 | else 12 | print(t) 13 | end 14 | end 15 | -- }}} 16 | 17 | -- {{{ server_handlers 18 | 19 | server_handlers = {} 20 | 21 | function server_handlers:BANNER(reply) 22 | reply.code = "220" 23 | reply.message = "test banner" 24 | end 25 | 26 | function server_handlers:EHLO(reply, ehlo_as) 27 | reply.code = "250" 28 | reply.message = "hello " .. ehlo_as 29 | end 30 | 31 | function server_handlers:MAIL(reply, address) 32 | reply.code = "250" 33 | reply.message = "test sender okay: " .. address 34 | end 35 | 36 | function server_handlers:RCPT(reply, address) 37 | reply.code = "250" 38 | reply.message = "test recipient okay: " .. address 39 | end 40 | 41 | function server_handlers:DATA(reply) 42 | reply.code = "354" 43 | reply.message = "ready for test data" 44 | end 45 | 46 | function server_handlers:HAVE_DATA(reply, data, err) 47 | if data then 48 | reply.code = "250" 49 | reply.message = "test data received" 50 | else 51 | if ratchet.error.is(err, "MSGTOOBIG") then 52 | reply.code = "552" 53 | reply.message = "message too big" 54 | reply.enhanced_status_code = "5.3.4" 55 | 56 | server_received_message = true 57 | else 58 | error(err) 59 | end 60 | end 61 | end 62 | 63 | function server_handlers:SLIMTA(reply, arg) 64 | reply.code = "123" 65 | reply.message = "custom command okay" 66 | end 67 | 68 | function server_handlers:QUIT(reply) 69 | reply.code = "221" 70 | reply.message = "test connection quit" 71 | end 72 | 73 | -- }}} 74 | 75 | -- {{{ client_handler() 76 | function client_handler(client) 77 | local banner = client:get_banner() 78 | assert(banner.code == "220" and banner.message == "test banner") 79 | 80 | local ehlo = client:ehlo("test") 81 | assert(ehlo.code == "250") 82 | 83 | local mailfrom = client:mailfrom("sender@slimta.org") 84 | local rcptto1 = client:rcptto("rcpt1@slimta.org") 85 | local rcptto2 = client:rcptto("rcpt2@slimta.org") 86 | local data = client:data() 87 | 88 | assert(mailfrom.code == "250" and mailfrom.message == "2.1.0 test sender okay: sender@slimta.org") 89 | assert(rcptto1.code == "250" and rcptto1.message == "2.1.5 test recipient okay: rcpt1@slimta.org") 90 | assert(rcptto2.code == "250" and rcptto2.message == "2.1.5 test recipient okay: rcpt2@slimta.org") 91 | assert(data.code == "354" and data.message == "ready for test data") 92 | 93 | local send_data = client:send_data(string.rep("a", 26)) 94 | assert(send_data.code == "552" and send_data.message == "5.3.4 message too big") 95 | 96 | local quit = client:quit() 97 | assert(quit.code == "221" and quit.message == "2.0.0 test connection quit") 98 | 99 | client_sent_message = true 100 | end 101 | -- }}} 102 | 103 | -- {{{ server_ctx() 104 | function server_ctx(host, port) 105 | local rec = ratchet.socket.prepare_tcp(host, port, "AF_INET") 106 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 107 | if debugging then socket:set_tracer(debug_print) end 108 | socket:setsockopt("SO_REUSEADDR", true) 109 | socket:bind(rec.addr) 110 | socket:listen() 111 | 112 | ratchet.thread.attach(client_ctx, host, port) 113 | 114 | local client, from = socket:accept() 115 | if debugging then client:set_tracer(debug_print) end 116 | 117 | local handler = ratchet.smtp.server.new(client, server_handlers) 118 | handler.extensions:add("SIZE", "25") 119 | handler:handle() 120 | end 121 | -- }}} 122 | 123 | -- {{{ client_ctx() 124 | function client_ctx(host, port) 125 | local rec = ratchet.socket.prepare_tcp(host, port, "AF_INET") 126 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 127 | if debugging then socket:set_tracer(debug_print) end 128 | socket:connect(rec.addr) 129 | 130 | local handler = ratchet.smtp.client.new(socket) 131 | client_handler(handler) 132 | end 133 | -- }}} 134 | 135 | kernel = ratchet.new(function () 136 | ratchet.thread.attach(server_ctx, "localhost", 10025) 137 | end) 138 | kernel:loop() 139 | 140 | assert(server_received_message) 141 | assert(client_sent_message) 142 | 143 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 144 | -------------------------------------------------------------------------------- /m4/ax_check_openssl.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_check_openssl.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CHECK_OPENSSL([action-if-found[, action-if-not-found]]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Look for OpenSSL in a number of default spots, or in a user-selected 12 | # spot (via --with-openssl). Sets 13 | # 14 | # OPENSSL_INCLUDES to the include directives required 15 | # OPENSSL_LIBS to the -l directives required 16 | # OPENSSL_LDFLAGS to the -L or -R flags required 17 | # 18 | # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately 19 | # 20 | # This macro sets OPENSSL_INCLUDES such that source files should use the 21 | # openssl/ directory in include directives: 22 | # 23 | # #include 24 | # 25 | # LICENSE 26 | # 27 | # Copyright (c) 2009,2010 Zmanda Inc. 28 | # Copyright (c) 2009,2010 Dustin J. Mitchell 29 | # 30 | # Copying and distribution of this file, with or without modification, are 31 | # permitted in any medium without royalty provided the copyright notice 32 | # and this notice are preserved. This file is offered as-is, without any 33 | # warranty. 34 | 35 | #serial 8 36 | 37 | AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL]) 38 | AC_DEFUN([AX_CHECK_OPENSSL], [ 39 | found=false 40 | AC_ARG_WITH([openssl], 41 | [AS_HELP_STRING([--with-openssl=DIR], 42 | [root of the OpenSSL directory])], 43 | [ 44 | case "$withval" in 45 | "" | y | ye | yes | n | no) 46 | AC_MSG_ERROR([Invalid --with-openssl value]) 47 | ;; 48 | *) ssldirs="$withval" 49 | ;; 50 | esac 51 | ], [ 52 | # if pkg-config is installed and openssl has installed a .pc file, 53 | # then use that information and don't search ssldirs 54 | AC_PATH_PROG([PKG_CONFIG], [pkg-config]) 55 | if test x"$PKG_CONFIG" != x""; then 56 | OPENSSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null` 57 | if test $? = 0; then 58 | OPENSSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null` 59 | OPENSSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null` 60 | found=true 61 | fi 62 | fi 63 | 64 | # no such luck; use some default ssldirs 65 | if ! $found; then 66 | ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" 67 | fi 68 | ] 69 | ) 70 | 71 | 72 | # note that we #include , so the OpenSSL headers have to be in 73 | # an 'openssl' subdirectory 74 | 75 | if ! $found; then 76 | OPENSSL_INCLUDES= 77 | for ssldir in $ssldirs; do 78 | AC_MSG_CHECKING([for openssl/ssl.h in $ssldir]) 79 | if test -f "$ssldir/include/openssl/ssl.h"; then 80 | OPENSSL_INCLUDES="-I$ssldir/include" 81 | OPENSSL_LDFLAGS="-L$ssldir/lib" 82 | OPENSSL_LIBS="-lssl -lcrypto" 83 | found=true 84 | AC_MSG_RESULT([yes]) 85 | break 86 | else 87 | AC_MSG_RESULT([no]) 88 | fi 89 | done 90 | 91 | # if the file wasn't found, well, go ahead and try the link anyway -- maybe 92 | # it will just work! 93 | fi 94 | 95 | # try the preprocessor and linker with our new flags, 96 | # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS 97 | 98 | AC_MSG_CHECKING([whether compiling and linking against OpenSSL works]) 99 | echo "Trying link with OPENSSL_LDFLAGS=$OPENSSL_LDFLAGS;" \ 100 | "OPENSSL_LIBS=$OPENSSL_LIBS; OPENSSL_INCLUDES=$OPENSSL_INCLUDES" >&AS_MESSAGE_LOG_FD 101 | 102 | save_LIBS="$LIBS" 103 | save_LDFLAGS="$LDFLAGS" 104 | save_CPPFLAGS="$CPPFLAGS" 105 | LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" 106 | LIBS="$OPENSSL_LIBS $LIBS" 107 | CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" 108 | AC_LINK_IFELSE( 109 | [AC_LANG_PROGRAM([#include ], [SSL_new(NULL)])], 110 | [ 111 | AC_MSG_RESULT([yes]) 112 | $1 113 | ], [ 114 | AC_MSG_RESULT([no]) 115 | $2 116 | ]) 117 | CPPFLAGS="$save_CPPFLAGS" 118 | LDFLAGS="$save_LDFLAGS" 119 | LIBS="$save_LIBS" 120 | 121 | AC_SUBST([OPENSSL_INCLUDES]) 122 | AC_SUBST([OPENSSL_LIBS]) 123 | AC_SUBST([OPENSSL_LDFLAGS]) 124 | ]) 125 | -------------------------------------------------------------------------------- /src/lua/http/server.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | local common = require "ratchet.http.common" 4 | 5 | ratchet.http = ratchet.http or {} 6 | ratchet.http.server = {} 7 | ratchet.http.server.__index = ratchet.http.server 8 | 9 | -- {{{ ratchet.http.server.new() 10 | function ratchet.http.server.new(socket, from, handlers) 11 | local self = {} 12 | setmetatable(self, ratchet.http.server) 13 | 14 | self.socket = socket 15 | self.from = from 16 | self.handlers = handlers 17 | 18 | return self 19 | end 20 | -- }}} 21 | 22 | -- {{{ build_response_and_headers() 23 | local function build_response_and_headers(response) 24 | local ret = "HTTP/1.0 " .. response.code .. " " .. response.message .. "\r\n" 25 | if response.headers and #response.headers then 26 | ret = ret .. common.build_header_string(response.headers) 27 | end 28 | ret = ret .. "\r\n" 29 | return ret 30 | end 31 | -- }}} 32 | 33 | -- {{{ send_response() 34 | local function send_response(self, response) 35 | local response_str = build_response_and_headers(response) 36 | local remaining = response_str 37 | if response.data then 38 | remaining = remaining .. response.data 39 | end 40 | repeat 41 | remaining = self.socket:send(remaining) 42 | until not remaining 43 | self.socket:shutdown("both") 44 | self.socket:close() 45 | end 46 | -- }}} 47 | 48 | -- {{{ parse_request_so_far() 49 | local function parse_request_so_far(so_far, unparsed_i, request) 50 | local i 51 | 52 | if not request.command or not request.uri then 53 | local cmd_pattern = "^(.-)%s+(.-)%s+[hH][tT][tT][pP]%/([%d%.]+)\r\n()" 54 | request.command, request.uri, request.http_ver, i = so_far:match(cmd_pattern, unparsed_i) 55 | if i then 56 | unparsed_i = i 57 | else 58 | if so_far:match("^.-\r\n", unparsed_i) then 59 | error("Malformed HTTP session.") 60 | end 61 | return false, unparsed_i 62 | end 63 | end 64 | 65 | if not request.headers then 66 | local hdr_pattern = "^(.-\r\n)\r\n()" 67 | local hdrs, i = so_far:match(hdr_pattern, unparsed_i) 68 | if i then 69 | request.headers = common.parse_header_string(hdrs) 70 | unparsed_i = i 71 | else 72 | hdr_pattern = "^\r\n()" 73 | i = so_far:match(hdr_pattern, unparsed_i) 74 | if i then 75 | request.headers = {} 76 | unparsed_i = i 77 | else 78 | return false, unparsed_i 79 | end 80 | end 81 | end 82 | 83 | if not request.data then 84 | if not request.headers['content-length'] then 85 | return true 86 | end 87 | local content_len = tonumber(request.headers['content-length'][1]) 88 | if not content_len then 89 | return true 90 | end 91 | 92 | local recved_len = #so_far - unparsed_i + 1 93 | if recved_len >= content_len then 94 | request.data = so_far:sub(unparsed_i, unparsed_i+content_len-1) 95 | return true 96 | else 97 | return false, unparsed_i 98 | end 99 | end 100 | end 101 | -- }}} 102 | 103 | -- {{{ get_request() 104 | local function get_request(socket) 105 | local request = {} 106 | local so_far = "" 107 | local unparsed_i = 1 108 | local done = false 109 | 110 | while not done do 111 | local data = socket:recv() 112 | so_far = so_far .. data 113 | done, unparsed_i = parse_request_so_far(so_far, unparsed_i, request) 114 | if data == "" then 115 | break 116 | end 117 | end 118 | socket:shutdown("read") 119 | 120 | --return request.command, request.uri, request.headers, request.data 121 | return request 122 | end 123 | -- }}} 124 | 125 | -- {{{ ratchet.http.server:handle() 126 | function ratchet.http.server:handle() 127 | local req = get_request(self.socket) 128 | 129 | local cmd_handler 130 | if req.command then 131 | cmd_handler = self.handlers[req.command:upper()] 132 | end 133 | local response = {code = 501, message = "Not Implemented"} 134 | if req.http_ver ~= "1.0" then 135 | response = {code = 505, message = "Version Not Supported"} 136 | end 137 | if cmd_handler then 138 | response = cmd_handler(self.handlers, req.uri, req.headers, req.data, self.from) 139 | end 140 | 141 | send_response(self, response) 142 | end 143 | -- }}} 144 | 145 | ratchet.http.server.__call = ratchet.http.server.handle 146 | 147 | return ratchet.http.server 148 | 149 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 150 | -------------------------------------------------------------------------------- /src/lua/socketpad/init.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | 4 | ratchet.socketpad = {} 5 | ratchet.socketpad.__index = ratchet.socketpad 6 | 7 | -- {{{ ratchet.socketpad.new() 8 | function ratchet.socketpad.new(socket, from) 9 | local self = {} 10 | setmetatable(self, ratchet.socketpad) 11 | 12 | self.socket = socket 13 | self.from = from 14 | self.data = {} 15 | 16 | self.recv_buffer = "" 17 | self.send_buffer = "" 18 | 19 | return self 20 | end 21 | -- }}} 22 | 23 | -- {{{ recv_once() 24 | local function recv_once(self) 25 | local data = self.socket:recv() 26 | 27 | self.recv_buffer = self.recv_buffer .. data 28 | return data 29 | end 30 | -- }}} 31 | 32 | -- {{{ recv_until_bytes() 33 | local function recv_until_bytes(self, bytes) 34 | local incomplete = nil 35 | while #self.recv_buffer < bytes do 36 | local data = recv_once(self) 37 | if data == '' then 38 | incomplete = true 39 | break 40 | end 41 | end 42 | 43 | local ret = self.recv_buffer:sub(1, bytes) 44 | self.recv_buffer = self.recv_buffer:sub(bytes+1) 45 | return ret, incomplete 46 | end 47 | -- }}} 48 | 49 | -- {{{ recv_until_string() 50 | local function recv_until_string(self, str) 51 | local start_i, end_i, incomplete 52 | while true do 53 | start_i, end_i = self.recv_buffer:find(str, 1, true) 54 | if end_i then 55 | end_i = end_i 56 | break 57 | end 58 | 59 | local data = recv_once(self) 60 | if data == "" then 61 | incomplete = true 62 | end_i = #self.recv_buffer 63 | break 64 | end 65 | end 66 | 67 | local ret = self.recv_buffer:sub(1, end_i) 68 | self.recv_buffer = self.recv_buffer:sub(end_i+1) 69 | return ret, incomplete 70 | end 71 | -- }}} 72 | 73 | -- {{{ ratchet.socketpad:recv() 74 | function ratchet.socketpad:recv(through) 75 | if type(through) == "string" then 76 | return recv_until_string(self, through) 77 | elseif type(through) == "number" then 78 | return recv_until_bytes(self, through) 79 | else 80 | return nil, "Argument must be a number or string." 81 | end 82 | end 83 | -- }}} 84 | 85 | -- {{{ ratchet.socketpad:recv_remaining() 86 | function ratchet.socketpad:recv_remaining() 87 | local ret = self.recv_buffer 88 | self.recv_buffer = '' 89 | 90 | return ret 91 | end 92 | -- }}} 93 | 94 | -- {{{ ratchet.socketpad:peek() 95 | function ratchet.socketpad:peek() 96 | return self.recv_buffer 97 | end 98 | -- }}} 99 | 100 | -- {{{ ratchet.socketpad:update_and_peek() 101 | function ratchet.socketpad:update_and_peek() 102 | local data, err = recv_once(self) 103 | if not data then 104 | return nil, err 105 | elseif data == '' then 106 | return self.recv_buffer, true 107 | end 108 | 109 | return self.recv_buffer 110 | end 111 | -- }}} 112 | 113 | -- {{{ ratchet.socketpad:send_behavior() 114 | function ratchet.socketpad:send_behavior(what, how) 115 | if what == "chunk_size" then 116 | self.chunk_size = tonumber(how) 117 | elseif what == "always_flush" then 118 | if type(how) == "nil" then 119 | self.always_flush = true 120 | else 121 | self.always_flush = how 122 | end 123 | else 124 | error("Unknown behavior type: " .. tostring(what)) 125 | end 126 | end 127 | -- }}} 128 | 129 | -- {{{ update_send_buffer() 130 | local function update_send_buffer(self, data) 131 | if self.chunk_size and #self.send_buffer + #data > self.chunk_size then 132 | while data ~= '' do 133 | local extra = self.chunk_size - #self.send_buffer 134 | local for_now = data:sub(1, extra) 135 | data = data:sub(extra+1) 136 | self.send_buffer = self.send_buffer .. for_now 137 | self:flush() 138 | end 139 | else 140 | self.send_buffer = self.send_buffer .. data 141 | end 142 | end 143 | -- }}} 144 | 145 | -- {{{ ratchet.socketpad:send() 146 | function ratchet.socketpad:send(data, more) 147 | if more and (not data or data == '') then 148 | self:flush() 149 | return 150 | end 151 | 152 | update_send_buffer(self, data) 153 | 154 | if not more or self.always_flush then 155 | self:flush() 156 | end 157 | end 158 | -- }}} 159 | 160 | -- {{{ ratchet.socketpad:flush() 161 | function ratchet.socketpad:flush() 162 | local to_send = self.send_buffer 163 | self.send_buffer = '' 164 | 165 | repeat 166 | to_send = self.socket:send(to_send) 167 | until not to_send 168 | end 169 | -- }}} 170 | 171 | -- {{{ ratchet.socketpad:close() 172 | function ratchet.socketpad:close() 173 | self.socket:close() 174 | end 175 | -- }}} 176 | 177 | return ratchet.socketpad 178 | 179 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 180 | -------------------------------------------------------------------------------- /test/test_smtp_tls.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | require "ratchet.smtp.client" 3 | require "ratchet.smtp.server" 4 | 5 | debugging = false 6 | 7 | -- {{{ debug_print() 8 | local function debug_print(t, data) 9 | if t == "send" or t == "recv" then 10 | print(t .. ": [" .. data .. "]") 11 | else 12 | print(t) 13 | end 14 | end 15 | -- }}} 16 | 17 | -- {{{ server_handlers 18 | 19 | server_handlers = {} 20 | 21 | function server_handlers:BANNER(reply) 22 | reply.code = "220" 23 | reply.message = "test banner" 24 | end 25 | 26 | function server_handlers:EHLO(reply, ehlo_as) 27 | reply.code = "250" 28 | reply.message = "hello " .. ehlo_as 29 | end 30 | 31 | function server_handlers:MAIL(reply, address) 32 | reply.code = "250" 33 | reply.message = "test sender okay: " .. address 34 | end 35 | 36 | function server_handlers:RCPT(reply, address) 37 | reply.code = "250" 38 | reply.message = "test recipient okay: " .. address 39 | end 40 | 41 | function server_handlers:DATA(reply) 42 | reply.code = "354" 43 | reply.message = "ready for test data" 44 | end 45 | 46 | function server_handlers:HAVE_DATA(reply, data) 47 | reply.code = "250" 48 | reply.message = "test data received" 49 | end 50 | 51 | function server_handlers:SLIMTA(arg) 52 | local reply = { 53 | code = "123", 54 | message = "custom command okay", 55 | } 56 | 57 | server_received_message = true 58 | return reply 59 | end 60 | 61 | function server_handlers:QUIT(reply) 62 | reply.code = "221" 63 | reply.message = "test connection quit" 64 | end 65 | 66 | -- }}} 67 | 68 | -- {{{ client_handler() 69 | function client_handler(client, check_host) 70 | local enc = client.io.socket:encrypt(ssl2) 71 | enc:client_handshake() 72 | 73 | local got_cert, verified, host_matched = enc:verify_certificate(check_host) 74 | assert(got_cert and verified and host_matched) 75 | 76 | local banner = client:get_banner() 77 | assert(banner.code == "220" and banner.message == "test banner") 78 | 79 | local ehlo = client:ehlo("test") 80 | assert(ehlo.code == "250") 81 | assert(not client.extensions:has("STARTTLS")) 82 | 83 | local mailfrom = client:mailfrom("sender@slimta.org") 84 | local rcptto1 = client:rcptto("rcpt1@slimta.org") 85 | local rcptto2 = client:rcptto("rcpt2@slimta.org") 86 | local data = client:data() 87 | 88 | assert(mailfrom.code == "250" and mailfrom.message == "2.1.0 test sender okay: sender@slimta.org") 89 | assert(rcptto1.code == "250" and rcptto1.message == "2.1.5 test recipient okay: rcpt1@slimta.org") 90 | assert(rcptto2.code == "250" and rcptto2.message == "2.1.5 test recipient okay: rcpt2@slimta.org") 91 | assert(data.code == "354" and data.message == "ready for test data") 92 | 93 | local send_data = client:send_data("arbitrary test message data") 94 | assert(send_data.code == "250" and send_data.message == "2.6.0 test data received") 95 | 96 | local custom = client:custom_command("SLIMTA", "test") 97 | assert(custom.code == "123" and custom.message == "custom command okay") 98 | 99 | local quit = client:quit() 100 | assert(quit.code == "221" and quit.message == "2.0.0 test connection quit") 101 | 102 | client_sent_message = true 103 | end 104 | -- }}} 105 | 106 | -- {{{ server_ctx() 107 | function server_ctx(host, port) 108 | local rec = ratchet.socket.prepare_tcp(host, port, "AF_INET") 109 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 110 | if debugging then socket:set_tracer(debug_print) end 111 | socket:setsockopt("SO_REUSEADDR", true) 112 | socket:bind(rec.addr) 113 | socket:listen() 114 | 115 | ratchet.thread.attach(client_ctx, host, port) 116 | 117 | local client, from = socket:accept() 118 | if debugging then client:set_tracer(debug_print) end 119 | 120 | local handler = ratchet.smtp.server.new(client, server_handlers, ssl1, true) 121 | handler:handle() 122 | assert(handler.using_tls) 123 | end 124 | -- }}} 125 | 126 | -- {{{ client_ctx() 127 | function client_ctx(host, port) 128 | local rec = ratchet.socket.prepare_tcp(host, port, "AF_INET") 129 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 130 | if debugging then socket:set_tracer(debug_print) end 131 | socket:connect(rec.addr) 132 | 133 | local handler = ratchet.smtp.client.new(socket) 134 | client_handler(handler, rec.host) 135 | end 136 | -- }}} 137 | 138 | ssl1 = ratchet.ssl.new(ratchet.ssl.SSLv3_server) 139 | ssl1:load_certs("cert.pem") 140 | 141 | ssl2 = ratchet.ssl.new(ratchet.ssl.SSLv3_client) 142 | ssl2:load_cas(nil, "cert.pem") 143 | 144 | kernel = ratchet.new(function () 145 | ratchet.thread.attach(server_ctx, "localhost", 10025) 146 | end, function (err, thread) 147 | print(debug.traceback(thread, tostring(err), 2)) 148 | os.exit(1) 149 | end) 150 | kernel:loop() 151 | 152 | assert(server_received_message) 153 | assert(client_sent_message) 154 | 155 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 156 | -------------------------------------------------------------------------------- /test/test_smtp_starttls.lua: -------------------------------------------------------------------------------- 1 | require "ratchet" 2 | require "ratchet.smtp.client" 3 | require "ratchet.smtp.server" 4 | 5 | debugging = false 6 | 7 | -- {{{ debug_print() 8 | local function debug_print(t, data) 9 | if t == "send" or t == "recv" then 10 | print(t .. ": [" .. data .. "]") 11 | else 12 | print(t) 13 | end 14 | end 15 | -- }}} 16 | 17 | -- {{{ server_handlers 18 | 19 | server_handlers = {} 20 | 21 | function server_handlers:BANNER(reply) 22 | reply.code = "220" 23 | reply.message = "test banner" 24 | end 25 | 26 | function server_handlers:EHLO(reply, ehlo_as) 27 | reply.code = "250" 28 | reply.message = "hello " .. ehlo_as 29 | end 30 | 31 | function server_handlers:STARTTLS(reply) 32 | reply.code = "220" 33 | reply.message = "testing TLS" 34 | end 35 | 36 | function server_handlers:MAIL(reply, address) 37 | reply.code = "250" 38 | reply.message = "test sender okay: " .. address 39 | end 40 | 41 | function server_handlers:RCPT(reply, address) 42 | reply.code = "250" 43 | reply.message = "test recipient okay: " .. address 44 | end 45 | 46 | function server_handlers:DATA(reply) 47 | reply.code = "354" 48 | reply.message = "ready for test data" 49 | end 50 | 51 | function server_handlers:HAVE_DATA(reply, data) 52 | reply.code = "250" 53 | reply.message = "test data received" 54 | end 55 | 56 | function server_handlers:SLIMTA(arg) 57 | local reply = { 58 | code = "123", 59 | message = "custom command okay", 60 | } 61 | 62 | server_received_message = true 63 | return reply 64 | end 65 | 66 | function server_handlers:QUIT(reply) 67 | reply.code = "221" 68 | reply.message = "test connection quit" 69 | end 70 | 71 | -- }}} 72 | 73 | -- {{{ client_handler() 74 | function client_handler(client, check_host) 75 | local banner = client:get_banner() 76 | assert(banner.code == "220" and banner.message == "test banner") 77 | 78 | local ehlo = client:ehlo("test") 79 | assert(ehlo.code == "250") 80 | assert(client.extensions:has("STARTTLS")) 81 | 82 | local starttls = client:starttls() 83 | assert(starttls.code == "220") 84 | 85 | local enc = client.io.socket:encrypt(ssl2) 86 | enc:client_handshake() 87 | 88 | local got_cert, verified, host_matched = enc:verify_certificate(check_host) 89 | assert(got_cert and verified and host_matched) 90 | 91 | local ehlo = client:ehlo("test") 92 | assert(ehlo.code == "250") 93 | assert(not client.extensions:has("STARTTLS")) 94 | 95 | local mailfrom = client:mailfrom("sender@slimta.org") 96 | local rcptto1 = client:rcptto("rcpt1@slimta.org") 97 | local rcptto2 = client:rcptto("rcpt2@slimta.org") 98 | local data = client:data() 99 | 100 | assert(mailfrom.code == "250" and mailfrom.message == "2.1.0 test sender okay: sender@slimta.org") 101 | assert(rcptto1.code == "250" and rcptto1.message == "2.1.5 test recipient okay: rcpt1@slimta.org") 102 | assert(rcptto2.code == "250" and rcptto2.message == "2.1.5 test recipient okay: rcpt2@slimta.org") 103 | assert(data.code == "354" and data.message == "ready for test data") 104 | 105 | local send_data = client:send_data("arbitrary test message data") 106 | assert(send_data.code == "250" and send_data.message == "2.6.0 test data received") 107 | 108 | local custom = client:custom_command("SLIMTA", "test") 109 | assert(custom.code == "123" and custom.message == "custom command okay") 110 | 111 | local quit = client:quit() 112 | assert(quit.code == "221" and quit.message == "2.0.0 test connection quit") 113 | 114 | client_sent_message = true 115 | end 116 | -- }}} 117 | 118 | -- {{{ server_ctx() 119 | function server_ctx(host, port) 120 | local rec = ratchet.socket.prepare_tcp(host, port, "AF_INET") 121 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 122 | if debugging then socket:set_tracer(debug_print) end 123 | socket:setsockopt("SO_REUSEADDR", true) 124 | socket:bind(rec.addr) 125 | socket:listen() 126 | 127 | ratchet.thread.attach(client_ctx, host, port) 128 | 129 | local client, from = socket:accept() 130 | if debugging then client:set_tracer(debug_print) end 131 | 132 | local handler = ratchet.smtp.server.new(client, server_handlers, ssl1) 133 | handler:handle() 134 | assert(handler.using_tls) 135 | end 136 | -- }}} 137 | 138 | -- {{{ client_ctx() 139 | function client_ctx(host, port) 140 | local rec = ratchet.socket.prepare_tcp(host, port, "AF_INET") 141 | local socket = ratchet.socket.new(rec.family, rec.socktype, rec.protocol) 142 | if debugging then socket:set_tracer(debug_print) end 143 | socket:connect(rec.addr) 144 | 145 | local handler = ratchet.smtp.client.new(socket) 146 | client_handler(handler, rec.host) 147 | end 148 | -- }}} 149 | 150 | ssl1 = ratchet.ssl.new(ratchet.ssl.SSLv3_server) 151 | ssl1:load_certs("cert.pem") 152 | 153 | ssl2 = ratchet.ssl.new(ratchet.ssl.SSLv3_client) 154 | ssl2:load_cas(nil, "cert.pem") 155 | 156 | kernel = ratchet.new(function () 157 | ratchet.thread.attach(server_ctx, "localhost", 10025) 158 | end) 159 | kernel:loop() 160 | 161 | assert(server_received_message) 162 | assert(client_sent_message) 163 | 164 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 165 | -------------------------------------------------------------------------------- /src/lua/smtp/smtp_io.lua: -------------------------------------------------------------------------------- 1 | 2 | local smtp_io = {} 3 | smtp_io.__index = smtp_io 4 | 5 | -- {{{ smtp_io.new() 6 | function smtp_io.new(socket) 7 | local self = {} 8 | setmetatable(self, smtp_io) 9 | 10 | self.socket = socket 11 | 12 | self.send_buffer = {} 13 | self.recv_buffer = "" 14 | 15 | return self 16 | end 17 | -- }}} 18 | 19 | -- {{{ smtp_io:close() 20 | function smtp_io:close() 21 | local enc = self.socket:get_encryption() 22 | if enc then 23 | pcall(enc.shutdown, enc) 24 | end 25 | return self.socket:close() 26 | end 27 | -- }}} 28 | 29 | -- {{{ smtp_io:buffered_recv() 30 | function smtp_io:buffered_recv() 31 | local received = self.socket:recv() 32 | 33 | if received == "" then 34 | local err = ratchet.error.new("Connection closed.", "ECONNCLOSED", "ratchet.smtp.io.recv()") 35 | error(err) 36 | end 37 | 38 | self.recv_buffer = self.recv_buffer .. received 39 | 40 | return self.recv_buffer 41 | end 42 | -- }}} 43 | 44 | -- {{{ smtp_io:buffered_send() 45 | function smtp_io:buffered_send(data) 46 | table.insert(self.send_buffer, data) 47 | end 48 | -- }}} 49 | 50 | -- {{{ smtp_io:flush_send() 51 | function smtp_io:flush_send() 52 | if not self.send_buffer[1] then 53 | -- Don't try to send nothing. 54 | return 55 | end 56 | 57 | local send_buffer = table.concat(self.send_buffer) 58 | self.send_buffer = {} 59 | 60 | repeat 61 | send_buffer = self.socket:send(send_buffer) 62 | until not send_buffer 63 | end 64 | -- }}} 65 | 66 | -- {{{ smtp_io:recv_reply() 67 | function smtp_io:recv_reply() 68 | local pattern 69 | local code, message_lines = nil, {} 70 | local bad_line_pattern = "^(.-)%\r?%\n()" 71 | local incomplete = true 72 | local input = self.recv_buffer 73 | 74 | while incomplete do 75 | -- Build the full reply pattern once we know the code. 76 | if not pattern then 77 | code = input:match("^%d%d%d") 78 | if code then 79 | pattern = "^" .. code .. "([% %\t%-])(.-)%\r?%\n()" 80 | else 81 | local bad_line, end_i = input:match(bad_line_pattern) 82 | if bad_line then 83 | self.recv_buffer = self.recv_buffer:sub(end_i) 84 | return nil, bad_line 85 | end 86 | end 87 | end 88 | 89 | -- Check for lines that match the pattern. 90 | if pattern then 91 | local start_i = 1 92 | repeat 93 | local splitter, line, end_i = input:match(pattern, start_i) 94 | if line then 95 | table.insert(message_lines, line) 96 | self.recv_buffer = self.recv_buffer:sub(end_i) 97 | 98 | if splitter ~= "-" then 99 | incomplete = false 100 | start_i = nil 101 | else 102 | start_i = end_i 103 | end 104 | else 105 | local bad_line, end_i = input:match(bad_line_pattern) 106 | if bad_line then 107 | self.recv_buffer = self.recv_buffer:sub(end_i) 108 | return nil, bad_line 109 | else 110 | start_i = nil 111 | end 112 | end 113 | until not start_i 114 | end 115 | 116 | -- Check if we need to receive more data. 117 | if incomplete then 118 | input = self:buffered_recv() 119 | end 120 | end 121 | 122 | return code, table.concat(message_lines, "\r\n") 123 | end 124 | -- }}} 125 | 126 | -- {{{ smtp_io:recv_line() 127 | function smtp_io:recv_line() 128 | local input = self.recv_buffer 129 | 130 | while true do 131 | local line, end_i = input:match("^(.-)%\r?%\n()") 132 | if line then 133 | self.recv_buffer = self.recv_buffer:sub(end_i) 134 | return line 135 | end 136 | 137 | input = self:buffered_recv() 138 | end 139 | end 140 | -- }}} 141 | 142 | -- {{{ smtp_io:recv_command() 143 | function smtp_io:recv_command() 144 | local line = self:recv_line() 145 | local command, extra = line:match("^(%a+)%s*(.-)%s*$") 146 | if command then 147 | return command:upper(), extra 148 | else 149 | return line 150 | end 151 | end 152 | -- }}} 153 | 154 | -- {{{ smtp_io:send_reply() 155 | function smtp_io:send_reply(code, message) 156 | local lines = {} 157 | message = message .. "\r\n" 158 | for line in message:gmatch("(.-)%\r?%\n") do 159 | table.insert(lines, line) 160 | end 161 | local num_lines = #lines 162 | 163 | if num_lines == 0 then 164 | local to_send = code .. " " .. message .. "\r\n" 165 | return self:buffered_send(to_send) 166 | else 167 | local to_send = "" 168 | for i=1,(num_lines-1) do 169 | to_send = to_send .. code .. "-" .. lines[i] .. "\r\n" 170 | end 171 | to_send = to_send .. code .. " " .. lines[num_lines] .. "\r\n" 172 | return self:buffered_send(to_send) 173 | end 174 | end 175 | -- }}} 176 | 177 | -- {{{ smtp_io:send_command() 178 | function smtp_io:send_command(command) 179 | return self:buffered_send(command.."\r\n") 180 | end 181 | -- }}} 182 | 183 | return smtp_io 184 | 185 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 186 | -------------------------------------------------------------------------------- /doc/html/modules/ratchet.bus.transaction.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | ratchet.bus.transaction: Module Index 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 | 116 | 117 |
118 | 119 |

Module ratchet.bus.transaction

120 | 121 |

After a request has been sent or received by the client- or server-side, respectively, of a message bus, the first returned value is a transaction object, defined by this module. A transaction object cannot be created directly, it is presented as a module for documentation purposes only.

122 | 123 | 124 | 125 | 126 | 127 |

Functions

128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |
recv_response (self)Receives a response from the server-side of a message bus.
send_response (self, response)Sends a response back to the client-side of a message bus, after a request was received fro that client.
141 | 142 | 143 | 144 | 145 | 146 | 147 |
148 |
149 | 150 | 151 | 152 |

Functions

153 |
154 | 155 | 156 | 157 |
recv_response (self)
158 |
159 | Receives a response from the server-side of a message bus. This function is available from transactions returned by recv_response(). 160 | 161 | 162 |

Parameters

163 |
    164 | 165 |
  • 166 | self: a transaction object returned by send_request(). 167 |
  • 168 | 169 |
170 | 171 | 172 | 173 | 174 | 175 | 176 |

Return value:

177 | the response from the server-side message bus. 178 | 179 | 180 | 181 |
182 | 183 | 184 | 185 | 186 |
send_response (self, response)
187 |
188 | Sends a response back to the client-side of a message bus, after a request was received fro that client. Information about the client's connection is available in the "from" index of the transaction object, if the bus is socket-based. This function is available from transactions returned by recv_request(). 189 | 190 | 191 |

Parameters

192 |
    193 | 194 |
  • 195 | self: a transaction object returned by recv_request(). 196 |
  • 197 | 198 |
  • 199 | response: a response object to send. 200 |
  • 201 | 202 |
203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 |
212 | 213 | 214 |
215 | 216 | 217 | 218 | 219 | 220 | 221 |
222 | 223 |
224 | 225 |
226 |

Valid XHTML 1.0!

227 |
228 | 229 |
230 | 231 | 232 | -------------------------------------------------------------------------------- /doc/html/modules/ratchet.dns.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | ratchet.dns: Module Index 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 | 116 | 117 |
118 | 119 |

Module ratchet.dns

120 | 121 |

Provides a system for querying DNS for information such as A, AAAA, PTR, MX, and TXT records. While there is a class-based system internally, that API is currently hidden by namespace functions that call everything necessary.

122 | 123 | 124 | 125 | 126 | 127 |

Functions

128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |
query (data, type)Queries for results for one type of query for the given data.
query_all (data, types)Queries for results for many types of query for the given data.
141 | 142 | 143 | 144 | 145 | 146 | 147 |
148 |
149 | 150 | 151 | 152 |

Functions

153 |
154 | 155 | 156 | 157 |
query (data, type)
158 |
159 | Queries for results for one type of query for the given data. Until the results come in, this function will pause the calling thread. The return value is a table whose contents are defined by the type of query. See manual for complete details. 160 | 161 | 162 |

Parameters

163 |
    164 | 165 |
  • 166 | data: The hostname, IP, or special-case to query against. 167 |
  • 168 | 169 |
  • 170 | type: The type of query, e.g. "a" or "mx". 171 |
  • 172 | 173 |
174 | 175 | 176 | 177 | 178 | 179 | 180 |

Return value:

181 | Table with results, or nil followed by an error message. 182 | 183 | 184 | 185 |
186 | 187 | 188 | 189 | 190 |
query_all (data, types)
191 |
192 | Queries for results for many types of query for the given data. Until all results come in, this function will pause the calling thread. This call attempts to perform these lookups in parallel, with a file descriptor for each query type. The return value is a table keyed on either the query types or the query type suffixed with "_error". The values will either be a table of results or an error message, respectively. See manual for complete details. 193 | 194 | 195 |

Parameters

196 |
    197 | 198 |
  • 199 | data: The hostname, IP, or special-case to query against. 200 |
  • 201 | 202 |
  • 203 | types: Table with types of queries, e.g. "a" or "mx". 204 |
  • 205 | 206 |
207 | 208 | 209 | 210 | 211 | 212 | 213 |

Return value:

214 | Table with results, or nil followed by an error message. 215 | 216 | 217 | 218 |
219 | 220 | 221 |
222 | 223 | 224 | 225 | 226 | 227 | 228 |
229 | 230 |
231 | 232 |
233 |

Valid XHTML 1.0!

234 |
235 | 236 |
237 | 238 | 239 | -------------------------------------------------------------------------------- /doc/html/modules/ratchet.http.client.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | ratchet.http.client: Module Index 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 | 116 | 117 |
118 | 119 |

Module ratchet.http.client

120 | 121 |

This library provides client-side access to an HTTP service, commonly used in many different cases across the Web. Currently the client only supports HTTP/1.0, with no current plans to support 1.1 or higher, as it is only provided for convenience and as an example of using the ratchet libraries.

122 | 123 | 124 | 125 | 126 | 127 |

Functions

128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |
new (socket, send_size)Creates a new HTTP client object.
query (self, command, uri, headers, data)Sends an HTTP query, and returns the results.
141 | 142 | 143 | 144 | 145 | 146 | 147 |
148 |
149 | 150 | 151 | 152 |

Functions

153 |
154 | 155 | 156 | 157 |
new (socket, send_size)
158 |
159 | Creates a new HTTP client object. 160 | 161 | 162 |

Parameters

163 |
    164 | 165 |
  • 166 | socket: Used as the underlying socket for the query, under the assumption that encryption has been established as needed and no other I/O has taken place. 167 |
  • 168 | 169 |
  • 170 | send_size: Used as the maximum size of a send() operation, defaults to half of the SO_SNDBUF socket option. 171 |
  • 172 | 173 |
174 | 175 | 176 | 177 | 178 | 179 | 180 |

Return value:

181 | a new http.client object. 182 | 183 | 184 | 185 |
186 | 187 | 188 | 189 | 190 |
query (self, command, uri, headers, data)
191 |
192 | Sends an HTTP query, and returns the results. 193 | 194 | 195 |

Parameters

196 |
    197 | 198 |
  • 199 | self: the http.client object. 200 |
  • 201 | 202 |
  • 203 | command: The HTTP command to use, such as "PUT" or "GET". 204 |
  • 205 | 206 |
  • 207 | uri: The URI to query. 208 |
  • 209 | 210 |
  • 211 | headers: A table whose keys are header names and values are arrays of strings. Each string in the value array will result in a new header of the given name. 212 |
  • 213 | 214 |
  • 215 | data: Additional data to send in the body of the query. It is usually important to specify a "Content-Length" header with the length of this string. 216 |
  • 217 | 218 |
219 | 220 | 221 | 222 | 223 | 224 | 225 |

Return value:

226 | In order: the return code of the query, the descriptive return message, additional headers returned (in the same format as the headers parameter), and a string of data returned by the query (if any). 227 | 228 | 229 | 230 |
231 | 232 | 233 |
234 | 235 | 236 | 237 | 238 | 239 | 240 |
241 | 242 |
243 | 244 |
245 |

Valid XHTML 1.0!

246 |
247 | 248 |
249 | 250 | 251 | -------------------------------------------------------------------------------- /src/lua/smtp/client.lua: -------------------------------------------------------------------------------- 1 | 2 | require "ratchet" 3 | 4 | local data_sender = require "ratchet.smtp.data_sender" 5 | local smtp_io = require "ratchet.smtp.smtp_io" 6 | local smtp_extensions = require "ratchet.smtp.smtp_extensions" 7 | local smtp_reply = require "ratchet.smtp.smtp_reply" 8 | 9 | ratchet.smtp = ratchet.smtp or {} 10 | ratchet.smtp.client = {} 11 | ratchet.smtp.client.__index = ratchet.smtp.client 12 | 13 | -- {{{ratchet.smtp.client.new() 14 | function ratchet.smtp.client.new(socket, iter_size) 15 | local self = {} 16 | setmetatable(self, ratchet.smtp.client) 17 | 18 | self.io = smtp_io.new(socket) 19 | self.iter_size = iter_size 20 | self.extensions = smtp_extensions.new() 21 | 22 | self.recv_queue = {} 23 | 24 | return self 25 | end 26 | -- }}} 27 | 28 | -- {{{ recv_batch() 29 | local function recv_batch(self) 30 | self.io:flush_send() 31 | 32 | repeat 33 | local reply = table.remove(self.recv_queue, 1) 34 | if reply then 35 | reply:recv(self.io) 36 | end 37 | until not reply 38 | end 39 | -- }}} 40 | 41 | -- {{{ratchet.smtp.client:get_banner() 42 | function ratchet.smtp.client:get_banner() 43 | local banner = smtp_reply.new("[BANNER]") 44 | table.insert(self.recv_queue, banner) 45 | 46 | recv_batch(self) 47 | 48 | return banner 49 | end 50 | -- }}} 51 | 52 | -- {{{ratchet.smtp.client:ehlo() 53 | function ratchet.smtp.client:ehlo(ehlo_as) 54 | local ehlo = smtp_reply.new("EHLO") 55 | table.insert(self.recv_queue, ehlo) 56 | 57 | local command = "EHLO " .. ehlo_as 58 | self.io:send_command(command) 59 | 60 | recv_batch(self) 61 | if ehlo.code == "250" then 62 | self.extensions:reset() 63 | ehlo.message = self.extensions:parse_string(ehlo.message) 64 | end 65 | 66 | return ehlo 67 | end 68 | -- }}} 69 | 70 | -- {{{ratchet.smtp.client:helo() 71 | function ratchet.smtp.client:helo(helo_as) 72 | local ehlo = smtp_reply.new("HELO") 73 | table.insert(self.recv_queue, ehlo) 74 | 75 | local command = "HELO " .. helo_as 76 | self.io:send_command(command) 77 | 78 | recv_batch(self) 79 | 80 | return ehlo 81 | end 82 | -- }}} 83 | 84 | -- {{{ratchet.smtp.client:starttls() 85 | function ratchet.smtp.client:starttls() 86 | local starttls = smtp_reply.new("STARTTLS") 87 | table.insert(self.recv_queue, starttls) 88 | 89 | local command = "STARTTLS" 90 | self.io:send_command(command) 91 | 92 | recv_batch(self) 93 | 94 | return starttls 95 | end 96 | -- }}} 97 | 98 | -- {{{ratchet.smtp.client:mailfrom() 99 | function ratchet.smtp.client:mailfrom(address, data_size) 100 | local mailfrom = smtp_reply.new("MAIL FROM") 101 | table.insert(self.recv_queue, mailfrom) 102 | 103 | local command = "MAIL FROM:<"..address..">" 104 | if data_size and self.extensions:has("SIZE") then 105 | command = command .. " SIZE=" .. data_size 106 | end 107 | self.io:send_command(command) 108 | 109 | if not self.extensions:has("PIPELINING") then 110 | recv_batch(self) 111 | end 112 | 113 | return mailfrom 114 | end 115 | -- }}} 116 | 117 | -- {{{ratchet.smtp.client:rcptto() 118 | function ratchet.smtp.client:rcptto(address) 119 | local rcptto = smtp_reply.new("RCPT TO") 120 | table.insert(self.recv_queue, rcptto) 121 | 122 | local command = "RCPT TO:<"..address..">" 123 | self.io:send_command(command) 124 | 125 | if not self.extensions:has("PIPELINING") then 126 | recv_batch(self) 127 | end 128 | 129 | return rcptto 130 | end 131 | -- }}} 132 | 133 | -- {{{ratchet.smtp.client:data() 134 | function ratchet.smtp.client:data() 135 | local data = smtp_reply.new("DATA") 136 | table.insert(self.recv_queue, data) 137 | 138 | local command = "DATA" 139 | self.io:send_command(command) 140 | 141 | recv_batch(self) 142 | 143 | return data 144 | end 145 | -- }}} 146 | 147 | -- {{{ratchet.smtp.client:send_data() 148 | function ratchet.smtp.client:send_data(data) 149 | local send_data = smtp_reply.new("[MESSAGE CONTENTS]") 150 | table.insert(self.recv_queue, send_data) 151 | 152 | local data_sender = data_sender.new(data, self.iter_size) 153 | data_sender:send(self.io) 154 | 155 | recv_batch(self) 156 | 157 | return send_data 158 | end 159 | -- }}} 160 | 161 | -- {{{ratchet.smtp.client:send_empty_data() 162 | function ratchet.smtp.client:send_empty_data() 163 | local send_data = smtp_reply.new("[MESSAGE CONTENTS]") 164 | table.insert(self.recv_queue, send_data) 165 | 166 | self.io:send_command(".") 167 | 168 | recv_batch(self) 169 | 170 | return send_data 171 | end 172 | -- }}} 173 | 174 | -- {{{ratchet.smtp.client:custom_command() 175 | function ratchet.smtp.client:custom_command(command, arg) 176 | local custom = smtp_reply.new(command:upper()) 177 | table.insert(self.recv_queue, custom) 178 | 179 | if arg then 180 | command = command .. " " .. arg 181 | end 182 | self.io:send_command(command) 183 | 184 | recv_batch(self) 185 | 186 | return custom 187 | end 188 | -- }}} 189 | 190 | -- {{{ratchet.smtp.client:rset() 191 | function ratchet.smtp.client:rset() 192 | local rset = smtp_reply.new("RSET") 193 | table.insert(self.recv_queue, rset) 194 | 195 | local command = "RSET" 196 | self.io:send_command(command) 197 | 198 | recv_batch(self) 199 | 200 | return rset 201 | end 202 | -- }}} 203 | 204 | -- {{{ratchet.smtp.client:quit() 205 | function ratchet.smtp.client:quit() 206 | local quit = smtp_reply.new("QUIT") 207 | table.insert(self.recv_queue, quit) 208 | 209 | local command = "QUIT" 210 | self.io:send_command(command) 211 | 212 | recv_batch(self) 213 | self.io:close() 214 | 215 | return quit 216 | end 217 | -- }}} 218 | 219 | -- {{{ratchet.smtp.client:close() 220 | function ratchet.smtp.client:close() 221 | self.io:close() 222 | end 223 | -- }}} 224 | 225 | return ratchet.smtp.client 226 | 227 | -- vim:foldmethod=marker:sw=4:ts=4:sts=4:et: 228 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | What is Ratchet? 2 | ================ 3 | 4 | The purpose of the ratchet library is to provide a generic socket control 5 | mechanism for large numbers of sockets without using threads or losing the ease 6 | of synchronous socket programming. Implementations of standard Berkeley sockets 7 | are provided, as well as rudimentary support for ZeroMQ sockets, provided 8 | versions greater than or equal to 2.1.0 are installed. The polling mechanism is 9 | written on top of libevent (http://monkey.org/~provos/libevent/). 10 | 11 | The ratchet library provides and manages a blend of libevent event loops and 12 | Lua coroutines to provide the effect of synchronous socket programming. Each 13 | call to a blocking socket (or DNS or ZeroMQ) method actually yields execution 14 | until the action is ready to be taken. Ratchet coroutines may also wait for each 15 | other. Any application protocol (text-based or binary) can be implemented. 16 | Currently, example HTTP and SMTP client and server implementations are provided, 17 | see docs for details. 18 | 19 | The project itself is built using autotools for portability, though it depends 20 | on several GNU/Linux features. The project provides a suite of post-build tests 21 | which double as sample usages, executed with "make check". 22 | 23 | Build Information 24 | ================= 25 | 26 | Lua 5.2+ is required to build, as this is a Lua library. Other than that, it 27 | should work on most modern GNU/Linux systems with kernel 2.6+ and glibc2. If 28 | your distribution or custom install added a suffixes to differentiate Lua 29 | versions, you can use the configure option: 30 | 31 | --with-lua-suffix=ARG Gives suffix for Lua binary and library files. 32 | 33 | Many distributions do not yet include Lua 5.2 in package management. To avoid 34 | blowing away an existing Lua 5.1 install, you might want to install Lua 5.2 35 | somewhere unique. Let's say you installed in /opt/lua-5.2: 36 | 37 | PATH="/opt/lua-5.2/bin:$PATH" \ 38 | CFLAGS="$CFLAGS -I/opt/lua-5.2/include" \ 39 | LDFLAGS="$LDFLAGS -I/opt/lua-5.2/lib" \ 40 | ./configure 41 | 42 | If the paths to C and Lua module installation directories are not available from 43 | pkg-config, they may need to be manually specified with: 44 | 45 | --with-lua-cpath=CPATH Install Lua C modules to CPATH 46 | --with-lua-lpath=LPATH Install Lua modules to LPATH 47 | 48 | There are two different ways to use this library: from C or from Lua. By 49 | default, both options are made available. If you don't need to use the library 50 | from the Lua interpreter, you can choose to disable that with the configure 51 | option: 52 | 53 | --disable-module Disable the Lua module. 54 | 55 | Similarly, if your Lua installation is custom, or the path to install Lua C 56 | modules is not available in pkg-config, you can define a custom installation 57 | path for the Lua module with a configure option: 58 | 59 | --enable-module=PATH Install the Lua module ratchet.so to PATH. 60 | 61 | If you do not need the developement libraries and headers installed, you can 62 | choose to disable them with the configure option: 63 | 64 | --disable-devel Disable developement headers and libraries. 65 | 66 | By default, ratchet attempts to build with ZeroMQ (2.1.0+ is required). If you 67 | do not need access to ZeroMQ sockets or do not meet the version requirement, 68 | you can disable that portion of the library with a configure option: 69 | 70 | --disable-zeromq Disable usage of ZeroMQ libraries. 71 | 72 | By default, ratchet uses dns.c to provide enhanced DNS querying. If you do not 73 | need access to DNS queries whatsoever, you can disable that portion of the build 74 | with the configure option below. Note, however, that currently there is no 75 | built-in method to build the data required for BSD sockets without this feature. 76 | 77 | --disable-dns Disable usage of dns.c libraries. 78 | 79 | To encrypt socket channels, ratchet can tie in to the OpenSSL suite. This is 80 | important, for example, to implement HTTPS or the STARTTLS SMTP command. If 81 | included in the build, the OpenSSL global state will be initialized every time 82 | a program using the ratchet library is started, regardless of if/when 83 | encryption is used (see `SSL_library_init()` and `SSL_load_error_strings()`). 84 | OpenSSL will not be included in the build if the following configure option is 85 | given: 86 | 87 | --disable-openssl Disable usage of OpenSSL libraries. 88 | 89 | For Linux users (see version requirements below), there is access to the 90 | timerfd (man page `timerfd_create()`) routines for intelligent time keeping and 91 | time-based triggers. If the requirements can't be met, this feature can be 92 | disabled with a configure option: 93 | 94 | --disable-timerfd Disabe usage of timerfd calls. 95 | 96 | If, for any reason, you do not need access to BSD sockets (for example you only 97 | need ZeroMQ, timers, or thread management), you can disable inclusion of these 98 | features with another configure option: 99 | 100 | --disable-socket Disable usage of BSD sockets. 101 | 102 | Example protocol implementations, installed to the Lua module path, can be 103 | disabled with the following configure options: 104 | 105 | --disable-http Do not install HTTP protocol implementation. 106 | --disable-smtp Do not install SMTP protocol implementation. 107 | 108 | To increase the size of the buffer used when the size of a piece of data is 109 | unknown, you can also use the following configure variable. Currently, it only 110 | controls the size of the buffer used in `recv()` system calls. 111 | 112 | BUFSIZ The size of the intermediate buffers used when building large 113 | Lua strings. 114 | 115 | All other configure options are standard from GNU autotools. 116 | 117 | Dependencies 118 | ============ 119 | 120 | * Lua 5.2+ (http://www.lua.org) 121 | * libevent 2+ (http://libevent.org/) 122 | 123 | * Without --disable-zeromq: ZeroMQ 2.1.0+ (http://www.zeromq.org) 124 | * Without --disable-timerfd: Linux kernel 2.6.25+, Glibc 2.8+ 125 | * Without --disable-openssl: OpenSSL (http://www.openssl.org/) 126 | 127 | -------------------------------------------------------------------------------- /doc/html/modules/ratchet.error.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | ratchet.error: Module Index 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 | 116 | 117 |
118 | 119 |

Module ratchet.error

120 | 121 |

A ratchet.error object is thrown in most cases when ratchet functions error. This object exposes certain information to present to the user or to make programmatic decisions based on. Converting these objects to a string presents human-readable information about the error.

122 | 123 | 124 | 125 | 126 | 127 |

Functions

128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |
is (error, code)Checks is the code assigned to an error message matches the given code.
new (description, code, func, file, line, syscall, errno, descripion)This function allows manual creation of error objects.
141 | 142 | 143 | 144 | 145 | 146 | 147 |
148 |
149 | 150 | 151 | 152 |

Functions

153 |
154 | 155 | 156 | 157 |
is (error, code)
158 |
159 | Checks is the code assigned to an error message matches the given code. This function is also exposed as a method of ratchet.error objects. 160 | 161 | 162 |

Parameters

163 |
    164 | 165 |
  • 166 | error: the ratchet.error object to check. 167 |
  • 168 | 169 |
  • 170 | code: the code to compare against. 171 |
  • 172 | 173 |
174 | 175 | 176 | 177 | 178 | 179 | 180 |

Return value:

181 | true if the error matches the code, false otherwise. 182 | 183 | 184 | 185 |
186 | 187 | 188 | 189 | 190 |
new (description, code, func, file, line, syscall, errno, descripion)
191 |
192 | This function allows manual creation of error objects. Usually it is created automatically by ratchet functions when an error occurs. The names of the parameters to this function are also the indices to access the information on a created ratchet.error object. 193 | 194 | 195 |

Parameters

196 |
    197 | 198 |
  • 199 | description: 200 |
  • 201 | 202 |
  • 203 | code: a string code that can identify the error message. 204 |
  • 205 | 206 |
  • 207 | func: optional string identifying the function throwing the error. 208 |
  • 209 | 210 |
  • 211 | file: optional C file where the error was thrown. 212 |
  • 213 | 214 |
  • 215 | line: optional C file line number where the error was thrown. 216 |
  • 217 | 218 |
  • 219 | syscall: optional string identifying system call causing the error. 220 |
  • 221 | 222 |
  • 223 | errno: optional integer identifying the system error code. 224 |
  • 225 | 226 |
  • 227 | descripion: human-readable description of the error. 228 |
  • 229 | 230 |
231 | 232 | 233 | 234 | 235 | 236 | 237 |

Return value:

238 | a new ratchet.error object. 239 | 240 | 241 | 242 |
243 | 244 | 245 |
246 | 247 | 248 | 249 | 250 | 251 | 252 |
253 | 254 |
255 | 256 |
257 |

Valid XHTML 1.0!

258 |
259 | 260 |
261 | 262 | 263 | -------------------------------------------------------------------------------- /doc/html/modules/ratchet.http.server.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | ratchet.http.server: Module Index 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 | 116 | 117 |
118 | 119 |

Module ratchet.http.server

120 | 121 |

This library provides server-side capabilities for an HTTP service. Currently the server only supports HTTP/1.0, with no current plans to support 1.1 or higher, as it is only provided for convenience and as an example of using the ratchet libraries.

122 | 123 | 124 | 125 | 126 | 127 |

Functions

128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
__call (self)An alias to the handle() method, allowing the http.server object to be callable as if it were a function.
handle (self)Receives an HTTP query, calls the corresponding handler, and returns the results.
new (socket, handlers, from, send_size)Creates a new HTTP server object.
146 | 147 | 148 | 149 | 150 | 151 | 152 |
153 |
154 | 155 | 156 | 157 |

Functions

158 |
159 | 160 | 161 | 162 |
__call (self)
163 |
164 | An alias to the handle() method, allowing the http.server object to be callable as if it were a function. 165 | 166 | 167 |

Parameters

168 |
    169 | 170 |
  • 171 | self: the http.server object. 172 |
  • 173 | 174 |
175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 |
184 | 185 | 186 | 187 | 188 |
handle (self)
189 |
190 | Receives an HTTP query, calls the corresponding handler, and returns the results. 191 | 192 | 193 |

Parameters

194 |
    195 | 196 |
  • 197 | self: The http.server object. 198 |
  • 199 | 200 |
201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 |
210 | 211 | 212 | 213 | 214 |
new (socket, handlers, from, send_size)
215 |
216 | Creates a new HTTP server object. A new object should be created with this function for each socket created by accept()'ing a new connection on a listening socket. 217 | 218 | 219 |

Parameters

220 |
    221 | 222 |
  • 223 | socket: Used as the underlying socket for the query, under the assumption that encryption has been established as needed and no other I/O has taken place. 224 |
  • 225 | 226 |
  • 227 | handlers: Table whose keys correspond to supported HTTP commands. The command is called as a method of this table, e.g. handlers:GET(...), with the arguments being the URI, headers, data, and source address. 228 |
  • 229 | 230 |
  • 231 | from: Passed as the last argument to a handler function, typically used to send the source address of the connection (the second value returned by accept()). 232 |
  • 233 | 234 |
  • 235 | send_size: Used as the maximum size of a send() operation, defaults to half of the SO_SNDBUF socket option. 236 |
  • 237 | 238 |
239 | 240 | 241 | 242 | 243 | 244 | 245 |

Return value:

246 | a new http.server object. 247 | 248 | 249 | 250 |
251 | 252 | 253 |
254 | 255 | 256 | 257 | 258 | 259 | 260 |
261 | 262 |
263 | 264 |
265 |

Valid XHTML 1.0!

266 |
267 | 268 |
269 | 270 | 271 | -------------------------------------------------------------------------------- /doc/smtp.client.luadoc: -------------------------------------------------------------------------------- 1 | 2 | --- This library provides client-side access to an SMTP server, commonly used 3 | -- to transport email messages. Currently the client will take advantage of 4 | -- the SIZE, STARTTLS and PIPELINING extensions and can send custom, arbitrary 5 | -- commands. It is provided for convenience and as an example of using the 6 | -- ratchet libraries. 7 | module "ratchet.smtp.client" 8 | 9 | --- Creates a new SMTP client object. 10 | -- @param socket Used as the underlying socket for the session, under the 11 | -- assumption that no I/O has taken place, except possibly 12 | -- encryption if not using STARTTLS. 13 | -- @param iter_size Maximum size of each iteration over message data, defaults 14 | -- to 1024 bytes at a time. 15 | -- @return a new smtp.client object. 16 | function new(socket, iter_size) 17 | 18 | --- Receives the SMTP banner from the server. When this function returns, you 19 | -- can safely expect the return table to be populated. 20 | -- @param self the smtp.client object. 21 | -- @return A reply table with "code" and "message" keys. 22 | function get_banner(self) 23 | 24 | --- Sends the EHLO command. When this function returns, you can safely expect 25 | -- the return table to be populated. The "message" string in the reply table 26 | -- does not include lines designated as SMTP extensions, these are parsed out 27 | -- into the "extensions" member object, see manual for details. 28 | -- @param self the smtp.client object. 29 | -- @param ehlo_as The string to identify as. 30 | -- @return A reply table with "code" and "message" keys. 31 | function ehlo(self, ehlo_as) 32 | 33 | --- Sends the HELO command. When this function returns, you can safely expect 34 | -- the return table to be populated. 35 | -- @param self the smtp.client object. 36 | -- @param helo_as The string to identify as. 37 | -- @return A reply table with "code" and "message" keys. 38 | function helo(self, helo_as) 39 | 40 | --- Sends a STARTTLS request to the server, which indicates that, on a 250 reply 41 | -- from the server, you will initiate a TLS handshake. This function does not 42 | -- do the TLS handshake. When this function returns, you can safely expect the 43 | -- return table to be populated. 44 | -- @param self the smtp.client object. 45 | -- @return A reply table with "code" and "message" keys. 46 | function starttls(self) 47 | 48 | --- Sends a MAIL FROM command, which starts a new message transmission on the 49 | -- session and specifies the sending address. This function will also add the 50 | -- "SIZE=nnn" parameter, if the SIZE extension is supported by the server, 51 | -- where "nnn" is the data_size parameter. When this function returns, the 52 | -- reply table WILL NOT be populated if the server supports the PIPELINING 53 | -- extension. You can only safely expect the return table to be populated 54 | -- after calling the data() function. 55 | -- @param self the smtp.client object. 56 | -- @param address The sender address of the message envelope. 57 | -- @param data_size The approximate size of the message data. 58 | -- @return A reply table with "code" and "message" keys. 59 | function mailfrom(self, address, data_size) 60 | 61 | --- Sends a RCPT TO command, which specifies one (of possibly many) recipient 62 | -- for the current message transaction, as started by mailfrom(). When this 63 | -- function returns the reply table WILL NOT be populated if the server 64 | -- supports the PIPELINING extension. You can only safely expect the return 65 | -- table to be populated after calling the data() function. 66 | -- @param self the smtp.client object. 67 | -- @param address A recipient address to add to the envelope. 68 | -- @return A reply table with "code" and "message" keys. 69 | function rcptto(self, address) 70 | 71 | --- Sends the DATA command, indicating to the server that you are ready to send 72 | -- message data. When this function returns, its reply table as well as those 73 | -- returned by mailfrom() and rcptto() will be populated. If the reply code 74 | -- is a 354, the SMTP protocol requires you send message data, ending with 75 | -- a ".", even if the mailfrom() or rcptto() commands failed. 76 | -- @param self the smtp.client object. 77 | -- @return A reply table with "code" and "message" keys. 78 | function data(self) 79 | 80 | --- Sends arbitrary message data. This function will apply the transparency 81 | -- filters specified by the RFC, so no special care must be taken with the 82 | -- message data before sending it. When message data has been sent, the end 83 | -- marker "." is also sent. It is not safe to expect the reply 84 | -- table to be populated when this function returns, if the server supports 85 | -- the PIPELINING extension. 86 | -- @param self the smtp.client object. 87 | -- @param data The message data, typically as per RFC 2822. 88 | -- @return A reply table with "code" and "message" keys. 89 | function send_data(self, data) 90 | 91 | --- Sends empty message data. This function simply sends the message end marker, 92 | -- ".", and should be used when the server replied to the data() 93 | -- function with a "354" code but calls to mailfrom() or rcptto() were not 94 | -- successful. It is not safe to expect the reply table to be populated when 95 | -- this function returns, if the server supports the PIPELINING extension. 96 | -- @param self the smtp.client object. 97 | -- @return A reply table with "code" and "message" keys. 98 | function send_empty_data(self) 99 | 100 | --- Sends a custom command. This function implies no logic, but does expect a 101 | -- standard SMTP code-message reply from the server. It is safe to expect the 102 | -- reply table to be populated when this function returns. 103 | -- @param self the smtp.client object. 104 | -- @param command The SMTP command to send. 105 | -- @param arg A string to send as an argument to the command, if applicable. 106 | -- @return A reply table with "code" and "message" keys. 107 | function custom_command(self, command, arg) 108 | 109 | --- Sends a RSET command. When this function returns, you can safely expect the 110 | -- return table to be populated. 111 | -- @param self the smtp.client object. 112 | -- @return A reply table with "code" and "message" keys. 113 | function rset(self) 114 | 115 | --- Sends a QUIT command, and closes the connection. You can safely expect the 116 | -- return table to be populated, when this function returns. 117 | -- @param self the smtp.client object. 118 | -- @return A reply table with "code" and "message" keys. 119 | function quit(self) 120 | 121 | -- vim:filetype=lua:sw=4:ts=4:sts=4:et: 122 | -------------------------------------------------------------------------------- /doc/html/luadoc.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-left: 1em; 3 | margin-right: 1em; 4 | font-family: arial, helvetica, geneva, sans-serif; 5 | background-color:#ffffff; margin:0px; 6 | } 7 | 8 | code { 9 | font-family: "Andale Mono", monospace; 10 | } 11 | 12 | tt { 13 | font-family: "Andale Mono", monospace; 14 | } 15 | 16 | body, td, th { font-size: 11pt; } 17 | 18 | h1, h2, h3, h4 { margin-left: 0em; } 19 | 20 | textarea, pre, tt { font-size:10pt; } 21 | body, td, th { color:#000000; } 22 | small { font-size:0.85em; } 23 | h1 { font-size:1.5em; } 24 | h2 { font-size:1.25em; } 25 | h3 { font-size:1.15em; } 26 | h4 { font-size:1.06em; } 27 | 28 | a:link { font-weight:bold; color: #004080; text-decoration: none; } 29 | a:visited { font-weight:bold; color: #006699; text-decoration: none; } 30 | a:link:hover { text-decoration:underline; } 31 | hr { color:#cccccc } 32 | img { border-width: 0px; } 33 | 34 | 35 | h3 { padding-top: 1em; } 36 | 37 | p { margin-left: 1em; } 38 | 39 | p.name { 40 | font-family: "Andale Mono", monospace; 41 | padding-top: 1em; 42 | margin-left: 0em; 43 | } 44 | 45 | blockquote { margin-left: 3em; } 46 | 47 | pre.example { 48 | background-color: rgb(245, 245, 245); 49 | border-top-width: 1px; 50 | border-right-width: 1px; 51 | border-bottom-width: 1px; 52 | border-left-width: 1px; 53 | border-top-style: solid; 54 | border-right-style: solid; 55 | border-bottom-style: solid; 56 | border-left-style: solid; 57 | border-top-color: silver; 58 | border-right-color: silver; 59 | border-bottom-color: silver; 60 | border-left-color: silver; 61 | padding: 1em; 62 | margin-left: 1em; 63 | margin-right: 1em; 64 | font-family: "Andale Mono", monospace; 65 | font-size: smaller; 66 | } 67 | 68 | 69 | hr { 70 | margin-left: 0em; 71 | background: #00007f; 72 | border: 0px; 73 | height: 1px; 74 | } 75 | 76 | ul { list-style-type: disc; } 77 | 78 | table.index { border: 1px #00007f; } 79 | table.index td { text-align: left; vertical-align: top; } 80 | table.index ul { padding-top: 0em; margin-top: 0em; } 81 | 82 | table { 83 | border: 1px solid black; 84 | border-collapse: collapse; 85 | margin-left: auto; 86 | margin-right: auto; 87 | } 88 | th { 89 | border: 1px solid black; 90 | padding: 0.5em; 91 | } 92 | td { 93 | border: 1px solid black; 94 | padding: 0.5em; 95 | } 96 | div.header, div.footer { margin-left: 0em; } 97 | 98 | #container 99 | { 100 | margin-left: 1em; 101 | margin-right: 1em; 102 | background-color: #f0f0f0; 103 | } 104 | 105 | #product 106 | { 107 | text-align: center; 108 | border-bottom: 1px solid #cccccc; 109 | background-color: #ffffff; 110 | } 111 | 112 | #product big { 113 | font-size: 2em; 114 | } 115 | 116 | #product_logo 117 | { 118 | } 119 | 120 | #product_name 121 | { 122 | } 123 | 124 | #product_description 125 | { 126 | } 127 | 128 | #main 129 | { 130 | background-color: #f0f0f0; 131 | border-left: 2px solid #cccccc; 132 | } 133 | 134 | #navigation 135 | { 136 | float: left; 137 | width: 18em; 138 | margin: 0; 139 | vertical-align: top; 140 | background-color: #f0f0f0; 141 | overflow:visible; 142 | } 143 | 144 | #navigation h1 { 145 | background-color:#e7e7e7; 146 | font-size:1.1em; 147 | color:#000000; 148 | text-align:left; 149 | margin:0px; 150 | padding:0.2em; 151 | border-top:1px solid #dddddd; 152 | border-bottom:1px solid #dddddd; 153 | } 154 | 155 | #navigation ul 156 | { 157 | font-size:1em; 158 | list-style-type: none; 159 | padding: 0; 160 | margin: 1px; 161 | } 162 | 163 | #navigation li 164 | { 165 | text-indent: -1em; 166 | margin: 0em 0em 0em 0.5em; 167 | display: block; 168 | padding: 3px 0px 0px 12px; 169 | } 170 | 171 | #navigation li li a 172 | { 173 | padding: 0px 3px 0px -1em; 174 | } 175 | 176 | #content 177 | { 178 | margin-left: 18em; 179 | padding: 1em; 180 | border-left: 2px solid #cccccc; 181 | border-right: 2px solid #cccccc; 182 | background-color: #ffffff; 183 | } 184 | 185 | #about 186 | { 187 | clear: both; 188 | margin: 0; 189 | padding: 5px; 190 | border-top: 2px solid #cccccc; 191 | background-color: #ffffff; 192 | } 193 | 194 | @media print { 195 | body { 196 | font: 12pt "Times New Roman", "TimeNR", Times, serif; 197 | } 198 | a { font-weight:bold; color: #004080; text-decoration: underline; } 199 | 200 | #main { background-color: #ffffff; border-left: 0px; } 201 | #container { margin-left: 2%; margin-right: 2%; background-color: #ffffff; } 202 | 203 | #content { margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff; } 204 | 205 | #navigation { display: none; 206 | } 207 | pre.example { 208 | font-family: "Andale Mono", monospace; 209 | font-size: 10pt; 210 | page-break-inside: avoid; 211 | } 212 | } 213 | 214 | table.module_list td 215 | { 216 | border-width: 1px; 217 | padding: 3px; 218 | border-style: solid; 219 | border-color: #cccccc; 220 | } 221 | table.module_list td.name { background-color: #f0f0f0; } 222 | table.module_list td.summary { width: 100%; } 223 | 224 | table.file_list 225 | { 226 | border-width: 1px; 227 | border-style: solid; 228 | border-color: #cccccc; 229 | border-collapse: collapse; 230 | } 231 | table.file_list td 232 | { 233 | border-width: 1px; 234 | padding: 3px; 235 | border-style: solid; 236 | border-color: #cccccc; 237 | } 238 | table.file_list td.name { background-color: #f0f0f0; } 239 | table.file_list td.summary { width: 100%; } 240 | 241 | 242 | table.function_list 243 | { 244 | border-width: 1px; 245 | border-style: solid; 246 | border-color: #cccccc; 247 | border-collapse: collapse; 248 | } 249 | table.function_list td 250 | { 251 | border-width: 1px; 252 | padding: 3px; 253 | border-style: solid; 254 | border-color: #cccccc; 255 | } 256 | table.function_list td.name { background-color: #f0f0f0; } 257 | table.function_list td.summary { width: 100%; } 258 | 259 | 260 | table.table_list 261 | { 262 | border-width: 1px; 263 | border-style: solid; 264 | border-color: #cccccc; 265 | border-collapse: collapse; 266 | } 267 | table.table_list td 268 | { 269 | border-width: 1px; 270 | padding: 3px; 271 | border-style: solid; 272 | border-color: #cccccc; 273 | } 274 | table.table_list td.name { background-color: #f0f0f0; } 275 | table.table_list td.summary { width: 100%; } 276 | 277 | dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} 278 | dl.function dd {padding-bottom: 1em;} 279 | dl.function h3 {padding: 0; margin: 0; font-size: medium;} 280 | 281 | dl.table dt {border-top: 1px solid #ccc; padding-top: 1em;} 282 | dl.table dd {padding-bottom: 1em;} 283 | dl.table h3 {padding: 0; margin: 0; font-size: medium;} 284 | 285 | #TODO: make module_list, file_list, function_list, table_list inherit from a list 286 | 287 | -------------------------------------------------------------------------------- /doc/html/modules/ratchet.smtp.server.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | ratchet.smtp.server: Module Index 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 | 116 | 117 |
118 | 119 |

Module ratchet.smtp.server

120 | 121 |

This library provides server-side capabilities for an SMTP server. Currently the server will advertize the 8BITMIME, PIPELINING, and ENHANCEDSTATUSCODES extensions and is capable of handling arbitrary, custom commands as well. It is provided for convenience and as an example of using the ratchet libraries.

122 | 123 | 124 | 125 | 126 | 127 |

Functions

128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
__call (self)An alias to the handle() method, allowing the smtp.server object to be callable as if it were a function.
handle (self)Sends a banner and starts listening for and responding to SMTP commands from the client.
new (socket, handlers, tls_context, tls_immediately)Creates a new SMTP server object.
146 | 147 | 148 | 149 | 150 | 151 | 152 |
153 |
154 | 155 | 156 | 157 |

Functions

158 |
159 | 160 | 161 | 162 |
__call (self)
163 |
164 | An alias to the handle() method, allowing the smtp.server object to be callable as if it were a function. 165 | 166 | 167 |

Parameters

168 |
    169 | 170 |
  • 171 | self: the smtp.server object. 172 |
  • 173 | 174 |
175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 |
184 | 185 | 186 | 187 | 188 |
handle (self)
189 |
190 | Sends a banner and starts listening for and responding to SMTP commands from the client. This function continues until the client sends a QUIT command. 191 | 192 | 193 |

Parameters

194 |
    195 | 196 |
  • 197 | self: the smtp.server object. 198 |
  • 199 | 200 |
201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 |
210 | 211 | 212 | 213 | 214 |
new (socket, handlers, tls_context, tls_immediately)
215 |
216 | Creates a new SMTP server object. A new object should be created with this function for each socket created by accept()'ing a new connection on a listening socket. 217 | 218 | 219 |

Parameters

220 |
    221 | 222 |
  • 223 | socket: Used as the underlying socket for the session, under the assumption that no I/O has taken place, except possibly encryption if not using STARTTLS. 224 |
  • 225 | 226 |
  • 227 | handlers: Table whose keys correspond to supported SMTP commands. The command is called as a method of this table, e.g. handlers:MAIL(...), with the first argument being a reply table that the method can use to set the "code" and "message" keys to control the response. See the manual for more details. 228 |
  • 229 | 230 |
  • 231 | tls_context: If given as a ratchet.ssl context object, the STARTTLS extension will be enabled allowing clients to encrypt the session. If given as boolean true, it is assumed that the socket was previously encrypted and the session is secure. 232 |
  • 233 | 234 |
  • 235 | tls_immediately: If enabled, instead of providing the STARTTLS extension, the connection would be immediately encrypted with tls_context before sending the banner. 236 |
  • 237 | 238 |
239 | 240 | 241 | 242 | 243 | 244 | 245 |

Return value:

246 | a new smtp.server object. 247 | 248 | 249 | 250 |
251 | 252 | 253 |
254 | 255 | 256 | 257 | 258 | 259 | 260 |
261 | 262 |
263 | 264 |
265 |

Valid XHTML 1.0!

266 |
267 | 268 |
269 | 270 | 271 | --------------------------------------------------------------------------------