summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmil Renner Berthing <esmil@mailme.dk>2013-01-05 22:26:36 +0100
committerEmil Renner Berthing <esmil@mailme.dk>2013-01-06 20:46:37 +0100
commit72400068a26edb6f2d88f62a70d2023d2a19a03c (patch)
tree44f6834f1b25095f2a051f2383a31d86cc6c2c6e
parent62c631fd7d5d5c66694af9266688851ac442508a (diff)
downloadlem-72400068a26edb6f2d88f62a70d2023d2a19a03c.tar.gz
lem-72400068a26edb6f2d88f62a70d2023d2a19a03c.tar.xz
lem-72400068a26edb6f2d88f62a70d2023d2a19a03c.zip
io/tcp: add MultiServer wrapper
..to easily liston on both IPv4 and IPv6
-rw-r--r--lem/io.lua69
-rw-r--r--lem/io/core.c10
-rw-r--r--lem/io/tcp.c32
-rwxr-xr-xtest/ctest.lua2
-rwxr-xr-xtest/stest.lua2
5 files changed, 103 insertions, 12 deletions
diff --git a/lem/io.lua b/lem/io.lua
index 7efccf0..4f7d31d 100644
--- a/lem/io.lua
+++ b/lem/io.lua
@@ -16,7 +16,8 @@
-- along with LEM. If not, see <http://www.gnu.org/licenses/>.
--
-local io = require 'lem.io.core'
+local utils = require 'lem.utils'
+local io = require 'lem.io.core'
local type = type
local assert = assert
@@ -88,6 +89,72 @@ do
end
end
+do
+ local MultiServer = {}
+ MultiServer.__index = MultiServer
+ io.MultiServer = MultiServer
+
+ function MultiServer:interrupt()
+ return self[1]:interrupt()
+ end
+
+ function MultiServer:close()
+ local rok, rerr = true
+ for i = 1, #self do
+ local ok, err = self[i]:close()
+ if not ok then
+ rok, rerr = ok, err
+ end
+ end
+ return rok, rerr
+ end
+
+
+ local function autospawn(self, i, handler)
+ local ok, err = self[i]:autospawn(handler)
+ if self.running then
+ self.running, self.ok, self.err = false, ok, err
+ end
+ for i = 1, #self do
+ self[i]:interrupt()
+ end
+ end
+
+ local spawn = utils.spawn
+
+ function MultiServer:autospawn(handler)
+ local n = #self
+
+ self.running = true
+ for i = 1, n-1 do
+ spawn(autospawn, self, i, handler)
+ end
+ autospawn(self, n, handler)
+
+ return self.ok, self.err
+ end
+
+ local setmetatable = setmetatable
+ local listen4, listen6 = io.tcp.listen4, io.tcp.listen6
+
+ function io.tcp.listen(host, port)
+ if host:match(':') then
+ return listen6(host, port)
+ end
+
+ local s6, err = listen6(host, port)
+ if s6 then
+ local s4 = listen4(host, port)
+ if s4 then
+ return setmetatable({ s6, s4 }, MultiServer)
+ end
+ return s6
+ else
+ return listen4(host, port)
+ end
+ end
+end
+
return io
-- vim: ts=2 sw=2 noet:
diff --git a/lem/io/core.c b/lem/io/core.c
index dffbe5c..83ac5bf 100644
--- a/lem/io/core.c
+++ b/lem/io/core.c
@@ -351,10 +351,14 @@ luaopen_lem_io_core(lua_State *L)
lua_getfield(L, -2, "Stream"); /* upvalue 1 = Stream */
lua_pushcclosure(L, tcp_connect, 1);
lua_setfield(L, -2, "connect");
- /* insert the listen function */
+ /* insert the listen4 function */
lua_getfield(L, -2, "Server"); /* upvalue 1 = Server */
- lua_pushcclosure(L, tcp_listen, 1);
- lua_setfield(L, -2, "listen");
+ lua_pushcclosure(L, tcp_listen4, 1);
+ lua_setfield(L, -2, "listen4");
+ /* insert the listen6 function */
+ lua_getfield(L, -2, "Server"); /* upvalue 1 = Server */
+ lua_pushcclosure(L, tcp_listen6, 1);
+ lua_setfield(L, -2, "listen6");
/* insert the tcp table */
lua_setfield(L, -2, "tcp");
diff --git a/lem/io/tcp.c b/lem/io/tcp.c
index 2a209b2..481563f 100644
--- a/lem/io/tcp.c
+++ b/lem/io/tcp.c
@@ -176,8 +176,8 @@ tcp_listen_work(struct lem_async *a)
{
struct tcp_getaddr *g = (struct tcp_getaddr *)a;
struct addrinfo hints = {
- .ai_flags = AI_CANONNAME | AI_PASSIVE,
- .ai_family = tcp_famnumber[g->sock],
+ .ai_flags = AI_PASSIVE,
+ .ai_family = g->sock,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
.ai_addrlen = 0,
@@ -190,6 +190,9 @@ tcp_listen_work(struct lem_async *a)
int ret;
uint16_t port;
+ if (g->node != NULL)
+ hints.ai_flags |= AI_CANONNAME;
+
/* lookup name */
ret = getaddrinfo(g->node, g->service, &hints, &addr);
if (ret) {
@@ -210,6 +213,10 @@ tcp_listen_work(struct lem_async *a)
/* set SO_REUSEADDR option if possible */
ret = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &ret, sizeof(int));
+#ifdef IPV6_V6ONLY
+ if (g->sock == AF_INET6)
+ setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &ret, sizeof(int));
+#endif
/* bind */
if (bind(sock, addr->ai_addr, addr->ai_addrlen)) {
@@ -254,6 +261,8 @@ tcp_listen_reap(struct lem_async *a)
lua_State *T = g->a.T;
int sock = g->sock;
+ if (g->node == NULL)
+ g->node = "*";
if (sock >= 0) {
struct addrinfo *result = g->result;
@@ -310,16 +319,15 @@ tcp_listen_reap(struct lem_async *a)
}
static int
-tcp_listen(lua_State *T)
+tcp_listen(lua_State *T, int family)
{
const char *node = luaL_checkstring(T, 1);
const char *service = luaL_checkstring(T, 2);
- int family = luaL_checkoption(T, 3, "any", tcp_famnames);
- int backlog = (int)luaL_optnumber(T, 4, MAXPENDING);
+ int backlog = (int)luaL_optnumber(T, 3, MAXPENDING);
struct tcp_getaddr *g;
if (node[0] == '*' && node[1] == '\0')
- node = "0.0.0.0";
+ node = NULL;
g = lem_xmalloc(sizeof(struct tcp_getaddr));
g->node = node;
@@ -332,3 +340,15 @@ tcp_listen(lua_State *T)
lua_pushvalue(T, lua_upvalueindex(1));
return lua_yield(T, 3);
}
+
+static int
+tcp_listen4(lua_State *T)
+{
+ return tcp_listen(T, AF_INET);
+}
+
+static int
+tcp_listen6(lua_State *T)
+{
+ return tcp_listen(T, AF_INET6);
+}
diff --git a/test/ctest.lua b/test/ctest.lua
index adf117e..c49d677 100755
--- a/test/ctest.lua
+++ b/test/ctest.lua
@@ -25,7 +25,7 @@ package.cpath = '?.so'
local utils = require 'lem.utils'
local io = require 'lem.io'
-local conn = assert(io.tcp.connect('localhost', arg[1] or 8080))
+local conn = assert(io.tcp.connect('localhost', arg[1] or '8080'))
for i = 1, 10 do
assert(conn:write('ping\n'))
diff --git a/test/stest.lua b/test/stest.lua
index 78c3406..82b432a 100755
--- a/test/stest.lua
+++ b/test/stest.lua
@@ -25,7 +25,7 @@ package.cpath = '?.so'
local utils = require 'lem.utils'
local io = require 'lem.io'
-local server = assert(io.tcp.listen('*', arg[1] or 8080))
+local server = assert(io.tcp.listen('*', arg[1] or '8080'))
--timer(10, function() exit(0) end)
utils.spawn(function()