Trying to get connect-with-timeout to work. Darwin works...

This commit is contained in:
Diego Nehab 2004-01-18 00:04:20 +00:00
parent 02ef4e7daa
commit c8d58798f0
14 changed files with 232 additions and 198 deletions

View File

@ -138,8 +138,7 @@ int sendraw(p_buf buf, const char *data, size_t count, size_t *sent)
int err = IO_DONE; int err = IO_DONE;
while (total < count && err == IO_DONE) { while (total < count && err == IO_DONE) {
size_t done; size_t done;
err = io->send(io->ctx, data+total, count-total, &done, err = io->send(io->ctx, data+total, count-total, &done, tm_get(tm));
tm_getsuccess(tm));
total += done; total += done;
} }
*sent = total; *sent = total;
@ -156,7 +155,7 @@ int recvraw(lua_State *L, p_buf buf, size_t wanted)
size_t total = 0; size_t total = 0;
luaL_Buffer b; luaL_Buffer b;
luaL_buffinit(L, &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; size_t count; const char *data;
err = buf_get(buf, &data, &count); err = buf_get(buf, &data, &count);
count = MIN(count, wanted - total); count = MIN(count, wanted - total);
@ -177,7 +176,7 @@ int recvall(lua_State *L, p_buf buf)
int err = IO_DONE; int err = IO_DONE;
luaL_Buffer b; luaL_Buffer b;
luaL_buffinit(L, &b); luaL_buffinit(L, &b);
while (err == IO_DONE) { while (err == IO_DONE || err == IO_RETRY) {
const char *data; size_t count; const char *data; size_t count;
err = buf_get(buf, &data, &count); err = buf_get(buf, &data, &count);
luaL_addlstring(&b, data, count); luaL_addlstring(&b, data, count);
@ -197,7 +196,7 @@ int recvline(lua_State *L, p_buf buf)
int err = IO_DONE; int err = IO_DONE;
luaL_Buffer b; luaL_Buffer b;
luaL_buffinit(L, &b); luaL_buffinit(L, &b);
while (err == IO_DONE) { while (err == IO_DONE || err == IO_RETRY) {
size_t count, pos; const char *data; size_t count, pos; const char *data;
err = buf_get(buf, &data, &count); err = buf_get(buf, &data, &count);
pos = 0; pos = 0;
@ -240,8 +239,7 @@ int buf_get(p_buf buf, const char **data, size_t *count)
p_tm tm = buf->tm; p_tm tm = buf->tm;
if (buf_isempty(buf)) { if (buf_isempty(buf)) {
size_t got; size_t got;
err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm_get(tm));
tm_getsuccess(tm));
buf->first = 0; buf->first = 0;
buf->last = got; buf->last = got;
} }

View File

@ -549,10 +549,16 @@ function request_cb(reqt, respt)
reqt.headers = fill_headers(reqt.headers, parsed) reqt.headers = fill_headers(reqt.headers, parsed)
-- try to connect to server -- try to connect to server
local sock local sock
sock, respt.error = socket.connect(parsed.host, parsed.port) sock, respt.error = socket.tcp()
if not sock then return respt end if not sock then return respt end
-- set connection timeout so that we do not hang forever -- set connection timeout so that we do not hang forever
sock:settimeout(TIMEOUT) 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 -- send request message
respt.error = send_request(sock, reqt.method, respt.error = send_request(sock, reqt.method,
request_uri(parsed), reqt.headers, reqt.body_cb) request_uri(parsed), reqt.headers, reqt.body_cb)

View File

@ -180,11 +180,11 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp)
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Tries to connect to remote address (address, port) * Tries to connect to remote address (address, port)
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
const char *inet_tryconnect(p_sock ps, const char *address, const char *inet_tryconnect(p_sock ps, p_tm tm, const char *address,
unsigned short port, int timeout) unsigned short port)
{ {
struct sockaddr_in remote; struct sockaddr_in remote;
const char *err; int err;
memset(&remote, 0, sizeof(remote)); memset(&remote, 0, sizeof(remote));
remote.sin_family = AF_INET; remote.sin_family = AF_INET;
remote.sin_port = htons(port); 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)); memcpy(&remote.sin_addr, *addr, sizeof(struct in_addr));
} }
} else remote.sin_family = AF_UNSPEC; } else remote.sin_family = AF_UNSPEC;
err = sock_connect(ps, (SA *) &remote, sizeof(remote), timeout); do err = sock_connect(ps, (SA *) &remote, sizeof(remote), tm_getretry(tm));
if (err) { while (err == IO_RETRY && tm_getretry(tm));
if (err != IO_DONE) {
sock_destroy(ps); sock_destroy(ps);
*ps = SOCK_INVALID; *ps = SOCK_INVALID;
return err; if (err == IO_ERROR) return sock_connectstrerror();
} else { else return io_strerror(err);
return NULL; } else return NULL;
}
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -214,7 +214,6 @@ const char *inet_trybind(p_sock ps, const char *address, unsigned short port,
int backlog) int backlog)
{ {
struct sockaddr_in local; struct sockaddr_in local;
const char *err;
memset(&local, 0, sizeof(local)); memset(&local, 0, sizeof(local));
/* address is either wildcard or a valid ip address */ /* address is either wildcard or a valid ip address */
local.sin_addr.s_addr = htonl(INADDR_ANY); 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)); memcpy(&local.sin_addr, *addr, sizeof(struct in_addr));
} }
sock_setblocking(ps); sock_setblocking(ps);
err = sock_bind(ps, (SA *) &local, sizeof(local)); if (sock_bind(ps, (SA *) &local, sizeof(local)) != IO_DONE) {
if (err) {
sock_destroy(ps); sock_destroy(ps);
*ps = SOCK_INVALID; *ps = SOCK_INVALID;
return err; return sock_bindstrerror();
} else { } else {
sock_setnonblocking(ps); sock_setnonblocking(ps);
if (backlog > 0) sock_listen(ps, backlog); 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) 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);
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\

