summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmil Renner Berthing <esmil@mailme.dk>2012-12-12 23:37:34 +0100
committerEmil Renner Berthing <esmil@mailme.dk>2012-12-17 10:11:06 +0100
commitccd4ec76f226acda8843f60d5230f7f8db876074 (patch)
tree4963f7dab5881c8e02e20f7719625492666deeda
parenta252badcc741bf4e30677cd746f7792106c80dcd (diff)
downloadlem-ccd4ec76f226acda8843f60d5230f7f8db876074.tar.gz
lem-ccd4ec76f226acda8843f60d5230f7f8db876074.tar.xz
lem-ccd4ec76f226acda8843f60d5230f7f8db876074.zip
io: lookup host names without blocking
-rw-r--r--lem/io/core.c2
-rw-r--r--lem/io/tcp.c184
-rwxr-xr-xtest/ctest.lua2
3 files changed, 112 insertions, 76 deletions
diff --git a/lem/io/core.c b/lem/io/core.c
index 237f6eb..c6dfaa2 100644
--- a/lem/io/core.c
+++ b/lem/io/core.c
@@ -362,7 +362,7 @@ luaopen_lem_io_core(lua_State *L)
lua_pushcclosure(L, stream_popen, 1);
lua_setfield(L, -2, "popen");
- /* insert the connect function */
+ /* insert the tcp_connect function */
lua_getfield(L, -1, "Stream"); /* upvalue 1 = Stream */
lua_pushcclosure(L, tcp_connect, 1);
lua_setfield(L, -2, "tcp_connect");
diff --git a/lem/io/tcp.c b/lem/io/tcp.c
index 23fea2a..1d0f911 100644
--- a/lem/io/tcp.c
+++ b/lem/io/tcp.c
@@ -16,25 +16,59 @@
* along with LEM. If not, see <http://www.gnu.org/licenses/>.
*/
-static void
-tcp_connect_cb(EV_P_ struct ev_io *w, int revents)
+struct tcp_getaddr {
+ struct lem_async a;
+ const char *node;
+ const char *service;
+ int sock;
+ int err;
+};
+
+static const int tcp_famnumber[] = { AF_UNSPEC, AF_INET, AF_INET6 };
+static const char *const tcp_famnames[] = { "any", "ipv4", "ipv6", NULL };
+
+static int
+tcp_connect_try(struct tcp_getaddr *g, struct addrinfo *addr)
{
- (void)revents;
+ int sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
- lem_debug("connection established");
- ev_io_stop(EV_A_ w);
- lem_queue(w->data, 1);
- w->data = NULL;
+ lem_debug("addr->ai_family = %d, sock = %d", addr->ai_family, sock);
+ if (sock < 0) {
+ int err = errno;
+
+ if (err == EAFNOSUPPORT || err == EPROTONOSUPPORT)
+ return 0;
+
+ g->sock = -2;
+ g->err = err;
+ return 1;
+ }
+
+ /* connect */
+ if (connect(sock, addr->ai_addr, addr->ai_addrlen)) {
+ close(sock);
+ return 0;
+ }
+
+ /* make the socket non-blocking */
+ if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
+ g->sock = -3;
+ g->err = errno;
+ close(sock);
+ return 1;
+ }
+
+ g->sock = sock;
+ return 1;
}
-static int
-tcp_connect(lua_State *T)
+static void
+tcp_connect_work(struct lem_async *a)
{
- const char *addr = luaL_checkstring(T, 1);
- uint16_t port = (uint16_t)luaL_checknumber(T, 2);
+ struct tcp_getaddr *g = (struct tcp_getaddr *)a;
struct addrinfo hints = {
.ai_flags = 0,
- .ai_family = AF_UNSPEC,
+ .ai_family = tcp_famnumber[g->sock],
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
.ai_addrlen = 0,
@@ -42,85 +76,87 @@ tcp_connect(lua_State *T)
.ai_canonname = NULL,
.ai_next = NULL
};
- struct addrinfo *ainfo;
- int sock;
+ struct addrinfo *result;
+ struct addrinfo *addr;
int ret;
- struct stream *s;
/* lookup name */
- ret = getaddrinfo(addr, NULL, &hints, &ainfo);
- if (ret != 0) {
- lua_pushnil(T);
- lua_pushfstring(T, "error looking up \"%s\": %s",
- addr, gai_strerror(ret));
- return 2;
+ ret = getaddrinfo(g->node, g->service, &hints, &result);
+ if (ret) {
+ g->sock = -1;
+ g->err = ret;
+ return;
}
/* create the TCP socket */
- switch (ainfo->ai_family) {
- case AF_INET:
- ((struct sockaddr_in *)ainfo->ai_addr)->sin_port = htons(port);
- sock = socket(PF_INET, ainfo->ai_socktype, ainfo->ai_protocol);
- break;
+ for (addr = result; addr; addr = addr->ai_next) {
+ if (tcp_connect_try(g, addr))
+ break;
+ }
- case AF_INET6:
- ((struct sockaddr_in6 *)ainfo->ai_addr)->sin6_port = htons(port);
- sock = socket(PF_INET6, ainfo->ai_socktype, ainfo->ai_protocol);
- break;
+ freeaddrinfo(result);
+ if (addr == NULL)
+ g->sock = -4;
+}
- default:
- freeaddrinfo(ainfo);
- lua_pushnil(T);
- lua_pushfstring(T, "getaddrinfo() returned neither "
- "IPv4 or IPv6 address for \"%s\"",
- addr);
- return 2;
- }
+static void
+tcp_connect_reap(struct lem_async *a)
+{
+ struct tcp_getaddr *g = (struct tcp_getaddr *)a;
+ lua_State *T = g->a.T;
+ const char *node = g->node;
+ const char *service = g->service;
+ int sock = g->sock;
+ int err = g->err;
- lem_debug("sock = %d", sock);
+ free(g);
- if (sock < 0) {
- freeaddrinfo(ainfo);
- lua_pushnil(T);
- lua_pushfstring(T, "error creating TCP socket: %s",
- strerror(errno));
- return 2;
+ lem_debug("connection established");
+ if (sock >= 0) {
+ stream_new(T, sock, 2);
+ lem_queue(T, 1);
+ return;
}
- /* make the socket non-blocking */
- if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
- close(sock);
- freeaddrinfo(ainfo);
- lua_pushnil(T);
+ lua_pushnil(T);
+ switch (-sock) {
+ case 1:
+ lua_pushfstring(T, "error looking up '%s:%s': %s",
+ node, service, gai_strerror(err));
+ break;
+ case 2:
+ lua_pushfstring(T, "error creating socket: %s",
+ strerror(err));
+ break;
+ case 3:
lua_pushfstring(T, "error making socket non-blocking: %s",
- strerror(errno));
- return 2;
+ strerror(err));
+ break;
+ case 4:
+ lua_pushfstring(T, "error connecting to '%s:%s'",
+ node, service);
+ break;
}
+ lem_queue(T, 2);
+}
- lua_settop(T, 1);
- s = stream_new(T, sock, lua_upvalueindex(1));
-
- /* connect */
- ret = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
- freeaddrinfo(ainfo);
- if (ret == 0) {
- lem_debug("connection established");
- return 2;
- }
+static int
+tcp_connect(lua_State *T)
+{
+ const char *node = luaL_checkstring(T, 1);
+ const char *service = luaL_checkstring(T, 2);
+ int family = luaL_checkoption(T, 3, "any", tcp_famnames);
+ struct tcp_getaddr *g;
- if (errno == EINPROGRESS) {
- lem_debug("EINPROGRESS");
- s->w.data = T;
- s->w.cb = tcp_connect_cb;
- ev_io_start(LEM_ &s->w);
- return lua_yield(T, 2);
- }
+ g = lem_xmalloc(sizeof(struct tcp_getaddr));
+ g->node = node;
+ g->service = service;
+ g->sock = family;
+ lem_async_do(&g->a, T, tcp_connect_work, tcp_connect_reap);
- close(sock);
- lua_pushnil(T);
- lua_pushfstring(T, "error connecting to %s:%d: %s",
- addr, (int)port, strerror(errno));
- return 2;
+ lua_settop(T, 1);
+ lua_pushvalue(T, lua_upvalueindex(1));
+ return lua_yield(T, 2);
}
static int
diff --git a/test/ctest.lua b/test/ctest.lua
index 399fd3a..ceebebe 100755
--- a/test/ctest.lua
+++ b/test/ctest.lua
@@ -22,7 +22,7 @@ print('Entered ' .. arg[0])
local utils = require 'lem.utils'
local io = require 'lem.io'
-local conn = assert(io.tcp_connect('127.0.0.1', 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'))