mirror of
https://github.com/lunarmodules/luasocket.git
synced 2024-12-25 12:08:21 +01:00
Trying to get connect-with-timeout to work. Darwin works...
This commit is contained in:
parent
02ef4e7daa
commit
c8d58798f0
12
src/buffer.c
12
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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
41
src/inet.c
41
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);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
|
@ -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);
|
||||
|
||||
|
35
src/io.c
35
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);
|
||||
}
|
||||
|
6
src/io.h
6
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);
|
||||
|
||||
|
@ -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);
|
||||
|
69
src/tcp.c
69
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
64
src/udp.c
64
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;
|
||||
}
|
||||
|
165
src/usocket.c
165
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;
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user