View File

@ -18,17 +18,21 @@
\*=========================================================================*/ \*=========================================================================*/
#include <lua.h> #include <lua.h>
#include "socket.h" #include "socket.h"
#include "timeout.h"
#ifdef WIN32 #ifdef WIN32
#define INET_ATON #define INET_ATON
#endif #endif
void inet_open(lua_State *L); 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, const char *inet_trybind(p_sock ps, const char *address,
unsigned short port, int backlog); unsigned short port, int backlog);
const char *inet_trycreate(p_sock ps, int type); 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_getpeername(lua_State *L, p_sock ps);
int inet_meth_getsockname(lua_State *L, p_sock ps); int inet_meth_getsockname(lua_State *L, p_sock ps);

View File

@ -22,27 +22,24 @@ void io_init(p_io io, p_send send, p_recv recv, void *ctx)
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Translate error codes to Lua * Translate error codes to Lua
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void io_pusherror(lua_State *L, int code) const char *io_strerror(int code)
{ {
switch (code) { switch (code) {
case IO_DONE: case IO_DONE: return NULL;
lua_pushnil(L); case IO_TIMEOUT: return "timeout";
break; case IO_RETRY: return "retry";
case IO_TIMEOUT: case IO_CLOSED: return "closed";
lua_pushstring(L, "timeout"); case IO_REFUSED: return "refused";
break; default: return "unknown error";
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;
} }
} }
/*-------------------------------------------------------------------------*\
* 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);
}

View File

@ -20,12 +20,11 @@
/* IO error codes */ /* IO error codes */
enum { enum {
IO_DONE, /* operation completed successfully */ IO_DONE, /* operation completed successfully */
IO_RETRY, /* please try again */
IO_TIMEOUT, /* operation timed out */ IO_TIMEOUT, /* operation timed out */
IO_CLOSED, /* the connection has been closed */ IO_CLOSED, /* the connection has been closed */
IO_ERROR, /* something wrong... */
IO_REFUSED, /* transfer has been refused */ IO_REFUSED, /* transfer has been refused */
IO_RETRY, /* please try again */ IO_ERROR /* something else wrong... */
IO_LIMITED /* maximum number of bytes reached */
}; };
/* interface to send function */ /* interface to send function */
@ -54,6 +53,7 @@ typedef struct t_io_ {
} t_io; } t_io;
typedef t_io *p_io; typedef t_io *p_io;
const char *io_strerror(int code);
void io_pusherror(lua_State *L, int code); void io_pusherror(lua_State *L, int code);
void io_init(p_io io, p_send send, p_recv recv, void *ctx); void io_init(p_io io, p_send send, p_recv recv, void *ctx);

