diff options
author | Emil Renner Berthing <esmil@mailme.dk> | 2013-01-04 00:01:03 +0100 |
---|---|---|
committer | Emil Renner Berthing <esmil@mailme.dk> | 2013-01-04 21:14:27 +0100 |
commit | 62c631fd7d5d5c66694af9266688851ac442508a (patch) | |
tree | 79832de2b0c24b4b9a3c369cde8a99a6b98de6cf | |
parent | 2c7077309341aad9f831ae8cf356899a18ee4e3b (diff) | |
download | lem-62c631fd7d5d5c66694af9266688851ac442508a.tar.gz lem-62c631fd7d5d5c66694af9266688851ac442508a.tar.xz lem-62c631fd7d5d5c66694af9266688851ac442508a.zip |
io/tcp: listen() and connect() return host and port
-rw-r--r-- | lem/io/tcp.c | 172 | ||||
-rwxr-xr-x | test/httptest.lua | 28 |
2 files changed, 114 insertions, 86 deletions
diff --git a/lem/io/tcp.c b/lem/io/tcp.c index d15eb24..2a209b2 100644 --- a/lem/io/tcp.c +++ b/lem/io/tcp.c @@ -20,54 +20,21 @@ struct tcp_getaddr { struct lem_async a; const char *node; const char *service; + struct addrinfo *result; int sock; int err; + uint16_t port; }; 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) -{ - int sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); - - 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 void tcp_connect_work(struct lem_async *a) { struct tcp_getaddr *g = (struct tcp_getaddr *)a; struct addrinfo hints = { - .ai_flags = 0, + .ai_flags = AI_CANONNAME, .ai_family = tcp_famnumber[g->sock], .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_TCP, @@ -76,27 +43,64 @@ tcp_connect_work(struct lem_async *a) .ai_canonname = NULL, .ai_next = NULL }; - struct addrinfo *result; struct addrinfo *addr; - int ret; + int sock; /* lookup name */ - ret = getaddrinfo(g->node, g->service, &hints, &result); - if (ret) { + sock = getaddrinfo(g->node, g->service, &hints, &g->result); + if (sock) { g->sock = -1; - g->err = ret; + g->err = sock; return; } - /* create the TCP socket */ - for (addr = result; addr; addr = addr->ai_next) { - if (tcp_connect_try(g, addr)) - break; + /* try the addresses in the order returned */ + for (addr = g->result; addr; addr = addr->ai_next) { + uint16_t port; + + sock = socket(addr->ai_family, + addr->ai_socktype, addr->ai_protocol); + + lem_debug("addr->ai_family = %d, sock = %d", addr->ai_family, sock); + if (sock < 0) { + int err = errno; + + if (err == EAFNOSUPPORT || err == EPROTONOSUPPORT) + continue; + + g->sock = -2; + g->err = err; + goto error; + } + + /* connect */ + if (connect(sock, addr->ai_addr, addr->ai_addrlen)) { + (void)close(sock); + continue; + } + + /* make the socket non-blocking */ + if (fcntl(sock, F_SETFL, O_NONBLOCK)) { + g->sock = -3; + g->err = errno; + goto error; + } + + g->sock = sock; + if (addr->ai_family == AF_INET6) + port = ((struct sockaddr_in6 *)addr->ai_addr)->sin6_port; + else + port = ((struct sockaddr_in *)addr->ai_addr)->sin_port; + g->port = ntohs(port); + return; } - freeaddrinfo(result); - if (addr == NULL) - g->sock = -4; + g->sock = -4; + +error: + freeaddrinfo(g->result); + if (sock >= 0) + close(sock); } static void @@ -104,17 +108,24 @@ 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; - - free(g); lem_debug("connection established"); if (sock >= 0) { + struct addrinfo *result = g->result; + const char *name = result->ai_canonname; + uint16_t port = g->port; + + if (name == NULL) + name = g->node; + + free(g); + stream_new(T, sock, 3); - lem_queue(T, 1); + lua_pushstring(T, name); + lua_pushnumber(T, port); + freeaddrinfo(result); + lem_queue(T, 3); return; } @@ -122,22 +133,23 @@ tcp_connect_reap(struct lem_async *a) switch (-sock) { case 1: lua_pushfstring(T, "error looking up '%s:%s': %s", - node, service, gai_strerror(err)); + g->node, g->service, gai_strerror(g->err)); break; case 2: lua_pushfstring(T, "error creating socket: %s", - strerror(err)); + strerror(g->err)); break; case 3: lua_pushfstring(T, "error making socket non-blocking: %s", - strerror(err)); + strerror(g->err)); break; case 4: lua_pushfstring(T, "error connecting to '%s:%s'", - node, service); + g->node, g->service); break; } lem_queue(T, 2); + free(g); } static int @@ -164,7 +176,7 @@ tcp_listen_work(struct lem_async *a) { struct tcp_getaddr *g = (struct tcp_getaddr *)a; struct addrinfo hints = { - .ai_flags = AI_PASSIVE, + .ai_flags = AI_CANONNAME | AI_PASSIVE, .ai_family = tcp_famnumber[g->sock], .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_TCP, @@ -176,13 +188,14 @@ tcp_listen_work(struct lem_async *a) struct addrinfo *addr = NULL; int sock = -1; int ret; + uint16_t port; /* lookup name */ ret = getaddrinfo(g->node, g->service, &hints, &addr); if (ret) { g->sock = -1; g->err = ret; - goto error; + return; } /* create the TCP socket */ @@ -204,8 +217,6 @@ tcp_listen_work(struct lem_async *a) g->err = errno; goto error; } - freeaddrinfo(addr); - addr = NULL; /* listen to the socket */ if (listen(sock, g->err)) { @@ -222,11 +233,16 @@ tcp_listen_work(struct lem_async *a) } g->sock = sock; + g->result = addr; + if (addr->ai_family == AF_INET6) + port = ((struct sockaddr_in6 *)addr->ai_addr)->sin6_port; + else + port = ((struct sockaddr_in *)addr->ai_addr)->sin_port; + g->port = ntohs(port); return; error: - if (addr != NULL) - freeaddrinfo(addr); + freeaddrinfo(addr); if (sock >= 0) close(sock); } @@ -236,16 +252,20 @@ tcp_listen_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; - free(g); if (sock >= 0) { + struct addrinfo *result = g->result; + const char *name = result->ai_canonname; + uint16_t port = g->port; struct ev_io *w; + if (name == NULL) + name = g->node; + + free(g); + /* create userdata and set the metatable */ w = lua_newuserdata(T, sizeof(struct ev_io)); lua_pushvalue(T, 3); @@ -254,7 +274,11 @@ tcp_listen_reap(struct lem_async *a) /* initialize userdata */ ev_io_init(w, NULL, sock, EV_READ); w->data = NULL; - lem_queue(T, 1); + + lua_pushstring(T, name); + lua_pushnumber(T, port); + freeaddrinfo(result); + lem_queue(T, 3); return; } @@ -262,24 +286,24 @@ tcp_listen_reap(struct lem_async *a) switch (-sock) { case 1: lua_pushfstring(T, "error looking up '%s:%s': %s", - node, service, gai_strerror(err)); + g->node, g->service, gai_strerror(g->err)); break; case 2: lua_pushfstring(T, "error creating socket: %s", - strerror(err)); + strerror(g->err)); break; case 3: lua_pushfstring(T, "error binding to '%s:%s': %s", - node, service, strerror(err)); + g->node, g->service, strerror(g->err)); break; case 4: lua_pushfstring(T, "error listening on '%s:%s': %s", - node, service, strerror(err)); + g->node, g->service, strerror(g->err)); break; case 5: lua_pushfstring(T, "error making socket non-blocking: %s", - strerror(err)); + strerror(g->err)); break; } lem_queue(T, 2); diff --git a/test/httptest.lua b/test/httptest.lua index 5642c0b..9ef07aa 100755 --- a/test/httptest.lua +++ b/test/httptest.lua @@ -24,36 +24,40 @@ local utils = require 'lem.utils' local io = require 'lem.io' local http = require 'lem.http' -local format = string.format -local concat = table.concat +local write, format = io.write, string.format +local function printf(...) + return write(format(...)) +end +local domain, port = 'www.google.com', 'http' +--local domain, port = 'localhost', '8080' local done = false utils.spawn(function() - local conn = assert(io.tcp.connect('www.google.dk', 80)) - --local conn = assert(io.tcp.connect('127.0.0.1', 8080)) + local conn, name, port = assert(io.tcp.connect(domain, port)) - print('\nConnected.') + printf('\nConnected to %s:%u\n', name, port) for i = 1, 2 do - --assert(conn:write('GET / HTTP/1.1\r\nHost: www.google.dk\r\nConnection: close\r\n\r\n')) - assert(conn:write('GET / HTTP/1.1\r\nHost: www.google.dk\r\n\r\n')) + --assert(conn:write('GET / HTTP/1.1\r\nHost: '..domain..'\r\nConnection: close\r\n\r\n')) + assert(conn:write('GET / HTTP/1.1\r\nHost: '..domain..'\r\n\r\n')) local res = assert(conn:read('HTTPResponse')) - print(format('\nHTTP/%s %d %s', res.version, res.status, res.text)) + printf('\nHTTP/%s %d %s\n', res.version, res.status, res.text) for k, v in pairs(res.headers) do - print(format('%s: %s', k, v)) + printf('%s: %s\n', k, v) end - --print(format('\n#body = %d', #assert(conn:read('*a')))) - print(format('\n#body = %d', #assert(res:body()))) + local body = assert(res:body()) + printf('\n#body = %d\n', #body) + --write(body, '\n') end done = true end) -local write, yield = io.write, utils.yield +local yield = utils.yield local sleeper = utils.newsleeper() repeat write('.') |