From c8d58798f0b0c789df5c566494112f81ac302432 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Sun, 18 Jan 2004 00:04:20 +0000 Subject: [PATCH] Trying to get connect-with-timeout to work. Darwin works... --- src/buffer.c | 12 ++-- src/http.lua | 8 ++- src/inet.c | 41 ++++++++---- src/inet.h | 8 ++- src/io.c | 35 +++++----- src/io.h | 6 +- src/socket.h | 7 +- src/tcp.c | 69 +++++++++---------- src/timeout.c | 4 +- src/timeout.h | 4 +- src/udp.c | 64 +++++++++++++----- src/usocket.c | 165 ++++++++++++++++++++++------------------------ test/httptest.lua | 3 +- test/testclnt.lua | 4 +- 14 files changed, 232 insertions(+), 198 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 04419e7..e6d4ce8 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -138,8 +138,7 @@ int sendraw(p_buf buf, const char *data, size_t count, size_t *sent) int err = IO_DONE; while (total < count && err == IO_DONE) { size_t done; - err = io->send(io->ctx, data+total, count-total, &done, - tm_getsuccess(tm)); + err = io->send(io->ctx, data+total, count-total, &done, tm_get(tm)); total += done; } *sent = total; @@ -156,7 +155,7 @@ int recvraw(lua_State *L, p_buf buf, size_t wanted) size_t total = 0; luaL_Buffer b; luaL_buffinit(L, &b); - while (total < wanted && err == IO_DONE) { + while (total < wanted && (err == IO_DONE || err == IO_RETRY)) { size_t count; const char *data; err = buf_get(buf, &data, &count); count = MIN(count, wanted - total); @@ -177,7 +176,7 @@ int recvall(lua_State *L, p_buf buf) int err = IO_DONE; luaL_Buffer b; luaL_buffinit(L, &b); - while (err == IO_DONE) { + while (err == IO_DONE || err == IO_RETRY) { const char *data; size_t count; err = buf_get(buf, &data, &count); luaL_addlstring(&b, data, count); @@ -197,7 +196,7 @@ int recvline(lua_State *L, p_buf buf) int err = IO_DONE; luaL_Buffer b; luaL_buffinit(L, &b); - while (err == IO_DONE) { + while (err == IO_DONE || err == IO_RETRY) { size_t count, pos; const char *data; err = buf_get(buf, &data, &count); pos = 0; @@ -240,8 +239,7 @@ int buf_get(p_buf buf, const char **data, size_t *count) p_tm tm = buf->tm; if (buf_isempty(buf)) { size_t got; - err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, - tm_getsuccess(tm)); + err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm_get(tm)); buf->first = 0; buf->last = got; } diff --git a/src/http.lua b/src/http.lua index 1925c68..2fc9e87 100644 --- a/src/http.lua +++ b/src/http.lua @@ -549,10 +549,16 @@ function request_cb(reqt, respt) reqt.headers = fill_headers(reqt.headers, parsed) -- try to connect to server local sock - sock, respt.error = socket.connect(parsed.host, parsed.port) + sock, respt.error = socket.tcp() if not sock then return respt end -- set connection timeout so that we do not hang forever sock:settimeout(TIMEOUT) + local ret + ret, respt.error = sock:connect(parsed.host, parsed.port) + if not ret then + sock:close() + return respt + end -- send request message respt.error = send_request(sock, reqt.method, request_uri(parsed), reqt.headers, reqt.body_cb) diff --git a/src/inet.c b/src/inet.c index 2334f53..80c488b 100644 --- a/src/inet.c +++ b/src/inet.c @@ -180,11 +180,11 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) /*-------------------------------------------------------------------------*\ * Tries to connect to remote address (address, port) \*-------------------------------------------------------------------------*/ -const char *inet_tryconnect(p_sock ps, const char *address, - unsigned short port, int timeout) +const char *inet_tryconnect(p_sock ps, p_tm tm, const char *address, + unsigned short port) { struct sockaddr_in remote; - const char *err; + int err; memset(&remote, 0, sizeof(remote)); remote.sin_family = AF_INET; remote.sin_port = htons(port); @@ -197,14 +197,14 @@ const char *inet_tryconnect(p_sock ps, const char *address, memcpy(&remote.sin_addr, *addr, sizeof(struct in_addr)); } } else remote.sin_family = AF_UNSPEC; - err = sock_connect(ps, (SA *) &remote, sizeof(remote), timeout); - if (err) { + do err = sock_connect(ps, (SA *) &remote, sizeof(remote), tm_getretry(tm)); + while (err == IO_RETRY && tm_getretry(tm)); + if (err != IO_DONE) { sock_destroy(ps); *ps = SOCK_INVALID; - return err; - } else { - return NULL; - } + if (err == IO_ERROR) return sock_connectstrerror(); + else return io_strerror(err); + } else return NULL; } /*-------------------------------------------------------------------------*\ @@ -214,7 +214,6 @@ const char *inet_trybind(p_sock ps, const char *address, unsigned short port, int backlog) { struct sockaddr_in local; - const char *err; memset(&local, 0, sizeof(local)); /* address is either wildcard or a valid ip address */ local.sin_addr.s_addr = htonl(INADDR_ANY); @@ -229,11 +228,10 @@ const char *inet_trybind(p_sock ps, const char *address, unsigned short port, memcpy(&local.sin_addr, *addr, sizeof(struct in_addr)); } sock_setblocking(ps); - err = sock_bind(ps, (SA *) &local, sizeof(local)); - if (err) { + if (sock_bind(ps, (SA *) &local, sizeof(local)) != IO_DONE) { sock_destroy(ps); *ps = SOCK_INVALID; - return err; + return sock_bindstrerror(); } else { sock_setnonblocking(ps); if (backlog > 0) sock_listen(ps, backlog); @@ -246,7 +244,22 @@ const char *inet_trybind(p_sock ps, const char *address, unsigned short port, \*-------------------------------------------------------------------------*/ const char *inet_trycreate(p_sock ps, int type) { - return sock_create(ps, AF_INET, type, 0); + if (sock_create(ps, AF_INET, type, 0) == IO_DONE) return NULL; + else return sock_createstrerror(); +} + +/*-------------------------------------------------------------------------*\ +* Tries to accept an inet socket +\*-------------------------------------------------------------------------*/ +const char *inet_tryaccept(p_sock ps, p_tm tm, p_sock pc) +{ + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + int err; + /* loop until connection accepted or timeout happens */ + do err = sock_accept(ps, pc, (SA *) &addr, &addr_len, tm_getretry(tm)); + while (err == IO_RETRY && tm_getretry(tm) != 0); + return io_strerror(err); } /*-------------------------------------------------------------------------*\ diff --git a/src/inet.h b/src/inet.h index 08979c1..ad52801 100644 --- a/src/inet.h +++ b/src/inet.h @@ -18,17 +18,21 @@ \*=========================================================================*/ #include #include "socket.h" +#include "timeout.h" #ifdef WIN32 #define INET_ATON #endif void inet_open(lua_State *L); -const char *inet_tryconnect(p_sock ps, const char *address, - unsigned short port, int timeout); + +const char *inet_tryconnect(p_sock ps, p_tm tm, const char *address, + unsigned short port); const char *inet_trybind(p_sock ps, const char *address, unsigned short port, int backlog); const char *inet_trycreate(p_sock ps, int type); +const char *inet_tryaccept(p_sock ps, p_tm tm, p_sock pc); + int inet_meth_getpeername(lua_State *L, p_sock ps); int inet_meth_getsockname(lua_State *L, p_sock ps); diff --git a/src/io.c b/src/io.c index 2d62147..612454b 100644 --- a/src/io.c +++ b/src/io.c @@ -22,27 +22,24 @@ void io_init(p_io io, p_send send, p_recv recv, void *ctx) /*-------------------------------------------------------------------------*\ * Translate error codes to Lua \*-------------------------------------------------------------------------*/ -void io_pusherror(lua_State *L, int code) +const char *io_strerror(int code) { switch (code) { - case IO_DONE: - lua_pushnil(L); - break; - case IO_TIMEOUT: - lua_pushstring(L, "timeout"); - break; - case IO_LIMITED: - lua_pushstring(L, "limited"); - break; - case IO_CLOSED: - lua_pushstring(L, "closed"); - break; - case IO_REFUSED: - lua_pushstring(L, "refused"); - break; - default: - lua_pushstring(L, "unknown error"); - break; + case IO_DONE: return NULL; + case IO_TIMEOUT: return "timeout"; + case IO_RETRY: return "retry"; + case IO_CLOSED: return "closed"; + case IO_REFUSED: return "refused"; + default: return "unknown error"; } } +/*-------------------------------------------------------------------------*\ +* Translate error codes to Lua +\*-------------------------------------------------------------------------*/ +void io_pusherror(lua_State *L, int code) +{ + const char *err = io_strerror(code); + if (err) lua_pushstring(L, err); + else lua_pushnil(L); +} diff --git a/src/io.h b/src/io.h index 33675d8..495bdc6 100644 --- a/src/io.h +++ b/src/io.h @@ -20,12 +20,11 @@ /* IO error codes */ enum { IO_DONE, /* operation completed successfully */ + IO_RETRY, /* please try again */ IO_TIMEOUT, /* operation timed out */ IO_CLOSED, /* the connection has been closed */ - IO_ERROR, /* something wrong... */ IO_REFUSED, /* transfer has been refused */ - IO_RETRY, /* please try again */ - IO_LIMITED /* maximum number of bytes reached */ + IO_ERROR /* something else wrong... */ }; /* interface to send function */ @@ -54,6 +53,7 @@ typedef struct t_io_ { } t_io; typedef t_io *p_io; +const char *io_strerror(int code); void io_pusherror(lua_State *L, int code); void io_init(p_io io, p_send send, p_recv recv, void *ctx); diff --git a/src/socket.h b/src/socket.h index aeadff0..739dedd 100644 --- a/src/socket.h +++ b/src/socket.h @@ -30,12 +30,12 @@ typedef struct sockaddr SA; * interface to sockets \*=========================================================================*/ int sock_open(void); -const char *sock_create(p_sock ps, int domain, int type, int protocol); +int sock_create(p_sock ps, int domain, int type, int protocol); void sock_destroy(p_sock ps); int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, int timeout); -const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, int timeout); -const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len); +int sock_connect(p_sock ps, SA *addr, socklen_t addr_len, int timeout); +int sock_bind(p_sock ps, SA *addr, socklen_t addr_len); void sock_listen(p_sock ps, int backlog); void sock_shutdown(p_sock ps, int how); int sock_send(p_sock ps, const char *data, size_t count, @@ -48,6 +48,7 @@ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got, SA *addr, socklen_t *addr_len, int timeout); void sock_setnonblocking(p_sock ps); void sock_setblocking(p_sock ps); + const char *sock_hoststrerror(void); const char *sock_createstrerror(void); const char *sock_bindstrerror(void); diff --git a/src/tcp.c b/src/tcp.c index fe09e8c..74f32f4 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -63,7 +63,7 @@ static luaL_reg tcp[] = { static luaL_reg opt[] = { {"keepalive", opt_keepalive}, {"reuseaddr", opt_reuseaddr}, - {"tcp-nodelay", opt_tcp_nodelay}, + {"tcp-nodelay", opt_tcp_nodelay}, {"linger", opt_linger}, {NULL, NULL} }; @@ -200,32 +200,26 @@ static int meth_dirty(lua_State *L) \*-------------------------------------------------------------------------*/ static int meth_accept(lua_State *L) { - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); - int err = IO_ERROR; p_tcp server = (p_tcp) aux_checkclass(L, "tcp{server}", 1); p_tm tm = &server->tm; - p_tcp client; t_sock sock; tm_markstart(tm); - /* loop until connection accepted or timeout happens */ - while (err != IO_DONE) { - err = sock_accept(&server->sock, &sock, - (SA *) &addr, &addr_len, tm_getfailure(tm)); - if (err == IO_CLOSED || (err == IO_TIMEOUT && !tm_getfailure(tm))) { - lua_pushnil(L); - io_pusherror(L, err); - return 2; - } + const char *err = inet_tryaccept(&server->sock, tm, &sock); + /* if successful, push client socket */ + if (!err) { + p_tcp clnt = lua_newuserdata(L, sizeof(t_tcp)); + aux_setclass(L, "tcp{client}", -1); + /* initialize structure fields */ + clnt->sock = sock; + io_init(&clnt->io, (p_send)sock_send, (p_recv)sock_recv, &clnt->sock); + tm_init(&clnt->tm, -1, -1); + buf_init(&clnt->buf, &clnt->io, &clnt->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; } - client = lua_newuserdata(L, sizeof(t_tcp)); - aux_setclass(L, "tcp{client}", -1); - client->sock = sock; - /* initialize remaining structure fields */ - io_init(&client->io, (p_send) sock_send, (p_recv) sock_recv, &client->sock); - tm_init(&client->tm, -1, -1); - buf_init(&client->buf, &client->io, &client->tm); - return 1; } /*-------------------------------------------------------------------------*\ @@ -260,7 +254,7 @@ static int meth_connect(lua_State *L) p_tm tm = &tcp->tm; const char *err; tm_markstart(tm); - err = inet_tryconnect(&tcp->sock, address, port, tm_getfailure(tm)); + err = inet_tryconnect(&tcp->sock, tm, address, port); if (err) { lua_pushnil(L); lua_pushstring(L, err); @@ -283,7 +277,7 @@ static int meth_close(lua_State *L) } /*-------------------------------------------------------------------------*\ -* Shuts the connection down +* Shuts the connection down partially \*-------------------------------------------------------------------------*/ static int meth_shutdown(lua_State *L) { @@ -341,22 +335,23 @@ static int meth_settimeout(lua_State *L) \*-------------------------------------------------------------------------*/ int global_create(lua_State *L) { - const char *err; - /* allocate tcp object */ - p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); - /* set its type as master object */ - aux_setclass(L, "tcp{master}", -1); + t_sock sock; + const char *err = inet_trycreate(&sock, SOCK_STREAM); /* try to allocate a system socket */ - err = inet_trycreate(&tcp->sock, SOCK_STREAM); - if (err) { /* get rid of object on stack and push error */ - lua_pop(L, 1); + if (!err) { + /* allocate tcp object */ + p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + tcp->sock = sock; + /* set its type as master object */ + aux_setclass(L, "tcp{master}", -1); + /* initialize remaining structure fields */ + io_init(&tcp->io, (p_send) sock_send, (p_recv) sock_recv, &tcp->sock); + tm_init(&tcp->tm, -1, -1); + buf_init(&tcp->buf, &tcp->io, &tcp->tm); + return 1; + } else { lua_pushnil(L); lua_pushstring(L, err); return 2; } - /* initialize remaining structure fields */ - io_init(&tcp->io, (p_send) sock_send, (p_recv) sock_recv, &tcp->sock); - tm_init(&tcp->tm, -1, -1); - buf_init(&tcp->buf, &tcp->io, &tcp->tm); - return 1; } diff --git a/src/timeout.c b/src/timeout.c index 2d88ded..0063378 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -54,7 +54,7 @@ void tm_init(p_tm tm, int block, int total) * Returns * the number of ms left or -1 if there is no time limit \*-------------------------------------------------------------------------*/ -int tm_getsuccess(p_tm tm) +int tm_get(p_tm tm) { if (tm->block < 0 && tm->total < 0) { return -1; @@ -89,7 +89,7 @@ int tm_getstart(p_tm tm) * Returns * the number of ms left or -1 if there is no time limit \*-------------------------------------------------------------------------*/ -int tm_getfailure(p_tm tm) +int tm_getretry(p_tm tm) { if (tm->block < 0 && tm->total < 0) { return -1; diff --git a/src/timeout.h b/src/timeout.h index 17f44ea..0a036f4 100644 --- a/src/timeout.h +++ b/src/timeout.h @@ -18,8 +18,8 @@ typedef t_tm *p_tm; void tm_open(lua_State *L); void tm_init(p_tm tm, int block, int total); -int tm_getsuccess(p_tm tm); -int tm_getfailure(p_tm tm); +int tm_get(p_tm tm); +int tm_getretry(p_tm tm); void tm_markstart(p_tm tm); int tm_getstart(p_tm tm); int tm_gettime(void); diff --git a/src/udp.c b/src/udp.c index eb2cea0..c730206 100644 --- a/src/udp.c +++ b/src/udp.c @@ -29,6 +29,7 @@ static int meth_getpeername(lua_State *L); static int meth_setsockname(lua_State *L); static int meth_setpeername(lua_State *L); static int meth_close(lua_State *L); +static int meth_shutdown(lua_State *L); static int meth_setoption(lua_State *L); static int meth_settimeout(lua_State *L); static int meth_fd(lua_State *L); @@ -53,6 +54,7 @@ static luaL_reg udp[] = { {"receivefrom", meth_receivefrom}, {"settimeout", meth_settimeout}, {"close", meth_close}, + {"shutdown", meth_shutdown}, {"setoption", meth_setoption}, {"__gc", meth_close}, {"fd", meth_fd}, @@ -110,7 +112,7 @@ static int meth_send(lua_State *L) int err; const char *data = luaL_checklstring(L, 2, &count); tm_markstart(tm); - err = sock_send(&udp->sock, data, count, &sent, tm_getsuccess(tm)); + err = sock_send(&udp->sock, data, count, &sent, tm_get(tm)); if (err == IO_DONE) lua_pushnumber(L, sent); else lua_pushnil(L); /* a 'closed' error on an unconnected means the target address was not @@ -139,7 +141,7 @@ static int meth_sendto(lua_State *L) addr.sin_port = htons(port); tm_markstart(tm); err = sock_sendto(&udp->sock, data, count, &sent, - (SA *) &addr, sizeof(addr), tm_getsuccess(tm)); + (SA *) &addr, sizeof(addr), tm_get(tm)); if (err == IO_DONE) lua_pushnumber(L, sent); else lua_pushnil(L); /* a 'closed' error on an unconnected means the target address was not @@ -160,7 +162,7 @@ static int meth_receive(lua_State *L) p_tm tm = &udp->tm; count = MIN(count, sizeof(buffer)); tm_markstart(tm); - err = sock_recv(&udp->sock, buffer, count, &got, tm_getsuccess(tm)); + err = sock_recv(&udp->sock, buffer, count, &got, tm_get(tm)); if (err == IO_DONE) lua_pushlstring(L, buffer, got); else lua_pushnil(L); io_pusherror(L, err); @@ -182,7 +184,7 @@ static int meth_receivefrom(lua_State *L) tm_markstart(tm); count = MIN(count, sizeof(buffer)); err = sock_recvfrom(&udp->sock, buffer, count, &got, - (SA *) &addr, &addr_len, tm_getsuccess(tm)); + (SA *) &addr, &addr_len, tm_get(tm)); if (err == IO_DONE) { lua_pushlstring(L, buffer, got); lua_pushstring(L, inet_ntoa(addr.sin_addr)); @@ -341,8 +343,7 @@ static int meth_setpeername(lua_State *L) unsigned short port = connecting ? (unsigned short) luaL_checknumber(L, 3) : (unsigned short) luaL_optnumber(L, 3, 0); - const char *err; - err = inet_tryconnect(&udp->sock, address, port, tm_getfailure(tm)); + const char *err = inet_tryconnect(&udp->sock, tm, address, port); if (err) { lua_pushnil(L); lua_pushstring(L, err); @@ -365,6 +366,33 @@ static int meth_close(lua_State *L) return 0; } +/*-------------------------------------------------------------------------*\ +* Shuts the connection down partially +\*-------------------------------------------------------------------------*/ +static int meth_shutdown(lua_State *L) +{ + p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); + const char *how = luaL_optstring(L, 2, "both"); + switch (how[0]) { + case 'b': + if (strcmp(how, "both")) goto error; + sock_shutdown(&udp->sock, 2); + break; + case 's': + if (strcmp(how, "send")) goto error; + sock_shutdown(&udp->sock, 1); + break; + case 'r': + if (strcmp(how, "receive")) goto error; + sock_shutdown(&udp->sock, 0); + break; + } + return 0; +error: + luaL_argerror(L, 2, "invalid shutdown method"); + return 0; +} + /*-------------------------------------------------------------------------*\ * Turns a master object into a server object \*-------------------------------------------------------------------------*/ @@ -391,21 +419,21 @@ static int meth_setsockname(lua_State *L) \*-------------------------------------------------------------------------*/ int global_create(lua_State *L) { - const char *err; - /* allocate udp object */ - p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); - /* set its type as master object */ - aux_setclass(L, "udp{unconnected}", -1); + t_sock sock; + const char *err = inet_trycreate(&sock, SOCK_DGRAM); /* try to allocate a system socket */ - err = inet_trycreate(&udp->sock, SOCK_DGRAM); - if (err) { - /* get rid of object on stack and push error */ - lua_pop(L, 1); + if (!err) { + /* allocate tcp object */ + p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); + udp->sock = sock; + /* set its type as master object */ + aux_setclass(L, "udp{unconnected}", -1); + /* initialize remaining structure fields */ + tm_init(&udp->tm, -1, -1); + return 1; + } else { lua_pushnil(L); lua_pushstring(L, err); return 2; } - /* initialize timeout management */ - tm_init(&udp->tm, -1, -1); - return 1; } diff --git a/src/usocket.c b/src/usocket.c index 9fcf4e8..acac852 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -46,50 +46,59 @@ void sock_destroy(p_sock ps) /*-------------------------------------------------------------------------*\ * Creates and sets up a socket \*-------------------------------------------------------------------------*/ -const char *sock_create(p_sock ps, int domain, int type, int protocol) +int sock_create(p_sock ps, int domain, int type, int protocol) { int val = 1; t_sock sock = socket(domain, type, protocol); - if (sock == SOCK_INVALID) return sock_createstrerror(); + if (sock == SOCK_INVALID) return IO_ERROR; *ps = sock; sock_setnonblocking(ps); setsockopt(*ps, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(val)); - return NULL; + return IO_DONE; } /*-------------------------------------------------------------------------*\ * Connects or returns error message \*-------------------------------------------------------------------------*/ -const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, int timeout) +int sock_connect(p_sock ps, SA *addr, socklen_t addr_len, int timeout) { t_sock sock = *ps; - if (sock == SOCK_INVALID) return "closed"; + if (sock == SOCK_INVALID) return IO_CLOSED; + /* if connect fails, we have to find out why */ if (connect(sock, addr, addr_len) < 0) { struct timeval tv; - fd_set wfds, efds; + fd_set rfds, efds, wfds; int err; + /* make sure the system is trying to connect */ + if (errno != EINPROGRESS) return IO_ERROR; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; - FD_ZERO(&wfds); FD_ZERO(&efds); - FD_SET(sock, &wfds); FD_SET(sock, &efds); - do err = select(sock+1, NULL, &wfds, &efds, timeout >= 0 ? &tv : NULL); - while (err < 0 && errno == EINTR); - if (err <= 0) return "timeout"; - if (FD_ISSET(sock, &efds)) { + FD_ZERO(&rfds); FD_SET(sock, &rfds); + FD_ZERO(&wfds); FD_SET(sock, &wfds); + FD_ZERO(&efds); FD_SET(sock, &efds); + /* we run select to avoid busy waiting */ + err = select(sock+1, &rfds, &wfds, &efds, timeout >= 0? &tv: NULL); + /* if select was interrupted, ask the user to retry */ + if (err < 0 && errno == EINTR) return IO_RETRY; + /* if selects readable, try reading */ + if (err > 0) { char dummy; - recv(sock, &dummy, 0, 0); - return sock_connectstrerror(); - } else return NULL; - } else return NULL; + /* try reading so that errno is set */ + if (recv(sock, &dummy, 0, 0) < 0) return IO_ERROR; + return IO_DONE; + /* if no event happened, there was a timeout */ + } else return IO_TIMEOUT; + /* otherwise connection succeeded */ + } else return IO_DONE; } /*-------------------------------------------------------------------------*\ * Binds or returns error message \*-------------------------------------------------------------------------*/ -const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len) +int sock_bind(p_sock ps, SA *addr, socklen_t addr_len) { - if (bind(*ps, addr, addr_len) < 0) return sock_bindstrerror(); - else return NULL; + if (bind(*ps, addr, addr_len) < 0) return IO_ERROR; + else return IO_DONE; } /*-------------------------------------------------------------------------*\ @@ -115,25 +124,24 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, int timeout) { t_sock sock = *ps; - struct timeval tv; SA dummy_addr; socklen_t dummy_len; - fd_set fds; - int err; if (sock == SOCK_INVALID) return IO_CLOSED; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - FD_ZERO(&fds); - FD_SET(sock, &fds); - *pa = SOCK_INVALID; - do err = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); - while (err < 0 && errno == EINTR); - if (err <= 0) return IO_TIMEOUT; if (!addr) addr = &dummy_addr; if (!addr_len) addr_len = &dummy_len; *pa = accept(sock, addr, addr_len); - if (*pa == SOCK_INVALID) return IO_ERROR; - else return IO_DONE; + if (*pa == SOCK_INVALID) { + struct timeval tv; + fd_set fds; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + FD_ZERO(&fds); + FD_SET(sock, &fds); + /* just call select to avoid busy-wait. doesn't really matter + * what happens. the caller will choose to retry or not */ + select(sock+1, &fds, NULL, NULL, timeout >= 0? &tv: NULL); + return IO_RETRY; + } else return IO_DONE; } /*-------------------------------------------------------------------------*\ @@ -144,32 +152,31 @@ int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, { t_sock sock = *ps; ssize_t put; - int ret; /* avoid making system calls on closed sockets */ if (sock == SOCK_INVALID) return IO_CLOSED; /* make sure we repeat in case the call was interrupted */ - do put = write(sock, data, count); - while (put <= 0 && errno == EINTR); + do put = send(sock, data, count, 0); + while (put < 0 && errno == EINTR); /* deal with failure */ if (put <= 0) { + struct timeval tv; + fd_set fds; /* in any case, nothing has been sent */ *sent = 0; - /* run select to avoid busy wait */ - if (errno != EPIPE) { - struct timeval tv; - fd_set fds; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - FD_ZERO(&fds); - FD_SET(sock, &fds); - do ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ?&tv : NULL); - while (ret < 0 && errno == EINTR); - /* tell the caller to call us again because there is more data */ - if (ret > 0) return IO_DONE; - /* tell the caller there was no data before timeout */ - else return IO_TIMEOUT; /* here we know the connection has been closed */ - } else return IO_CLOSED; + if (errno == EPIPE) return IO_CLOSED; + /* run select to avoid busy wait */ + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + FD_ZERO(&fds); + FD_SET(sock, &fds); + if (select(sock+1, NULL, &fds, NULL, timeout >= 0? &tv: NULL) <= 0) { + /* here the call was interrupted. calling again might work */ + if (errno == EINTR) return IO_RETRY; + /* here there was no data before timeout */ + else return IO_TIMEOUT; + /* here we didn't send anything, but now we can */ + } else return IO_DONE; /* here we successfully sent something */ } else { *sent = put; @@ -185,33 +192,22 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent, { t_sock sock = *ps; ssize_t put; - int ret; - /* avoid making system calls on closed sockets */ if (sock == SOCK_INVALID) return IO_CLOSED; - /* make sure we repeat in case the call was interrupted */ do put = sendto(sock, data, count, 0, addr, addr_len); - while (put <= 0 && errno == EINTR); - /* deal with failure */ + while (put < 0 && errno == EINTR); if (put <= 0) { - /* in any case, nothing has been sent */ + struct timeval tv; + fd_set fds; *sent = 0; - /* run select to avoid busy wait */ - if (errno != EPIPE) { - struct timeval tv; - fd_set fds; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - FD_ZERO(&fds); - FD_SET(sock, &fds); - do ret = select(sock+1, NULL, &fds, NULL, timeout >= 0? &tv: NULL); - while (ret < 0 && errno == EINTR); - /* tell the caller to call us again because there is more data */ - if (ret > 0) return IO_DONE; - /* tell the caller there was no data before timeout */ + if (errno == EPIPE) return IO_CLOSED; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + FD_ZERO(&fds); + FD_SET(sock, &fds); + if (select(sock+1, NULL, &fds, NULL, timeout >= 0? &tv: NULL) <= 0) { + if (errno == EINTR) return IO_RETRY; else return IO_TIMEOUT; - /* here we know the connection has been closed */ - } else return IO_CLOSED; - /* here we successfully sent something */ + } else return IO_DONE; } else { *sent = put; return IO_DONE; @@ -220,11 +216,6 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent, /*-------------------------------------------------------------------------*\ * Receive with timeout -* Here we exchanged the order of the calls to write and select -* The idea is that the outer loop (whoever is calling sock_send) -* will call the function again if we didn't time out, so we can -* call write and then select only if it fails. -* Should speed things up! \*-------------------------------------------------------------------------*/ int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) { @@ -232,7 +223,7 @@ int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) ssize_t taken; if (sock == SOCK_INVALID) return IO_CLOSED; do taken = read(sock, data, count); - while (taken <= 0 && errno == EINTR); + while (taken < 0 && errno == EINTR); if (taken <= 0) { struct timeval tv; fd_set fds; @@ -243,10 +234,10 @@ int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) tv.tv_usec = (timeout % 1000) * 1000; FD_ZERO(&fds); FD_SET(sock, &fds); - do ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); - while (ret < 0 && errno == EINTR); - if (ret > 0) return IO_DONE; - else return IO_TIMEOUT; + ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); + if (ret < 0 && errno == EINTR) return IO_RETRY; + if (ret == 0) return IO_TIMEOUT; + else return IO_DONE; } else { *got = taken; return IO_DONE; @@ -263,7 +254,7 @@ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got, ssize_t taken; if (sock == SOCK_INVALID) return IO_CLOSED; do taken = recvfrom(sock, data, count, 0, addr, addr_len); - while (taken <= 0 && errno == EINTR); + while (taken < 0 && errno == EINTR); if (taken <= 0) { struct timeval tv; fd_set fds; @@ -274,10 +265,10 @@ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got, tv.tv_usec = (timeout % 1000) * 1000; FD_ZERO(&fds); FD_SET(sock, &fds); - do ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); - while (ret < 0 && errno == EINTR); - if (ret > 0) return IO_DONE; - else return IO_TIMEOUT; + ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); + if (ret < 0 && errno == EINTR) return IO_RETRY; + if (ret == 0) return IO_TIMEOUT; + else return IO_DONE; } else { *got = taken; return IO_DONE; diff --git a/test/httptest.lua b/test/httptest.lua index 9d9fa25..d3a4dc0 100644 --- a/test/httptest.lua +++ b/test/httptest.lua @@ -314,9 +314,10 @@ body = socket.http.get { check(body == index) io.write("testing HEAD method: ") +socket.http.TIMEOUT = 1 response = socket.http.request { method = "HEAD", - url = "http://www.tecgraf.puc-rio.br/~diego/" + url = "http://www.cs.princeton.edu/~diego/" } check(response and response.headers) diff --git a/test/testclnt.lua b/test/testclnt.lua index 6b07dca..3dea831 100644 --- a/test/testclnt.lua +++ b/test/testclnt.lua @@ -359,6 +359,7 @@ test_methods(socket.tcp(), { "getsockname", "setoption", "settimeout", + "shutdown", "close", }) test_methods(socket.udp(), { @@ -372,6 +373,7 @@ test_methods(socket.udp(), { "receivefrom", "setoption", "settimeout", + "shutdown", "close", }) @@ -484,6 +486,4 @@ test_blockingtimeoutreceive(800091, 3, 2) test_blockingtimeoutreceive(800091, 3, 1) ]] -socket.done() - test(string.format("done in %.2fs", socket.time() - start))