View File

@ -30,12 +30,12 @@ typedef struct sockaddr SA;
* interface to sockets * interface to sockets
\*=========================================================================*/ \*=========================================================================*/
int sock_open(void); 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); void sock_destroy(p_sock ps);
int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len,
int timeout); int timeout);
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);
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);
void sock_listen(p_sock ps, int backlog); void sock_listen(p_sock ps, int backlog);
void sock_shutdown(p_sock ps, int how); void sock_shutdown(p_sock ps, int how);
int sock_send(p_sock ps, const char *data, size_t count, 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); size_t *got, SA *addr, socklen_t *addr_len, int timeout);
void sock_setnonblocking(p_sock ps); void sock_setnonblocking(p_sock ps);
void sock_setblocking(p_sock ps); void sock_setblocking(p_sock ps);
const char *sock_hoststrerror(void); const char *sock_hoststrerror(void);
const char *sock_createstrerror(void); const char *sock_createstrerror(void);
const char *sock_bindstrerror(void); const char *sock_bindstrerror(void);

View File

@ -200,33 +200,27 @@ static int meth_dirty(lua_State *L)
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
static int meth_accept(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_tcp server = (p_tcp) aux_checkclass(L, "tcp{server}", 1);
p_tm tm = &server->tm; p_tm tm = &server->tm;
p_tcp client;
t_sock sock; t_sock sock;
tm_markstart(tm); tm_markstart(tm);
/* loop until connection accepted or timeout happens */ const char *err = inet_tryaccept(&server->sock, tm, &sock);
while (err != IO_DONE) { /* if successful, push client socket */
err = sock_accept(&server->sock, &sock, if (!err) {
(SA *) &addr, &addr_len, tm_getfailure(tm)); p_tcp clnt = lua_newuserdata(L, sizeof(t_tcp));
if (err == IO_CLOSED || (err == IO_TIMEOUT && !tm_getfailure(tm))) { 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_pushnil(L);
io_pusherror(L, err); lua_pushstring(L, err);
return 2; 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;
}
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Turns a master object into a server object * Turns a master object into a server object
@ -260,7 +254,7 @@ static int meth_connect(lua_State *L)
p_tm tm = &tcp->tm; p_tm tm = &tcp->tm;
const char *err; const char *err;
tm_markstart(tm); tm_markstart(tm);
err = inet_tryconnect(&tcp->sock, address, port, tm_getfailure(tm)); err = inet_tryconnect(&tcp->sock, tm, address, port);
if (err) { if (err) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, err); 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) static int meth_shutdown(lua_State *L)
{ {
@ -341,22 +335,23 @@ static int meth_settimeout(lua_State *L)
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int global_create(lua_State *L) int global_create(lua_State *L)
{ {
const char *err; t_sock sock;
const char *err = inet_trycreate(&sock, SOCK_STREAM);
/* try to allocate a system socket */
if (!err) {
/* allocate tcp object */ /* allocate tcp object */
p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
tcp->sock = sock;
/* set its type as master object */ /* set its type as master object */
aux_setclass(L, "tcp{master}", -1); aux_setclass(L, "tcp{master}", -1);
/* 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);
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
/* initialize remaining structure fields */ /* initialize remaining structure fields */
io_init(&tcp->io, (p_send) sock_send, (p_recv) sock_recv, &tcp->sock); io_init(&tcp->io, (p_send) sock_send, (p_recv) sock_recv, &tcp->sock);
tm_init(&tcp->tm, -1, -1); tm_init(&tcp->tm, -1, -1);
buf_init(&tcp->buf, &tcp->io, &tcp->tm); buf_init(&tcp->buf, &tcp->io, &tcp->tm);
return 1; return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
} }

View File

@ -54,7 +54,7 @@ void tm_init(p_tm tm, int block, int total)
* Returns * Returns
* the number of ms left or -1 if there is no time limit * 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) { if (tm->block < 0 && tm->total < 0) {
return -1; return -1;
@ -89,7 +89,7 @@ int tm_getstart(p_tm tm)
* Returns * Returns
* the number of ms left or -1 if there is no time limit * 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) { if (tm->block < 0 && tm->total < 0) {
return -1; return -1;

View File

@ -18,8 +18,8 @@ typedef t_tm *p_tm;
void tm_open(lua_State *L); void tm_open(lua_State *L);
void tm_init(p_tm tm, int block, int total); void tm_init(p_tm tm, int block, int total);
int tm_getsuccess(p_tm tm); int tm_get(p_tm tm);
int tm_getfailure(p_tm tm); int tm_getretry(p_tm tm);
void tm_markstart(p_tm tm); void tm_markstart(p_tm tm);
int tm_getstart(p_tm tm); int tm_getstart(p_tm tm);
int tm_gettime(void); int tm_gettime(void);

View File

@ -29,6 +29,7 @@ static int meth_getpeername(lua_State *L);
static int meth_setsockname(lua_State *L); static int meth_setsockname(lua_State *L);
static int meth_setpeername(lua_State *L); static int meth_setpeername(lua_State *L);
static int meth_close(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_setoption(lua_State *L);
static int meth_settimeout(lua_State *L); static int meth_settimeout(lua_State *L);
static int meth_fd(lua_State *L); static int meth_fd(lua_State *L);
@ -53,6 +54,7 @@ static luaL_reg udp[] = {
{"receivefrom", meth_receivefrom}, {"receivefrom", meth_receivefrom},
{"settimeout", meth_settimeout}, {"settimeout", meth_settimeout},
{"close", meth_close}, {"close", meth_close},
{"shutdown", meth_shutdown},
{"setoption", meth_setoption}, {"setoption", meth_setoption},
{"__gc", meth_close}, {"__gc", meth_close},
{"fd", meth_fd}, {"fd", meth_fd},
@ -110,7 +112,7 @@ static int meth_send(lua_State *L)
int err; int err;
const char *data = luaL_checklstring(L, 2, &count); const char *data = luaL_checklstring(L, 2, &count);
tm_markstart(tm); 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); if (err == IO_DONE) lua_pushnumber(L, sent);
else lua_pushnil(L); else lua_pushnil(L);
/* a 'closed' error on an unconnected means the target address was not /* 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); addr.sin_port = htons(port);
tm_markstart(tm); tm_markstart(tm);
err = sock_sendto(&udp->sock, data, count, &sent, 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); if (err == IO_DONE) lua_pushnumber(L, sent);
else lua_pushnil(L); else lua_pushnil(L);
/* a 'closed' error on an unconnected means the target address was not /* 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; p_tm tm = &udp->tm;
count = MIN(count, sizeof(buffer)); count = MIN(count, sizeof(buffer));
tm_markstart(tm); 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); if (err == IO_DONE) lua_pushlstring(L, buffer, got);
else lua_pushnil(L); else lua_pushnil(L);
io_pusherror(L, err); io_pusherror(L, err);
@ -182,7 +184,7 @@ static int meth_receivefrom(lua_State *L)
tm_markstart(tm); tm_markstart(tm);
count = MIN(count, sizeof(buffer)); count = MIN(count, sizeof(buffer));
err = sock_recvfrom(&udp->sock, buffer, count, &got, 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) { if (err == IO_DONE) {
lua_pushlstring(L, buffer, got); lua_pushlstring(L, buffer, got);
lua_pushstring(L, inet_ntoa(addr.sin_addr)); 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 port = connecting ?
(unsigned short) luaL_checknumber(L, 3) : (unsigned short) luaL_checknumber(L, 3) :
(unsigned short) luaL_optnumber(L, 3, 0); (unsigned short) luaL_optnumber(L, 3, 0);
const char *err; const char *err = inet_tryconnect(&udp->sock, tm, address, port);
err = inet_tryconnect(&udp->sock, address, port, tm_getfailure(tm));
if (err) { if (err) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, err); lua_pushstring(L, err);
@ -365,6 +366,33 @@ static int meth_close(lua_State *L)
return 0; 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 * 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) int global_create(lua_State *L)
{ {
const char *err; t_sock sock;
/* allocate udp object */ const char *err = inet_trycreate(&sock, SOCK_DGRAM);
/* try to allocate a system socket */
if (!err) {
/* allocate tcp object */
p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp));
udp->sock = sock;
/* set its type as master object */ /* set its type as master object */
aux_setclass(L, "udp{unconnected}", -1); aux_setclass(L, "udp{unconnected}", -1);
/* try to allocate a system socket */ /* initialize remaining structure fields */
err = inet_trycreate(&udp->sock, SOCK_DGRAM); tm_init(&udp->tm, -1, -1);
if (err) { return 1;
/* get rid of object on stack and push error */ } else {
lua_pop(L, 1);
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, err); lua_pushstring(L, err);
return 2; return 2;
} }
/* initialize timeout management */
tm_init(&udp->tm, -1, -1);
return 1;
} }

View File

@ -46,50 +46,59 @@ void sock_destroy(p_sock ps)
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Creates and sets up a socket * 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; int val = 1;
t_sock sock = socket(domain, type, protocol); t_sock sock = socket(domain, type, protocol);
if (sock == SOCK_INVALID) return sock_createstrerror(); if (sock == SOCK_INVALID) return IO_ERROR;
*ps = sock; *ps = sock;
sock_setnonblocking(ps); sock_setnonblocking(ps);
setsockopt(*ps, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(val)); setsockopt(*ps, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(val));
return NULL; return IO_DONE;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Connects or returns error message * 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; 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) { if (connect(sock, addr, addr_len) < 0) {
struct timeval tv; struct timeval tv;
fd_set wfds, efds; fd_set rfds, efds, wfds;
int err; int err;
/* make sure the system is trying to connect */
if (errno != EINPROGRESS) return IO_ERROR;
tv.tv_sec = timeout / 1000; tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000; tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&wfds); FD_ZERO(&efds); FD_ZERO(&rfds); FD_SET(sock, &rfds);
FD_SET(sock, &wfds); FD_SET(sock, &efds); FD_ZERO(&wfds); FD_SET(sock, &wfds);
do err = select(sock+1, NULL, &wfds, &efds, timeout >= 0 ? &tv : NULL); FD_ZERO(&efds); FD_SET(sock, &efds);
while (err < 0 && errno == EINTR); /* we run select to avoid busy waiting */
if (err <= 0) return "timeout"; err = select(sock+1, &rfds, &wfds, &efds, timeout >= 0? &tv: NULL);
if (FD_ISSET(sock, &efds)) { /* 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; char dummy;
recv(sock, &dummy, 0, 0); /* try reading so that errno is set */
return sock_connectstrerror(); if (recv(sock, &dummy, 0, 0) < 0) return IO_ERROR;
} else return NULL; return IO_DONE;
} else return NULL; /* if no event happened, there was a timeout */
} else return IO_TIMEOUT;
/* otherwise connection succeeded */
} else return IO_DONE;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Binds or returns error message * 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(); if (bind(*ps, addr, addr_len) < 0) return IO_ERROR;
else return NULL; 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) int timeout)
{ {
t_sock sock = *ps; t_sock sock = *ps;
struct timeval tv;
SA dummy_addr; SA dummy_addr;
socklen_t dummy_len; socklen_t dummy_len;
fd_set fds;
int err;
if (sock == SOCK_INVALID) return IO_CLOSED; if (sock == SOCK_INVALID) return IO_CLOSED;
if (!addr) addr = &dummy_addr;
if (!addr_len) addr_len = &dummy_len;
*pa = accept(sock, addr, addr_len);
if (*pa == SOCK_INVALID) {
struct timeval tv;
fd_set fds;
tv.tv_sec = timeout / 1000; tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000; tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(sock, &fds); FD_SET(sock, &fds);
*pa = SOCK_INVALID; /* just call select to avoid busy-wait. doesn't really matter
do err = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); * what happens. the caller will choose to retry or not */
while (err < 0 && errno == EINTR); select(sock+1, &fds, NULL, NULL, timeout >= 0? &tv: NULL);
if (err <= 0) return IO_TIMEOUT; return IO_RETRY;
if (!addr) addr = &dummy_addr; } else return IO_DONE;
if (!addr_len) addr_len = &dummy_len;
*pa = accept(sock, addr, addr_len);
if (*pa == SOCK_INVALID) return IO_ERROR;
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; t_sock sock = *ps;
ssize_t put; ssize_t put;
int ret;
/* avoid making system calls on closed sockets */ /* avoid making system calls on closed sockets */
if (sock == SOCK_INVALID) return IO_CLOSED; if (sock == SOCK_INVALID) return IO_CLOSED;
/* make sure we repeat in case the call was interrupted */ /* make sure we repeat in case the call was interrupted */
do put = write(sock, data, count); do put = send(sock, data, count, 0);
while (put <= 0 && errno == EINTR); while (put < 0 && errno == EINTR);
/* deal with failure */ /* deal with failure */
if (put <= 0) { if (put <= 0) {
/* in any case, nothing has been sent */
*sent = 0;
/* run select to avoid busy wait */
if (errno != EPIPE) {
struct timeval tv; struct timeval tv;
fd_set fds; fd_set fds;
/* in any case, nothing has been sent */
*sent = 0;
/* here we know the connection has been closed */
if (errno == EPIPE) return IO_CLOSED;
/* run select to avoid busy wait */
tv.tv_sec = timeout / 1000; tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000; tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(sock, &fds); FD_SET(sock, &fds);
do ret = select(sock+1, NULL, &fds, NULL, timeout >= 0 ?&tv : NULL); if (select(sock+1, NULL, &fds, NULL, timeout >= 0? &tv: NULL) <= 0) {
while (ret < 0 && errno == EINTR); /* here the call was interrupted. calling again might work */
/* tell the caller to call us again because there is more data */ if (errno == EINTR) return IO_RETRY;
if (ret > 0) return IO_DONE; /* here there was no data before timeout */
/* tell the caller there was no data before timeout */
else return IO_TIMEOUT; else return IO_TIMEOUT;
/* here we know the connection has been closed */ /* here we didn't send anything, but now we can */
} else return IO_CLOSED; } else return IO_DONE;
/* here we successfully sent something */ /* here we successfully sent something */
} else { } else {
*sent = put; *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; t_sock sock = *ps;
ssize_t put; ssize_t put;
int ret;
/* avoid making system calls on closed sockets */
if (sock == SOCK_INVALID) return IO_CLOSED; 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); do put = sendto(sock, data, count, 0, addr, addr_len);
while (put <= 0 && errno == EINTR); while (put < 0 && errno == EINTR);
/* deal with failure */
if (put <= 0) { if (put <= 0) {
/* in any case, nothing has been sent */
*sent = 0;
/* run select to avoid busy wait */
if (errno != EPIPE) {
struct timeval tv; struct timeval tv;
fd_set fds; fd_set fds;
*sent = 0;
if (errno == EPIPE) return IO_CLOSED;
tv.tv_sec = timeout / 1000; tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000; tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(sock, &fds); FD_SET(sock, &fds);
do ret = select(sock+1, NULL, &fds, NULL, timeout >= 0? &tv: NULL); if (select(sock+1, NULL, &fds, NULL, timeout >= 0? &tv: NULL) <= 0) {
while (ret < 0 && errno == EINTR); if (errno == EINTR) return IO_RETRY;
/* 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; else return IO_TIMEOUT;
/* here we know the connection has been closed */ } else return IO_DONE;
} else return IO_CLOSED;
/* here we successfully sent something */
} else { } else {
*sent = put; *sent = put;
return IO_DONE; 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 * 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) 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; ssize_t taken;
if (sock == SOCK_INVALID) return IO_CLOSED; if (sock == SOCK_INVALID) return IO_CLOSED;
do taken = read(sock, data, count); do taken = read(sock, data, count);
while (taken <= 0 && errno == EINTR); while (taken < 0 && errno == EINTR);
if (taken <= 0) { if (taken <= 0) {
struct timeval tv; struct timeval tv;
fd_set fds; 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; tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(sock, &fds); FD_SET(sock, &fds);
do ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL);
while (ret < 0 && errno == EINTR); if (ret < 0 && errno == EINTR) return IO_RETRY;
if (ret > 0) return IO_DONE; if (ret == 0) return IO_TIMEOUT;
else return IO_TIMEOUT; else return IO_DONE;
} else { } else {
*got = taken; *got = taken;
return IO_DONE; return IO_DONE;
@ -263,7 +254,7 @@ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got,
ssize_t taken; ssize_t taken;
if (sock == SOCK_INVALID) return IO_CLOSED; if (sock == SOCK_INVALID) return IO_CLOSED;
do taken = recvfrom(sock, data, count, 0, addr, addr_len); do taken = recvfrom(sock, data, count, 0, addr, addr_len);
while (taken <= 0 && errno == EINTR); while (taken < 0 && errno == EINTR);
if (taken <= 0) { if (taken <= 0) {
struct timeval tv; struct timeval tv;
fd_set fds; 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; tv.tv_usec = (timeout % 1000) * 1000;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(sock, &fds); FD_SET(sock, &fds);
do ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL); ret = select(sock+1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL);
while (ret < 0 && errno == EINTR); if (ret < 0 && errno == EINTR) return IO_RETRY;
if (ret > 0) return IO_DONE; if (ret == 0) return IO_TIMEOUT;
else return IO_TIMEOUT; else return IO_DONE;
} else { } else {
*got = taken; *got = taken;
return IO_DONE; return IO_DONE;

View File

@ -314,9 +314,10 @@ body = socket.http.get {
check(body == index) check(body == index)
io.write("testing HEAD method: ") io.write("testing HEAD method: ")
socket.http.TIMEOUT = 1
response = socket.http.request { response = socket.http.request {
method = "HEAD", method = "HEAD",
url = "http://www.tecgraf.puc-rio.br/~diego/" url = "http://www.cs.princeton.edu/~diego/"
} }
check(response and response.headers) check(response and response.headers)

View File

@ -359,6 +359,7 @@ test_methods(socket.tcp(), {
"getsockname", "getsockname",
"setoption", "setoption",
"settimeout", "settimeout",
"shutdown",
"close", "close",
}) })
test_methods(socket.udp(), { test_methods(socket.udp(), {
@ -372,6 +373,7 @@ test_methods(socket.udp(), {
"receivefrom", "receivefrom",
"setoption", "setoption",
"settimeout", "settimeout",
"shutdown",
"close", "close",
}) })
@ -484,6 +486,4 @@ test_blockingtimeoutreceive(800091, 3, 2)
test_blockingtimeoutreceive(800091, 3, 1) test_blockingtimeoutreceive(800091, 3, 1)
]] ]]
socket.done()
test(string.format("done in %.2fs", socket.time() - start)) test(string.format("done in %.2fs", socket.time() - start))