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;
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;
}

View File

@ -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)

View File

@ -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);
}
/*-------------------------------------------------------------------------*\

View File

@ -18,17 +18,21 @@
\*=========================================================================*/
#include <lua.h>
#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);

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
\*-------------------------------------------------------------------------*/
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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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))) {
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);
io_pusherror(L, err);
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;
t_sock sock;
const char *err = inet_trycreate(&sock, SOCK_STREAM);
/* try to allocate a system socket */
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);
/* 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 */
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;
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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 */
t_sock sock;
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));
udp->sock = sock;
/* set its type as master object */
aux_setclass(L, "udp{unconnected}", -1);
/* 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);
/* 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;
}

View File

@ -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;
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_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;
/* 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) {
/* in any case, nothing has been sent */
*sent = 0;
/* run select to avoid busy wait */
if (errno != EPIPE) {
struct timeval tv;
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_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 (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 know the connection has been closed */
} else return IO_CLOSED;
/* 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 */
*sent = 0;
/* run select to avoid busy wait */
if (errno != EPIPE) {
struct timeval tv;
fd_set fds;
*sent = 0;
if (errno == EPIPE) return IO_CLOSED;
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 (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;

View File

@ -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)

View File

@ -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))