Moving on to beta2.

This commit is contained in:
Diego Nehab 2004-07-01 03:32:09 +00:00
parent 7aaba59909
commit 7115c12fbc
16 changed files with 391 additions and 443 deletions

7
FIX
View File

@ -1,8 +1,9 @@
setup error messages in the default case.
listen defaults to 32 backlog
smtp sends quit on exceptions
accept/connect interrupt safe
smtp/ftp/http fail gracefully
accept/connect/select interrupt safe
accepted sockets are nonblocking
new timming functions. better sleep/gettime
new timming functions. higher resolution, no wrap around
bug fixes in the manual
getfd missing cast
added unix domain support example

8
TODO
View File

@ -1,8 +1,8 @@
setup error messages in the default case.
create the getstats method.
ajeitar o connect com a dica do mike
if ((err > 1 || !FD_ISSET(sock, &wfds)) &&
recv(sock, &dummy, 0, 0) < 0 && errno != EWOULDBLOCK) ...
sent, received, age = sock:getstats()
take a look at DB's smtp patch
sort out the wrap around of gettime...
use doubles all over

View File

@ -72,50 +72,51 @@ local function tget(gett)
local retries, dgram, sent, datahost, dataport, code
local last = 0
local con = socket.try(socket.udp())
local try = socket.newtry(function() con:close() end)
-- convert from name to ip if needed
gett.host = socket.try(socket.dns.toip(gett.host))
gett.host = try(socket.dns.toip(gett.host))
con:settimeout(1)
-- first packet gives data host/port to be used for data transfers
retries = 0
repeat
sent = socket.try(con:sendto(RRQ(gett.path, "octet"),
sent = try(con:sendto(RRQ(gett.path, "octet"),
gett.host, gett.port))
dgram, datahost, dataport = con:receivefrom()
retries = retries + 1
until dgram or datahost ~= "timeout" or retries > 5
socket.try(dgram, datahost)
try(dgram, datahost)
-- associate socket with data host/port
socket.try(con:setpeername(datahost, dataport))
try(con:setpeername(datahost, dataport))
-- default sink
local sink = gett.sink or ltn12.sink.null()
-- process all data packets
while 1 do
-- decode packet
code = get_OP(dgram)
socket.try(code ~= OP_ERROR, get_ERROR(dgram))
socket.try(code == OP_DATA, "unhandled opcode " .. code)
try(code ~= OP_ERROR, get_ERROR(dgram))
try(code == OP_DATA, "unhandled opcode " .. code)
-- get data packet parts
local block, data = split_DATA(dgram)
-- if not repeated, write
if block == last+1 then
socket.try(sink(data))
try(sink(data))
last = block
end
-- last packet brings less than 512 bytes of data
if string.len(data) < 512 then
socket.try(con:send(ACK(block)))
socket.try(con:close())
socket.try(sink(nil))
try(con:send(ACK(block)))
try(con:close())
try(sink(nil))
return 1
end
-- get the next packet
retries = 0
repeat
sent = socket.try(con:send(ACK(last)))
sent = try(con:send(ACK(last)))
dgram, err = con:receive()
retries = retries + 1
until dgram or err ~= "timeout" or retries > 5
socket.try(dgram, err)
try(dgram, err)
end
end

51
samples/lpr.lua Normal file
View File

@ -0,0 +1,51 @@
local lp = require("lp")
local function usage()
print('\nUsage: lp filename [keyword=val...]\n')
print('Valid keywords are :')
print(
' host=remote host or IP address (default "localhost")\n' ..
' queue=remote queue or printer name (default "printer")\n' ..
' port=remote port number (default 515)\n' ..
' user=sending user name\n' ..
' format=["binary" | "text" | "ps" | "pr" | "fortran"] (default "binary")\n' ..
' banner=true|false\n' ..
' indent=number of columns to indent\n' ..
' mail=email of address to notify when print is complete\n' ..
' title=title to use for "pr" format\n' ..
' width=width for "text" or "pr" formats\n' ..
' class=\n' ..
' job=\n' ..
' name=\n' ..
' localbind=true|false\n'
)
return nil
end
if not arg or not arg[1] then
return usage()
end
do
local s="opt = {"
for i = 2 , table.getn(arg), 1 do
s = s .. string.gsub(arg[i],"[%s%c%p]*([%w]*)=([\"]?[%w%s_!@#$%%^&*()<>:;]+[\"]\?\.?)","%1%=\"%2\",\n")
end
s = s .. "};\n"
assert(loadstring(s))();
if not arg[2] then
return usage()
end
if arg[1] ~= "query" then
r,e=lp.send(arg[1],opt)
io.stderr:write(tostring(r or e),'\n')
else
r,e=lp.query(opt)
io.stderr:write(tostring(r or e), '\n')
end
end
-- trivial tests
--lua lp.lua lp.lua queue=default host=localhost
--lua lp.lua lp.lua queue=default host=localhost format=binary localbind=1
--lua lp.lua query queue=default host=localhost

View File

@ -33,8 +33,7 @@ static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent);
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int buf_open(lua_State *L)
{
int buf_open(lua_State *L) {
(void) L;
return 0;
}
@ -42,8 +41,7 @@ int buf_open(lua_State *L)
/*-------------------------------------------------------------------------*\
* Initializes C structure
\*-------------------------------------------------------------------------*/
void buf_init(p_buf buf, p_io io, p_tm tm)
{
void buf_init(p_buf buf, p_io io, p_tm tm) {
buf->first = buf->last = 0;
buf->io = io;
buf->tm = tm;
@ -52,13 +50,11 @@ void buf_init(p_buf buf, p_io io, p_tm tm)
/*-------------------------------------------------------------------------*\
* object:send() interface
\*-------------------------------------------------------------------------*/
int buf_meth_send(lua_State *L, p_buf buf)
{
int buf_meth_send(lua_State *L, p_buf buf) {
int top = lua_gettop(L);
size_t total = 0;
int arg, err = IO_DONE;
p_tm tm = buf->tm;
tm_markstart(tm);
p_tm tm = tm_markstart(buf->tm);
for (arg = 2; arg <= top; arg++) { /* first arg is socket object */
size_t sent, count;
const char *data = luaL_optlstring(L, arg, NULL, &count);
@ -69,7 +65,7 @@ int buf_meth_send(lua_State *L, p_buf buf)
/* check if there was an error */
if (err != IO_DONE) {
lua_pushnil(L);
io_pusherror(L, err);
io_pusherror(L, buf->io, err);
lua_pushnumber(L, total);
} else {
lua_pushnumber(L, total);
@ -78,7 +74,7 @@ int buf_meth_send(lua_State *L, p_buf buf)
}
#ifdef LUASOCKET_DEBUG
/* push time elapsed during operation as the last return value */
lua_pushnumber(L, (tm_gettime() - tm_getstart(tm))/1000.0);
lua_pushnumber(L, tm_gettime() - tm_getstart(tm));
#endif
return lua_gettop(L) - top;
}
@ -86,13 +82,11 @@ int buf_meth_send(lua_State *L, p_buf buf)
/*-------------------------------------------------------------------------*\
* object:receive() interface
\*-------------------------------------------------------------------------*/
int buf_meth_receive(lua_State *L, p_buf buf)
{
int buf_meth_receive(lua_State *L, p_buf buf) {
int err = IO_DONE, top = lua_gettop(L);
p_tm tm = buf->tm;
p_tm tm = tm_markstart(buf->tm);
luaL_Buffer b;
luaL_buffinit(L, &b);
tm_markstart(tm);
/* receive all patterns */
if (!lua_isnumber(L, 2)) {
static const char *patternnames[] = {"*l", "*a", NULL};
@ -107,7 +101,7 @@ int buf_meth_receive(lua_State *L, p_buf buf)
/* check if there was an error */
if (err != IO_DONE) {
luaL_pushresult(&b);
io_pusherror(L, err);
io_pusherror(L, buf->io, err);
lua_pushvalue(L, -2);
lua_pushnil(L);
lua_replace(L, -4);
@ -118,7 +112,7 @@ int buf_meth_receive(lua_State *L, p_buf buf)
}
#ifdef LUASOCKET_DEBUG
/* push time elapsed during operation as the last return value */
lua_pushnumber(L, (tm_gettime() - tm_getstart(tm))/1000.0);
lua_pushnumber(L, tm_gettime() - tm_getstart(tm));
#endif
return lua_gettop(L) - top;
}
@ -126,8 +120,7 @@ int buf_meth_receive(lua_State *L, p_buf buf)
/*-------------------------------------------------------------------------*\
* Determines if there is any data in the read buffer
\*-------------------------------------------------------------------------*/
int buf_isempty(p_buf buf)
{
int buf_isempty(p_buf buf) {
return buf->first >= buf->last;
}
@ -137,16 +130,14 @@ int buf_isempty(p_buf buf)
/*-------------------------------------------------------------------------*\
* Sends a block of data (unbuffered)
\*-------------------------------------------------------------------------*/
static
int sendraw(p_buf buf, const char *data, size_t count, size_t *sent)
{
static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent) {
p_io io = buf->io;
p_tm tm = buf->tm;
size_t total = 0;
int err = IO_DONE;
while (total < count && (err == IO_DONE || err == IO_RETRY)) {
while (total < count && err == IO_DONE) {
size_t done;
err = io->send(io->ctx, data+total, count-total, &done, tm_get(tm));
err = io->send(io->ctx, data+total, count-total, &done, tm);
total += done;
}
*sent = total;
@ -156,12 +147,10 @@ int sendraw(p_buf buf, const char *data, size_t count, size_t *sent)
/*-------------------------------------------------------------------------*\
* Reads a fixed number of bytes (buffered)
\*-------------------------------------------------------------------------*/
static
int recvraw(p_buf buf, size_t wanted, luaL_Buffer *b)
{
int err = IO_DONE;
static int recvraw(p_buf buf, size_t wanted, luaL_Buffer *b) {
int err = IO_DONE;
size_t total = 0;
while (total < wanted && (err == IO_DONE || err == IO_RETRY)) {
while (total < wanted && err == IO_DONE) {
size_t count; const char *data;
err = buf_get(buf, &data, &count);
count = MIN(count, wanted - total);
@ -175,11 +164,9 @@ int recvraw(p_buf buf, size_t wanted, luaL_Buffer *b)
/*-------------------------------------------------------------------------*\
* Reads everything until the connection is closed (buffered)
\*-------------------------------------------------------------------------*/
static
int recvall(p_buf buf, luaL_Buffer *b)
{
static int recvall(p_buf buf, luaL_Buffer *b) {
int err = IO_DONE;
while (err == IO_DONE || err == IO_RETRY) {
while (err == IO_DONE) {
const char *data; size_t count;
err = buf_get(buf, &data, &count);
luaL_addlstring(b, data, count);
@ -193,11 +180,9 @@ int recvall(p_buf buf, luaL_Buffer *b)
* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF
* are not returned by the function and are discarded from the buffer
\*-------------------------------------------------------------------------*/
static
int recvline(p_buf buf, luaL_Buffer *b)
{
static int recvline(p_buf buf, luaL_Buffer *b) {
int err = IO_DONE;
while (err == IO_DONE || err == IO_RETRY) {
while (err == IO_DONE) {
size_t count, pos; const char *data;
err = buf_get(buf, &data, &count);
pos = 0;
@ -219,9 +204,7 @@ int recvline(p_buf buf, luaL_Buffer *b)
* Skips a given number of bytes from read buffer. No data is read from the
* transport layer
\*-------------------------------------------------------------------------*/
static
void buf_skip(p_buf buf, size_t count)
{
static void buf_skip(p_buf buf, size_t count) {
buf->first += count;
if (buf_isempty(buf))
buf->first = buf->last = 0;
@ -231,15 +214,13 @@ void buf_skip(p_buf buf, size_t count)
* Return any data available in buffer, or get more data from transport layer
* if buffer is empty
\*-------------------------------------------------------------------------*/
static
int buf_get(p_buf buf, const char **data, size_t *count)
{
static int buf_get(p_buf buf, const char **data, size_t *count) {
int err = IO_DONE;
p_io io = buf->io;
p_tm tm = buf->tm;
if (buf_isempty(buf)) {
size_t got;
err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm_get(tm));
err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm);
buf->first = 0;
buf->last = got;
}
@ -247,4 +228,3 @@ int buf_get(p_buf buf, const char **data, size_t *count)
*data = buf->data + buf->first;
return err;
}

View File

@ -12,34 +12,34 @@
/*-------------------------------------------------------------------------*\
* Initializes C structure
\*-------------------------------------------------------------------------*/
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, p_geterr geterr, void *ctx) {
io->send = send;
io->recv = recv;
io->geterr = geterr;
io->ctx = ctx;
}
/*-------------------------------------------------------------------------*\
* Translate error codes to Lua
\*-------------------------------------------------------------------------*/
const char *io_strerror(int code)
{
const char *io_strerror(int code) {
switch (code) {
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";
case IO_TIMEOUT: return "timeout";
case IO_CLIPPED: return "clipped";
default: return "unknown error";
}
}
/*-------------------------------------------------------------------------*\
* Translate error codes to Lua
* Push error message from code or from driver
\*-------------------------------------------------------------------------*/
void io_pusherror(lua_State *L, int code)
void io_pusherror(lua_State *L, p_io io, int code)
{
const char *err = io_strerror(code);
const char *err = NULL;
if (code < IO_USER) err = io_strerror(code);
else err = io->geterr(io->ctx, code);
if (err) lua_pushstring(L, err);
else lua_pushnil(L);
}

View File

@ -17,14 +17,15 @@
#include <stdio.h>
#include <lua.h>
#include "timeout.h"
/* 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_REFUSED, /* transfer has been refused */
IO_ERROR /* something else wrong... */
IO_CLIPPED, /* maxium bytes count reached */
IO_USER /* last element in enum is user custom error */
};
/* interface to send function */
@ -33,7 +34,13 @@ typedef int (*p_send) (
const char *data, /* pointer to buffer with data to send */
size_t count, /* number of bytes to send from buffer */
size_t *sent, /* number of bytes sent uppon return */
int timeout /* number of miliseconds left for transmission */
p_tm tm /* timeout control */
);
/* returns an error string */
typedef const char *(*p_geterr) (
void *ctx, /* context needed by geterror */
int code /* error code */
);
/* interface to recv function */
@ -42,7 +49,7 @@ typedef int (*p_recv) (
char *data, /* pointer to buffer where data will be writen */
size_t count, /* number of bytes to receive into buffer */
size_t *got, /* number of bytes received uppon return */
int timeout /* number of miliseconds left for transmission */
p_tm tm /* timeout control */
);
/* IO driver definition */
@ -50,11 +57,12 @@ typedef struct t_io_ {
void *ctx; /* context needed by send/recv */
p_send send; /* send function pointer */
p_recv recv; /* receive function pointer */
p_geterr geterr; /* receive function pointer */
} 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);
void io_pusherror(lua_State *L, p_io io, int code);
void io_init(p_io io, p_send send, p_recv recv, p_geterr geterr, void *ctx);
#endif /* IO_H */

View File

@ -10,6 +10,7 @@
#include <lauxlib.h>
#include "socket.h"
#include "timeout.h"
#include "select.h"
/*=========================================================================*\
@ -48,19 +49,21 @@ int select_open(lua_State *L) {
* Waits for a set of sockets until a condition is met or timeout.
\*-------------------------------------------------------------------------*/
static int global_select(lua_State *L) {
int timeout, rtab, wtab, itab, max_fd, ret, ndirty;
int rtab, wtab, itab, max_fd, ret, ndirty;
fd_set rset, wset;
t_tm tm;
double t = luaL_optnumber(L, 3, -1);
FD_ZERO(&rset); FD_ZERO(&wset);
lua_settop(L, 3);
timeout = lua_isnil(L, 3) ? -1 : (int)(luaL_checknumber(L, 3) * 1000);
lua_newtable(L); itab = lua_gettop(L);
lua_newtable(L); rtab = lua_gettop(L);
lua_newtable(L); wtab = lua_gettop(L);
max_fd = collect_fd(L, 1, -1, itab, &rset);
ndirty = check_dirty(L, 1, rtab, &rset);
timeout = ndirty > 0? 0: timeout;
t = ndirty > 0? 0.0: t;
tm_init(&tm, t, -1);
max_fd = collect_fd(L, 2, max_fd, itab, &wset);
ret = sock_select(max_fd+1, &rset, &wset, NULL, timeout);
ret = sock_select(max_fd+1, &rset, &wset, NULL, &tm);
if (ret > 0 || (ret == 0 && ndirty > 0)) {
return_fd(L, &rset, max_fd+1, itab, rtab, ndirty);
return_fd(L, &wset, max_fd+1, itab, wtab, 0);

View File

@ -41,17 +41,15 @@ int sock_open(void);
int sock_close(void);
void sock_destroy(p_sock ps);
void sock_shutdown(p_sock ps, int how);
int sock_send(p_sock ps, const char *data, size_t count,
size_t *sent, int timeout);
int sock_recv(p_sock ps, char *data, size_t count,
size_t *got, int timeout);
int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, p_tm tm);
int sock_recv(p_sock ps, char *data, size_t count, size_t *got, p_tm tm);
int sock_sendto(p_sock ps, const char *data, size_t count,
size_t *sent, SA *addr, socklen_t addr_len, int timeout);
size_t *sent, SA *addr, socklen_t addr_len, p_tm tm);
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, p_tm tm);
void sock_setnonblocking(p_sock ps);
void sock_setblocking(p_sock ps);
int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, int timeout);
int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_tm tm);
const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, p_tm tm);
const char *sock_create(p_sock ps, int domain, int type, int protocol);
@ -60,6 +58,8 @@ const char *sock_listen(p_sock ps, int backlog);
const char *sock_accept(p_sock ps, p_sock pa, SA *addr,
socklen_t *addr_len, p_tm tm);
const char *sock_geterr(p_sock ps, int code);
const char *sock_hoststrerror(void);
const char *sock_strerror(void);
#endif /* SOCK_H */

View File

@ -163,7 +163,8 @@ static int meth_accept(lua_State *L)
/* initialize structure fields */
sock_setnonblocking(&sock);
clnt->sock = sock;
io_init(&clnt->io, (p_send)sock_send, (p_recv)sock_recv, &clnt->sock);
io_init(&clnt->io, (p_send) sock_send, (p_recv) sock_recv,
(p_geterr) sock_geterr, &clnt->sock);
tm_init(&clnt->tm, -1, -1);
buf_init(&clnt->buf, &clnt->io, &clnt->tm);
return 1;
@ -313,7 +314,8 @@ static int global_create(lua_State *L)
/* initialize remaining structure fields */
sock_setnonblocking(&sock);
tcp->sock = sock;
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,
(p_geterr) sock_geterr, &tcp->sock);
tm_init(&tcp->tm, -1, -1);
buf_init(&tcp->buf, &tcp->io, &tcp->tm);
return 1;

View File

@ -46,8 +46,7 @@ static luaL_reg func[] = {
/*-------------------------------------------------------------------------*\
* Initialize structure
\*-------------------------------------------------------------------------*/
void tm_init(p_tm tm, int block, int total)
{
void tm_init(p_tm tm, double block, double total) {
tm->block = block;
tm->total = total;
}
@ -60,18 +59,17 @@ 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_get(p_tm tm)
{
if (tm->block < 0 && tm->total < 0) {
double tm_get(p_tm tm) {
if (tm->block < 0.0 && tm->total < 0.0) {
return -1;
} else if (tm->block < 0) {
int t = tm->total - tm_gettime() + tm->start;
return MAX(t, 0);
} else if (tm->total < 0) {
} else if (tm->block < 0.0) {
double t = tm->total - tm_gettime() + tm->start;
return MAX(t, 0.0);
} else if (tm->total < 0.0) {
return tm->block;
} else {
int t = tm->total - tm_gettime() + tm->start;
return MIN(tm->block, MAX(t, 0));
double t = tm->total - tm_gettime() + tm->start;
return MIN(tm->block, MAX(t, 0.0));
}
}
@ -82,8 +80,7 @@ int tm_get(p_tm tm)
* Returns
* start field of structure
\*-------------------------------------------------------------------------*/
int tm_getstart(p_tm tm)
{
double tm_getstart(p_tm tm) {
return tm->start;
}
@ -95,19 +92,18 @@ int tm_getstart(p_tm tm)
* Returns
* the number of ms left or -1 if there is no time limit
\*-------------------------------------------------------------------------*/
int tm_getretry(p_tm tm)
{
if (tm->block < 0 && tm->total < 0) {
double tm_getretry(p_tm tm) {
if (tm->block < 0.0 && tm->total < 0.0) {
return -1;
} else if (tm->block < 0) {
int t = tm->total - tm_gettime() + tm->start;
return MAX(t, 0);
} else if (tm->total < 0) {
int t = tm->block - tm_gettime() + tm->start;
return MAX(t, 0);
} else if (tm->block < 0.0) {
double t = tm->total - tm_gettime() + tm->start;
return MAX(t, 0.0);
} else if (tm->total < 0.0) {
double t = tm->block - tm_gettime() + tm->start;
return MAX(t, 0.0);
} else {
int t = tm->total - tm_gettime() + tm->start;
return MIN(tm->block, MAX(t, 0));
double t = tm->total - tm_gettime() + tm->start;
return MIN(tm->block, MAX(t, 0.0));
}
}
@ -116,8 +112,7 @@ int tm_getretry(p_tm tm)
* Input
* tm: timeout control structure
\*-------------------------------------------------------------------------*/
p_tm tm_markstart(p_tm tm)
{
p_tm tm_markstart(p_tm tm) {
tm->start = tm_gettime();
return tm;
}
@ -128,24 +123,23 @@ p_tm tm_markstart(p_tm tm)
* time in ms.
\*-------------------------------------------------------------------------*/
#ifdef _WIN32
int tm_gettime(void)
{
return GetTickCount();
double tm_gettime(void) {
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return ft.dwLowDateTime/1.0e7 + ft.dwHighDateTime*(4294967296.0/1.0e7);
}
#else
int tm_gettime(void)
{
double tm_gettime(void) {
struct timeval v;
gettimeofday(&v, (struct timezone *) NULL);
return v.tv_sec * 1000 + v.tv_usec/1000;
return v.tv_sec + v.tv_usec/1.0e6;
}
#endif
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int tm_open(lua_State *L)
{
int tm_open(lua_State *L) {
luaL_openlib(L, NULL, func, 0);
return 0;
}
@ -156,16 +150,15 @@ int tm_open(lua_State *L)
* time: time out value in seconds
* mode: "b" for block timeout, "t" for total timeout. (default: b)
\*-------------------------------------------------------------------------*/
int tm_meth_settimeout(lua_State *L, p_tm tm)
{
int ms = lua_isnil(L, 2) ? -1 : (int) (luaL_checknumber(L, 2)*1000.0);
int tm_meth_settimeout(lua_State *L, p_tm tm) {
double t = luaL_optnumber(L, 2, -1);
const char *mode = luaL_optstring(L, 3, "b");
switch (*mode) {
case 'b':
tm->block = ms;
tm->block = t;
break;
case 'r': case 't':
tm->total = ms;
tm->total = t;
break;
default:
luaL_argcheck(L, 0, 3, "invalid timeout mode");
@ -183,7 +176,7 @@ int tm_meth_settimeout(lua_State *L, p_tm tm)
\*-------------------------------------------------------------------------*/
static int tm_lua_gettime(lua_State *L)
{
lua_pushnumber(L, tm_gettime()/1000.0);
lua_pushnumber(L, tm_gettime());
return 1;
}

View File

@ -10,19 +10,19 @@
/* timeout control structure */
typedef struct t_tm_ {
int total; /* total number of miliseconds for operation */
int block; /* maximum time for blocking calls */
int start; /* time of start of operation */
double total; /* total number of miliseconds for operation */
double block; /* maximum time for blocking calls */
double start; /* time of start of operation */
} t_tm;
typedef t_tm *p_tm;
int tm_open(lua_State *L);
void tm_init(p_tm tm, int block, int total);
int tm_get(p_tm tm);
int tm_getretry(p_tm tm);
void tm_init(p_tm tm, double block, double total);
double tm_get(p_tm tm);
double tm_getretry(p_tm tm);
p_tm tm_markstart(p_tm tm);
int tm_getstart(p_tm tm);
int tm_gettime(void);
double tm_getstart(p_tm tm);
double tm_gettime(void);
int tm_meth_settimeout(lua_State *L, p_tm tm);
#endif /* TM_H */

107
src/udp.c
View File

@ -41,25 +41,26 @@ static int meth_settimeout(lua_State *L);
static int meth_getfd(lua_State *L);
static int meth_setfd(lua_State *L);
static int meth_dirty(lua_State *L);
static void pusherror(lua_State *L, int code);
/* udp object methods */
static luaL_reg udp[] = {
{"setpeername", meth_setpeername},
{"setsockname", meth_setsockname},
{"getsockname", meth_getsockname},
{"getpeername", meth_getpeername},
{"send", meth_send},
{"sendto", meth_sendto},
{"receive", meth_receive},
{"receivefrom", meth_receivefrom},
{"settimeout", meth_settimeout},
{"close", meth_close},
{"setoption", meth_setoption},
{"__gc", meth_close},
{"__tostring", aux_tostring},
{"getfd", meth_getfd},
{"setfd", meth_setfd},
{"close", meth_close},
{"dirty", meth_dirty},
{"getfd", meth_getfd},
{"getpeername", meth_getpeername},
{"getsockname", meth_getsockname},
{"receive", meth_receive},
{"receivefrom", meth_receivefrom},
{"send", meth_send},
{"sendto", meth_sendto},
{"setfd", meth_setfd},
{"setoption", meth_setoption},
{"setpeername", meth_setpeername},
{"setsockname", meth_setsockname},
{"settimeout", meth_settimeout},
{NULL, NULL}
};
@ -99,35 +100,43 @@ int udp_open(lua_State *L)
return 0;
}
/*=========================================================================*\
* Lua methods
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Pushes the error message
\*-------------------------------------------------------------------------*/
void pusherror(lua_State *L, int code) {
const char *err = code != IO_USER? io_strerror(code): "refused";
err = err? err: sock_strerror();
if (err) lua_pushstring(L, err);
else lua_pushnil(L);
}
/*-------------------------------------------------------------------------*\
* Send data through connected udp socket
\*-------------------------------------------------------------------------*/
static int meth_send(lua_State *L)
{
static int meth_send(lua_State *L) {
p_udp udp = (p_udp) aux_checkclass(L, "udp{connected}", 1);
p_tm tm = &udp->tm;
size_t count, sent = 0;
int err;
const char *data = luaL_checklstring(L, 2, &count);
tm_markstart(tm);
do err = sock_send(&udp->sock, data, count, &sent, tm_getretry(tm));
while (err == IO_RETRY);
err = sock_send(&udp->sock, data, count, &sent, 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
* accepted by the transport layer */
io_pusherror(L, err == IO_CLOSED ? IO_REFUSED : err);
pusherror(L, err == IO_CLOSED ? IO_USER : err);
return 2;
}
/*-------------------------------------------------------------------------*\
* Send data through unconnected udp socket
\*-------------------------------------------------------------------------*/
static int meth_sendto(lua_State *L)
{
static int meth_sendto(lua_State *L) {
p_udp udp = (p_udp) aux_checkclass(L, "udp{unconnected}", 1);
size_t count, sent = 0;
const char *data = luaL_checklstring(L, 2, &count);
@ -142,22 +151,20 @@ static int meth_sendto(lua_State *L)
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
tm_markstart(tm);
do err = sock_sendto(&udp->sock, data, count, &sent,
(SA *) &addr, sizeof(addr), tm_get(tm));
while (err == IO_RETRY);
err = sock_sendto(&udp->sock, data, count, &sent,
(SA *) &addr, sizeof(addr), 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
* accepted by the transport layer */
io_pusherror(L, err == IO_CLOSED ? IO_REFUSED : err);
pusherror(L, err == IO_CLOSED ? IO_USER : err);
return 2;
}
/*-------------------------------------------------------------------------*\
* Receives data from a UDP socket
\*-------------------------------------------------------------------------*/
static int meth_receive(lua_State *L)
{
static int meth_receive(lua_State *L) {
p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1);
char buffer[UDP_DATAGRAMSIZE];
size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer));
@ -165,19 +172,17 @@ static int meth_receive(lua_State *L)
p_tm tm = &udp->tm;
count = MIN(count, sizeof(buffer));
tm_markstart(tm);
do err = sock_recv(&udp->sock, buffer, count, &got, tm_get(tm));
while (err == IO_RETRY);
err = sock_recv(&udp->sock, buffer, count, &got, tm);
if (err == IO_DONE) lua_pushlstring(L, buffer, got);
else lua_pushnil(L);
io_pusherror(L, err);
pusherror(L, err);
return 2;
}
/*-------------------------------------------------------------------------*\
* Receives data and sender from a UDP socket
\*-------------------------------------------------------------------------*/
static int meth_receivefrom(lua_State *L)
{
static int meth_receivefrom(lua_State *L) {
p_udp udp = (p_udp) aux_checkclass(L, "udp{unconnected}", 1);
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
@ -187,9 +192,8 @@ static int meth_receivefrom(lua_State *L)
p_tm tm = &udp->tm;
tm_markstart(tm);
count = MIN(count, sizeof(buffer));
do err = sock_recvfrom(&udp->sock, buffer, count, &got,
(SA *) &addr, &addr_len, tm_get(tm));
while (err == IO_RETRY);
err = sock_recvfrom(&udp->sock, buffer, count, &got,
(SA *) &addr, &addr_len, tm);
if (err == IO_DONE) {
lua_pushlstring(L, buffer, got);
lua_pushstring(L, inet_ntoa(addr.sin_addr));
@ -197,7 +201,7 @@ static int meth_receivefrom(lua_State *L)
return 3;
} else {
lua_pushnil(L);
io_pusherror(L, err);
pusherror(L, err);
return 2;
}
}
@ -205,23 +209,20 @@ static int meth_receivefrom(lua_State *L)
/*-------------------------------------------------------------------------*\
* Select support methods
\*-------------------------------------------------------------------------*/
static int meth_getfd(lua_State *L)
{
static int meth_getfd(lua_State *L) {
p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1);
lua_pushnumber(L, (int) udp->sock);
return 1;
}
/* this is very dangerous, but can be handy for those that are brave enough */
static int meth_setfd(lua_State *L)
{
static int meth_setfd(lua_State *L) {
p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1);
udp->sock = (t_sock) luaL_checknumber(L, 2);
return 0;
}
static int meth_dirty(lua_State *L)
{
static int meth_dirty(lua_State *L) {
p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1);
(void) udp;
lua_pushboolean(L, 0);
@ -231,14 +232,12 @@ static int meth_dirty(lua_State *L)
/*-------------------------------------------------------------------------*\
* Just call inet methods
\*-------------------------------------------------------------------------*/
static int meth_getpeername(lua_State *L)
{
static int meth_getpeername(lua_State *L) {
p_udp udp = (p_udp) aux_checkclass(L, "udp{connected}", 1);
return inet_meth_getpeername(L, &udp->sock);
}
static int meth_getsockname(lua_State *L)
{
static int meth_getsockname(lua_State *L) {
p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1);
return inet_meth_getsockname(L, &udp->sock);
}
@ -246,8 +245,7 @@ static int meth_getsockname(lua_State *L)
/*-------------------------------------------------------------------------*\
* Just call option handler
\*-------------------------------------------------------------------------*/
static int meth_setoption(lua_State *L)
{
static int meth_setoption(lua_State *L) {
p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1);
return opt_meth_setoption(L, opt, &udp->sock);
}
@ -255,8 +253,7 @@ static int meth_setoption(lua_State *L)
/*-------------------------------------------------------------------------*\
* Just call tm methods
\*-------------------------------------------------------------------------*/
static int meth_settimeout(lua_State *L)
{
static int meth_settimeout(lua_State *L) {
p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1);
return tm_meth_settimeout(L, &udp->tm);
}
@ -264,8 +261,7 @@ static int meth_settimeout(lua_State *L)
/*-------------------------------------------------------------------------*\
* Turns a master udp object into a client object.
\*-------------------------------------------------------------------------*/
static int meth_setpeername(lua_State *L)
{
static int meth_setpeername(lua_State *L) {
p_udp udp = (p_udp) aux_checkclass(L, "udp{unconnected}", 1);
p_tm tm = &udp->tm;
const char *address = luaL_checkstring(L, 2);
@ -289,8 +285,7 @@ static int meth_setpeername(lua_State *L)
/*-------------------------------------------------------------------------*\
* Closes socket used by object
\*-------------------------------------------------------------------------*/
static int meth_close(lua_State *L)
{
static int meth_close(lua_State *L) {
p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1);
sock_destroy(&udp->sock);
return 0;
@ -299,8 +294,7 @@ static int meth_close(lua_State *L)
/*-------------------------------------------------------------------------*\
* Turns a master object into a server object
\*-------------------------------------------------------------------------*/
static int meth_setsockname(lua_State *L)
{
static int meth_setsockname(lua_State *L) {
p_udp udp = (p_udp) aux_checkclass(L, "udp{unconnected}", 1);
const char *address = luaL_checkstring(L, 2);
unsigned short port = (unsigned short) luaL_checknumber(L, 3);
@ -320,8 +314,7 @@ static int meth_setsockname(lua_State *L)
/*-------------------------------------------------------------------------*\
* Creates a master udp object
\*-------------------------------------------------------------------------*/
static int global_create(lua_State *L)
{
static int global_create(lua_State *L) {
t_sock sock;
const char *err = inet_trycreate(&sock, SOCK_DGRAM);
/* try to allocate a system socket */

View File

@ -20,17 +20,10 @@
#include "socket.h"
static const char *sock_createstrerror(int err);
static const char *sock_bindstrerror(int err);
static const char *sock_connectstrerror(int err);
static const char *sock_acceptstrerror(int err);
static const char *sock_listenstrerror(int err);
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int sock_open(void)
{
int sock_open(void) {
#if DOESNT_COMPILE_TRY_THIS
struct sigaction ignore;
memset(&ignore, 0, sizeof(ignore));
@ -45,16 +38,14 @@ int sock_open(void)
/*-------------------------------------------------------------------------*\
* Close module
\*-------------------------------------------------------------------------*/
int sock_close(void)
{
int sock_close(void) {
return 1;
}
/*-------------------------------------------------------------------------*\
* Close and inutilize socket
\*-------------------------------------------------------------------------*/
void sock_destroy(p_sock ps)
{
void sock_destroy(p_sock ps) {
if (*ps != SOCK_INVALID) {
sock_setblocking(ps);
close(*ps);
@ -63,23 +54,26 @@ void sock_destroy(p_sock ps)
}
/*-------------------------------------------------------------------------*\
* Select with int timeout in ms
* Select with timeout control
\*-------------------------------------------------------------------------*/
int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, int timeout)
{
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
return select(n, rfds, wfds, efds, timeout >= 0? &tv: NULL);
int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_tm tm) {
int ret;
do {
struct timeval tv;
double t = tm_getretry(tm);
tv.tv_sec = (int) t;
tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6);
ret = select(n, rfds, wfds, efds, t >= 0.0? &tv: NULL);
} while (ret < 0 && errno == EINTR);
return ret;
}
/*-------------------------------------------------------------------------*\
* Creates and sets up a socket
\*-------------------------------------------------------------------------*/
const char *sock_create(p_sock ps, int domain, int type, int protocol)
{
const char *sock_create(p_sock ps, int domain, int type, int protocol) {
t_sock sock = socket(domain, type, protocol);
if (sock == SOCK_INVALID) return sock_createstrerror(errno);
if (sock == SOCK_INVALID) return sock_strerror();
*ps = sock;
return NULL;
}
@ -87,50 +81,49 @@ const char *sock_create(p_sock ps, int domain, int type, int protocol)
/*-------------------------------------------------------------------------*\
* Connects or returns error message
\*-------------------------------------------------------------------------*/
const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, p_tm tm)
{
const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, p_tm tm) {
t_sock sock = *ps;
int err;
/* don't call on closed socket */
if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED);
if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED);
/* ask system to connect */
do err = connect(sock, addr, addr_len);
while (err < 0 && errno == EINTR);
/* if no error, we're done */
if (err == 0) return NULL;
/* make sure the system is trying to connect */
if (errno != EINPROGRESS) return sock_connectstrerror(errno);
if (errno != EINPROGRESS) return sock_strerror();
/* optimize for timeout = 0 */
if (tm_get(tm) == 0.0) return io_strerror(IO_TIMEOUT);
/* wait for a timeout or for the system's answer */
for ( ;; ) {
fd_set rfds, wfds, efds;
fd_set rfds, wfds;
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 */
do err = sock_select(sock+1, &rfds, &wfds, &efds, tm_getretry(tm));
while (err < 0 && errno == EINTR);
/* if selects readable, try reading */
err = sock_select(sock+1, &rfds, &wfds, NULL, tm);
/* if there was an event, check what happened */
if (err > 0) {
char dummy;
/* recv will set errno to the value a blocking connect would set */
if (recv(sock, &dummy, 0, 0) < 0 && errno != EAGAIN)
return sock_connectstrerror(errno);
if (err > 1 && FD_ISSET(sock, &rfds) &&
recv(sock, &dummy, 0, 0) < 0 && errno != EAGAIN)
return sock_strerror();
else
return NULL;
/* if no event happened, there was a timeout */
} else return io_strerror(IO_TIMEOUT);
} else if (err == 0) return io_strerror(IO_TIMEOUT);
}
return io_strerror(IO_TIMEOUT); /* can't get here */
return sock_strerror();
}
/*-------------------------------------------------------------------------*\
* Binds or returns error message
\*-------------------------------------------------------------------------*/
const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len)
{
const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len) {
const char *err = NULL;
sock_setblocking(ps);
if (bind(*ps, addr, addr_len) < 0) err = sock_bindstrerror(errno);
if (bind(*ps, addr, addr_len) < 0) err = sock_strerror();
sock_setnonblocking(ps);
return err;
}
@ -138,12 +131,10 @@ const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len)
/*-------------------------------------------------------------------------*\
*
\*-------------------------------------------------------------------------*/
const char* sock_listen(p_sock ps, int backlog)
{
const char* sock_listen(p_sock ps, int backlog) {
const char *err = NULL;
sock_setblocking(ps);
if (listen(*ps, backlog))
err = sock_listenstrerror(errno);
if (listen(*ps, backlog)) err = sock_strerror();
sock_setnonblocking(ps);
return err;
}
@ -151,8 +142,7 @@ const char* sock_listen(p_sock ps, int backlog)
/*-------------------------------------------------------------------------*\
*
\*-------------------------------------------------------------------------*/
void sock_shutdown(p_sock ps, int how)
{
void sock_shutdown(p_sock ps, int how) {
sock_setblocking(ps);
shutdown(*ps, how);
sock_setnonblocking(ps);
@ -162,12 +152,11 @@ void sock_shutdown(p_sock ps, int how)
* Accept with timeout
\*-------------------------------------------------------------------------*/
const char *sock_accept(p_sock ps, p_sock pa, SA *addr,
socklen_t *addr_len, p_tm tm)
{
socklen_t *addr_len, p_tm tm) {
t_sock sock = *ps;
SA dummy_addr;
socklen_t dummy_len = sizeof(dummy_addr);
if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED);
if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED);
if (!addr) addr = &dummy_addr;
if (!addr_len) addr_len = &dummy_len;
for (;;) {
@ -179,14 +168,16 @@ const char *sock_accept(p_sock ps, p_sock pa, SA *addr,
/* if result is valid, we are done */
if (*pa != SOCK_INVALID) return NULL;
/* find out if we failed for a fatal reason */
if (errno != EAGAIN && errno != ECONNABORTED)
return sock_acceptstrerror(errno);
/* if connection was aborted, we can try again if we have time */
if (errno != EAGAIN && errno != ECONNABORTED) return sock_strerror();
/* optimize for timeout = 0 case */
if (tm_get(tm) == 0.0) return io_strerror(IO_TIMEOUT);
/* call select to avoid busy-wait. */
FD_ZERO(&fds);
FD_SET(sock, &fds);
do err = sock_select(sock+1, &fds, NULL, NULL, tm_getretry(tm));
while (err < 0 && errno == EINTR);
err = sock_select(sock+1, &fds, NULL, NULL, tm);
if (err == 0) return io_strerror(IO_TIMEOUT);
else if (err < 0) return sock_strerror();
}
return io_strerror(IO_TIMEOUT); /* can't get here */
}
@ -194,136 +185,136 @@ const char *sock_accept(p_sock ps, p_sock pa, SA *addr,
/*-------------------------------------------------------------------------*\
* Send with timeout
\*-------------------------------------------------------------------------*/
int sock_send(p_sock ps, const char *data, size_t count, size_t *sent,
int timeout)
int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, p_tm tm)
{
t_sock sock = *ps;
ssize_t put;
/* 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 = send(sock, data, count, 0);
while (put < 0 && errno == EINTR);
/* deal with failure */
if (put <= 0) {
/* loop until we send something or we give up on error */
for ( ;; ) {
int ret;
fd_set fds;
/* in any case, nothing has been sent */
ssize_t put;
/* make sure we repeat in case the call was interrupted */
do put = send(sock, data, count, 0);
while (put < 0 && errno == EINTR);
/* if we sent something, get out */
if (put > 0) {
*sent = put;
return IO_DONE;
}
/* deal with failure */
*sent = 0;
/* only proceed to select if no error happened */
if (errno != EAGAIN) return IO_ERROR;
/* optimize for the timeout = 0 case */
if (timeout == 0) return IO_TIMEOUT;
/* here we know the connection has been closed */
if (errno == EPIPE) return IO_CLOSED;
if (put < 0 && errno == EPIPE) return IO_CLOSED;
/* send shouldn't return zero and we can only proceed if
* there was no serious error */
if (put == 0 || errno != EAGAIN) return IO_USER;
/* optimize for the timeout = 0 case */
if (tm_get(tm) == 0.0) return IO_TIMEOUT;
/* run select to avoid busy wait */
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = sock_select(sock+1, NULL, &fds, NULL, timeout);
ret = sock_select(sock+1, NULL, &fds, NULL, tm);
if (ret == 0) return IO_TIMEOUT;
else if (ret > 0 || errno == EINTR) return IO_RETRY;
else return IO_ERROR;
/* here we successfully sent something */
} else {
*sent = put;
return IO_DONE;
}
if (ret < 0) return IO_USER;
/* otherwise, try sending again */
}
}
/*-------------------------------------------------------------------------*\
* Sendto with timeout
\*-------------------------------------------------------------------------*/
int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent,
SA *addr, socklen_t addr_len, int timeout)
SA *addr, socklen_t addr_len, p_tm tm)
{
t_sock sock = *ps;
ssize_t put;
/* avoid making system calls on closed sockets */
if (sock == SOCK_INVALID) return IO_CLOSED;
do put = sendto(sock, data, count, 0, addr, addr_len);
while (put < 0 && errno == EINTR);
if (put <= 0) {
/* loop until we send something or we give up on error */
for ( ;; ) {
int ret;
fd_set fds;
ssize_t put;
do put = sendto(sock, data, count, 0, addr, addr_len);
while (put < 0 && errno == EINTR);
if (put > 0) {
*sent = put;
return IO_DONE;
}
*sent = 0;
if (errno != EAGAIN) return IO_ERROR;
if (timeout == 0) return IO_TIMEOUT;
if (errno == EPIPE) return IO_CLOSED;
if (put < 0 && errno == EPIPE) return IO_CLOSED;
if (put == 0 || errno != EAGAIN) return IO_USER;
if (tm_get(tm) == 0.0) return IO_TIMEOUT;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = sock_select(sock+1, NULL, &fds, NULL, timeout);
ret = sock_select(sock+1, NULL, &fds, NULL, tm);
if (ret == 0) return IO_TIMEOUT;
else if (ret > 0 || errno == EINTR) return IO_RETRY;
else return IO_ERROR;
} else {
*sent = put;
return IO_DONE;
}
if (ret < 0) return IO_USER;
}
}
/*-------------------------------------------------------------------------*\
* Receive with timeout
\*-------------------------------------------------------------------------*/
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, p_tm tm) {
t_sock sock = *ps;
ssize_t taken;
if (sock == SOCK_INVALID) return IO_CLOSED;
do taken = read(sock, data, count);
while (taken < 0 && errno == EINTR);
if (taken <= 0) {
for ( ;; ) {
fd_set fds;
int ret;
ssize_t taken;
do taken = read(sock, data, count);
while (taken < 0 && errno == EINTR);
if (taken > 0) {
*got = taken;
return IO_DONE;
}
*got = 0;
if (taken == 0) return IO_CLOSED;
if (errno != EAGAIN) return IO_ERROR;
if (timeout == 0) return IO_TIMEOUT;
if (errno != EAGAIN) return IO_USER;
if (tm_get(tm) == 0.0) return IO_TIMEOUT;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = sock_select(sock+1, &fds, NULL, NULL, timeout);
ret = sock_select(sock+1, &fds, NULL, NULL, tm);
if (ret == 0) return IO_TIMEOUT;
else if (ret > 0 || errno == EINTR) return IO_RETRY;
else return IO_ERROR;
} else {
*got = taken;
return IO_DONE;
}
if (ret < 0) return IO_USER;
}
}
/*-------------------------------------------------------------------------*\
* Recvfrom with timeout
\*-------------------------------------------------------------------------*/
int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got,
SA *addr, socklen_t *addr_len, int timeout)
{
SA *addr, socklen_t *addr_len, p_tm tm) {
t_sock sock = *ps;
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);
if (taken <= 0) {
for ( ;; ) {
fd_set fds;
int ret;
ssize_t taken;
do taken = recvfrom(sock, data, count, 0, addr, addr_len);
while (taken < 0 && errno == EINTR);
if (taken > 0) {
*got = taken;
return IO_DONE;
}
*got = 0;
if (taken == 0) return IO_CLOSED;
if (errno != EAGAIN) return IO_ERROR;
if (timeout == 0) return IO_TIMEOUT;
if (errno != EAGAIN) return IO_USER;
if (tm_get(tm) == 0.0) return IO_TIMEOUT;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = sock_select(sock+1, &fds, NULL, NULL, timeout);
ret = sock_select(sock+1, &fds, NULL, NULL, tm);
if (ret == 0) return IO_TIMEOUT;
else if (ret > 0 || errno == EINTR) return IO_RETRY;
else return IO_ERROR;
} else {
*got = taken;
return IO_DONE;
}
if (ret < 0) return IO_USER;
}
}
/*-------------------------------------------------------------------------*\
* Put socket into blocking mode
\*-------------------------------------------------------------------------*/
void sock_setblocking(p_sock ps)
{
void sock_setblocking(p_sock ps) {
int flags = fcntl(*ps, F_GETFL, 0);
flags &= (~(O_NONBLOCK));
fcntl(*ps, F_SETFL, flags);
@ -332,8 +323,7 @@ void sock_setblocking(p_sock ps)
/*-------------------------------------------------------------------------*\
* Put socket into non-blocking mode
\*-------------------------------------------------------------------------*/
void sock_setnonblocking(p_sock ps)
{
void sock_setnonblocking(p_sock ps) {
int flags = fcntl(*ps, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(*ps, F_SETFL, flags);
@ -342,98 +332,20 @@ void sock_setnonblocking(p_sock ps)
/*-------------------------------------------------------------------------*\
* Error translation functions
\*-------------------------------------------------------------------------*/
/* return error messages for the known errors reported by gethostbyname */
const char *sock_hoststrerror(void)
{
switch (h_errno) {
case HOST_NOT_FOUND: return "host not found";
case NO_ADDRESS: return "valid host but no ip found";
case NO_RECOVERY: return "name server error";
case TRY_AGAIN: return "name server unavailable, try again later";
default: return "unknown error";
const char *sock_hoststrerror(void) {
return hstrerror(h_errno);
}
/* make sure important error messages are standard */
const char *sock_strerror(void) {
switch (errno) {
case EADDRINUSE:
return "address already in use";
default:
return strerror(errno);
}
}
/* return error messages for the known errors reported by socket */
static const char *sock_createstrerror(int err)
{
switch (err) {
case EPROTONOSUPPORT: return "protocol not supported";
case EACCES: return "access denied";
case EMFILE: return "process file table is full";
case ENFILE: return "kernel file table is full";
case EINVAL: return "unknown protocol or family";
case ENOBUFS: return "insuffucient buffer space";
default: return "unknown error";
}
}
/* return error messages for the known errors reported by accept */
static const char *sock_acceptstrerror(int err)
{
switch (err) {
case EAGAIN: return io_strerror(IO_RETRY);
case EBADF: return "invalid descriptor";
case ENOBUFS: case ENOMEM: return "insuffucient buffer space";
case ENOTSOCK: return "descriptor not a socket";
case EOPNOTSUPP: return "not supported";
case EINTR: return "call interrupted";
case ECONNABORTED: return "connection aborted";
case EINVAL: return "not listening";
case EMFILE: return "process file table is full";
case ENFILE: return "kernel file table is full";
case EFAULT: return "invalid memory address";
default: return "unknown error";
}
}
/* return error messages for the known errors reported by bind */
static const char *sock_bindstrerror(int err)
{
switch (err) {
case EBADF: return "invalid descriptor";
case ENOTSOCK: return "descriptor not a socket";
case EADDRNOTAVAIL: return "address unavailable in local host";
case EADDRINUSE: return "address already in use";
case EINVAL: return "already bound";
case EACCES: return "access denied";
case EFAULT: return "invalid memory address";
case ENOMEM: return "out of memory";
default: return "unknown error";
}
}
/* return error messages for the known errors reported by listen */
static const char *sock_listenstrerror(int err)
{
switch (err) {
case EADDRINUSE: return "local address already in use";
case EBADF: return "invalid descriptor";
case ENOTSOCK: return "descriptor not a socket";
case EOPNOTSUPP: return "not supported";
default: return "unknown error";
}
}
/* return error messages for the known errors reported by connect */
static const char *sock_connectstrerror(int err)
{
switch (err) {
case EBADF: return "invalid descriptor";
case EFAULT: return "invalid memory address";
case ENOTSOCK: return "descriptor not a socket";
case EADDRNOTAVAIL: return "address not available in local host";
case EISCONN: return "already connected";
case ECONNREFUSED: return "connection refused";
case ETIMEDOUT: return io_strerror(IO_TIMEOUT);
case ENETUNREACH: return "network is unreachable";
case EADDRINUSE: return "local address already in use";
case EINPROGRESS: return "would block";
case EALREADY: return "connect already in progress";
case EAGAIN: return "not enough free ports";
case EAFNOSUPPORT: return "address family not supported";
case EPERM: return "broadcast not enabled or firewall block";
default: return "unknown error";
}
const char *sock_geterr(p_sock ps, int code) {
return sock_strerror();
}

View File

@ -16,14 +16,12 @@
#include <unistd.h>
/* fnctnl function and associated constants */
#include <fcntl.h>
/* struct timeval and CLK_TCK */
#include <sys/time.h>
/* times function and struct tms */
#include <sys/times.h>
/* struct sockaddr */
#include <sys/types.h>
/* socket function */
#include <sys/socket.h>
/* struct timeval */
#include <sys/time.h>
/* gethostbyname and gethostbyaddr functions */
#include <netdb.h>
/* sigpipe handling */

View File

@ -228,6 +228,7 @@ function test_totaltimeoutsend(len, tm, sl)
data:settimeout(tm, "total")
str = string.rep("a", 2*len)
total, err, partial, elapsed = data:send(str)
print(elapsed, "!")
check_timeout(tm, sl, elapsed, err, "send", "total",
total == 2*len)
end
@ -400,27 +401,27 @@ function accept_errors()
d:setfd(c:getfd())
d:settimeout(2)
local r, e = d:accept()
assert(not r and e == "not listening", e)
print("ok")
assert(not r and e)
print("ok: ", e)
io.stderr:write("not supported: ")
local c, e = socket.udp()
assert(c, e);
d:setfd(c:getfd())
local r, e = d:accept()
assert(not r and e == "not supported" or e == "not listening", e)
print("ok")
assert(not r and e)
print("ok: ", e)
end
------------------------------------------------------------------------
function connect_errors()
io.stderr:write("connection refused: ")
local c, e = socket.connect("localhost", 1);
assert(not c and e == "connection refused", e)
print("ok")
assert(not c and e)
print("ok: ", e)
io.stderr:write("host not found: ")
local c, e = socket.connect("host.is.invalid", 1);
assert(not c and e == "host not found", e)
print("ok")
assert(not c and e, e)
print("ok: ", e)
end
------------------------------------------------------------------------
@ -432,36 +433,25 @@ function rebind_test()
s:setoption("reuseaddr", false)
r, e = s:bind("localhost", p)
assert(not r, "managed to rebind!")
assert(e == "address already in use")
print("ok")
assert(e)
print("ok: ", e)
end
------------------------------------------------------------------------
test("character line")
test_asciiline(1)
test_asciiline(17)
test_asciiline(200)
test_asciiline(4091)
test_asciiline(80199)
test_asciiline(8000000)
test_asciiline(80199)
test_asciiline(4091)
test_asciiline(200)
test_asciiline(17)
test_asciiline(1)
test("method registration")
test_methods(socket.tcp(), {
"accept",
"accept",
"bind",
"close",
"connect",
"dirty",
"getfd",
"getpeername",
"getsockname",
"listen",
"receive",
"send",
"setfd",
"setoption",
"setpeername",
"setsockname",
@ -472,15 +462,19 @@ test_methods(socket.tcp(), {
test_methods(socket.udp(), {
"close",
"getpeername",
"dirty",
"getfd",
"getpeername",
"getsockname",
"receive",
"receivefrom",
"send",
"sendto",
"setfd",
"setoption",
"setpeername",
"setsockname",
"settimeout",
"settimeout"
})
test("select function")
@ -504,7 +498,18 @@ test("accept function: ")
accept_timeout()
accept_errors()
test("character line")
test_asciiline(1)
test_asciiline(17)
test_asciiline(200)
test_asciiline(4091)
test_asciiline(80199)
test_asciiline(8000000)
test_asciiline(80199)
test_asciiline(4091)
test_asciiline(200)
test_asciiline(17)
test_asciiline(1)
test("mixed patterns")
test_mixed(1)
@ -566,9 +571,10 @@ test_raw(1)
test("total timeout on send")
test_totaltimeoutsend(800091, 1, 3)
test_totaltimeoutsend(800091, 2, 3)
test_totaltimeoutsend(800091, 3, 2)
test_totaltimeoutsend(800091, 5, 2)
test_totaltimeoutsend(800091, 3, 1)
test("total timeout on receive")
test_totaltimeoutreceive(800091, 1, 3)
test_totaltimeoutreceive(800091, 2, 3)