From 41643c2643751de17125da4c23b616840dc0f1e3 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Sat, 13 Jan 2001 07:10:00 +0000 Subject: [PATCH] All input from sockets is now buffered. This has drastically improved line I/O. The code is much simpler now, too. All timeout management has been rewritten. --- src/luasocket.c | 596 ++++++++++++++++++++++-------------------------- 1 file changed, 274 insertions(+), 322 deletions(-) diff --git a/src/luasocket.c b/src/luasocket.c index 2216b90..4f03ad2 100644 --- a/src/luasocket.c +++ b/src/luasocket.c @@ -6,9 +6,7 @@ * Module: LUASOCKET.C * * This module is part of an effort to make the most important features -* of the TCP/IP protocol available for Lua scripts. -* The main intent of the project was the distribution with the CGILua -* toolkit, in which is is used to implement the SMTP client functions. +* of the TCP/IP protocol available to Lua scripts. * The Lua interface to TCP/IP follows the BSD TCP/IP API closely, * trying to simplify all tasks involved in setting up a client connection * and simple server connections. @@ -21,16 +19,15 @@ /*=========================================================================*\ * Common include files \*=========================================================================*/ +#include +#include +#include #include #include -#include #include -#include -#include - -#include #include +#include #include #include "luasocket.h" @@ -41,11 +38,11 @@ #ifdef WIN32 #include #include +#else /*=========================================================================*\ * BSD include files \*=========================================================================*/ -#else /* close function */ #include /* fnctnl function and associated constants */ @@ -100,6 +97,12 @@ #define NET_TIMEOUT 0 /* operation timed out */ #define NET_CLOSED 1 /* the connection has been closed */ +/*-------------------------------------------------------------------------*\ +* Time out mode to be checked +\*-------------------------------------------------------------------------*/ +#define TM_RECEIVE 1 +#define TM_SEND 2 + /*-------------------------------------------------------------------------*\ * As far as a Lua script is concerned, there are two kind of objects * representing a socket. A client socket is an object created by the @@ -107,7 +110,7 @@ * and close. A server socket is an object created by the function bind, * and implementing the methods listen, accept and close. Lua tag values * for these objects are created in the lua_socketlibopen function, and -* passed as closure values (first argumnents to every library function, +* passed as closure values (last argumnents to every library function) # because we can't have any global variables. \*-------------------------------------------------------------------------*/ #define CLIENT_TAG -2 @@ -115,27 +118,30 @@ /*-------------------------------------------------------------------------*\ * Both socket types are stored in the same structure to simplify -* implementation. The tag value used is different, though. The timeout -* fields are not used for the server socket object. -* There are two timeout values. The block timeout specifies the maximum -* time the any IO operation performed by luasocket can be blocked waiting -* for completion. The return timeout specifies the maximum time a Lua script -* can be blocked waiting for an luasocket IO operation to complete. +* implementation. The tag value used is different, though. +* The timeout and buffer parameters are not used by server sockets. \*-------------------------------------------------------------------------*/ typedef struct t_sock { - SOCKET sock; /* operating system socket object */ - int b; /* block timeout in ms */ - int r; /* return timeout in ms */ - int blocking; /* is this socket in blocking mode? */ + /* operating system socket object */ + SOCKET sock; + /* start time of the current operation */ + int tm_start; +#ifdef _DEBUG + /* end time of current operation, for debug purposes */ + int tm_end; +#endif + /* return and blocking timeout values (-1 if no limit) */ + int tm_return, tm_block; + /* buffered I/O storage */ + unsigned char bf_buffer[LUASOCKET_BUFFERSIZE]; + /* first and last red bytes not yet passed to application */ + int bf_first, bf_last; } t_sock; typedef t_sock *p_sock; /*-------------------------------------------------------------------------*\ * Macros and internal declarations \*-------------------------------------------------------------------------*/ -/* return time since marked start in ms */ -#define time_since(start) (get_time()-start) - /* min and max macros */ #ifndef min #define min(x, y) ((x) < (y) ? x : y) @@ -157,6 +163,24 @@ static int net_receive(lua_State *L); static int net_timeout(lua_State *L); static int net_close(lua_State *L); +/* buffered I/O management */ +static const unsigned char *bf_receive(p_sock sock, int *length); +static void bf_skip(p_sock sock, int length); +static int bf_isempty(p_sock sock); + +/* timeout management */ +static int tm_timedout(p_sock sock, int mode); +static int tm_gettimeleft(p_sock sock); +static int tm_gettime(void); +static void tm_markstart(p_sock sock); + +/* I/O */ +static int send_raw(p_sock sock, const char *data, int wanted, int *err); +static int receive_raw(lua_State *L, p_sock sock, int wanted); +static int receive_dosline(lua_State *L, p_sock sock); +static int receive_unixline(lua_State *L, p_sock sock); +static int receive_all(lua_State *L, p_sock sock); + /* fallbacks */ static int server_gettable(lua_State *L); static int client_gettable(lua_State *L); @@ -170,7 +194,7 @@ static p_sock check_sock(lua_State *L, int numArg, int server_tag, static void pop_tags(lua_State *L, int *client_tag, int *server_tag); static void push_tags(lua_State *L, int client_tag, int server_tag); -/* result handling routines */ +/* error code translations functions */ static char *host_strerror(void); static char *bind_strerror(void); static char *sock_strerror(void); @@ -181,7 +205,6 @@ static void push_client(lua_State *L, p_sock sock, int client_tag); static void push_server(lua_State *L, p_sock sock, int server_tag); /* plataform specific functions */ -static int get_time(void); static void set_blocking(p_sock sock); static void set_nonblocking(p_sock sock); @@ -190,19 +213,6 @@ static p_sock create_sock(void); static p_sock create_tcpsock(void); static int fill_sockaddr(struct sockaddr_in *server, const char *hostname, unsigned short port); -static int get_timeout(p_sock sock, int elapsed); -static int read_or_timeout(p_sock sock, int elapsed); -static int write_or_timeout(p_sock sock, int elapsed); -static int send_raw(p_sock sock, const char *data, int wanted, - int start, int *err, int *end); -static void receive_raw(lua_State *L, p_sock sock, int wanted, - int start, int *err, int *end); -static void receive_dosline(lua_State *L, p_sock sock, int start, - int *err, int *end); -static void receive_unixline(lua_State *L, p_sock sock, int start, - int *err, int *end); -static void receive_all(lua_State *L, p_sock sock, int start, - int *err, int *end); /*=========================================================================*\ * Test support functions @@ -214,7 +224,7 @@ static void receive_all(lua_State *L, p_sock sock, int start, static int net_time(lua_State *L); static int net_time(lua_State *L) { - lua_pushnumber(L, get_time()/1000.0); + lua_pushnumber(L, tm_gettime()/1000.0); return 1; } @@ -279,6 +289,7 @@ static int net_connect(lua_State *L) lua_pushstring(L, connect_strerror()); return 2; } + set_nonblocking(sock); push_client(L, sock, client_tag); lua_pushnil(L); return 2; @@ -341,6 +352,7 @@ static int net_accept(lua_State *L) return 2; } else { client->sock = client_sock; + set_nonblocking(client); push_client(L, client, client_tag); lua_pushnil(L); return 2; @@ -431,10 +443,10 @@ static int net_timeout(lua_State *L) mode = luaL_opt_string(L, 3, "b"); switch (*mode) { case 'b': - sock->b = ms; + sock->tm_block = ms; break; case 'r': - sock->r = ms; + sock->tm_return = ms; break; default: luaL_arg_check(L, 0, 3, "invalid timeout mode"); @@ -459,35 +471,26 @@ static int net_send(lua_State *L) { p_sock sock; const char *data; - size_t size; - int start = get_time(); + int wanted; long total = 0; int arg; int err = NET_DONE; - int end; int top; int client_tag, server_tag; pop_tags(L, &client_tag, &server_tag); top = lua_gettop(L); sock = check_client(L, 1, client_tag); -#ifdef _DEBUG_BLOCK -printf("luasocket: send start\n"); -#endif + tm_markstart(sock); for (arg = 2; arg <= top; arg++) { - data = luaL_opt_lstr(L, arg, NULL, &size); - if (!data || err != NET_DONE) - break; - total += send_raw(sock, data, size, start, &err, &end); + data = luaL_opt_lstr(L, arg, NULL, &wanted); + if (!data || err != NET_DONE) break; + total += send_raw(sock, data, wanted, &err); } push_error(L, err); lua_pushnumber(L, (double) total); -#ifdef _DEBUG_BLOCK -printf("luasocket: send end\n"); -#endif #ifdef _DEBUG -/* pass the time elapsed during function execution to Lua, so that -** the test script can make sure we respected the timeouts */ -lua_pushnumber(L, (end-start)/1000.0); + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, (sock->tm_end - sock->tm_start)/1000.0); #endif return lua_gettop(L) - top; } @@ -501,33 +504,31 @@ lua_pushnumber(L, (end-start)/1000.0); * by a LF character, preceded or not by a CR character. This is * the default pattern * "*lu": reads a text line, terminanted by a CR character only. (Unix mode) +* "*a": reads until connection closed * number: reads 'number' characters from the socket * Returns * On success: one string for each pattern * On error: all strings for which there was no error, followed by one -* nil value for each failed string, followed by an error code +* nil value for the remaining strings, followed by an error code \*-------------------------------------------------------------------------*/ static int net_receive(lua_State *L) { static const char *const modenames[] = {"*l", "*lu", "*a", NULL}; int err = NET_DONE, arg = 2; - int start = get_time(); - int end; + const char *mode; int client_tag, server_tag; int top; p_sock sock; - const char *mode; pop_tags(L, &client_tag, &server_tag); sock = check_client(L, 1, client_tag); -#ifdef _DEBUG_BLOCK -printf("luasocket: receive start\n"); -#endif + tm_markstart(sock); /* push default pattern */ top = lua_gettop(L); if (top < 2) { lua_pushstring(L, "*l"); top++; } + /* receive all patterns */ for (arg = 2; arg <= top; arg++) { /* if one pattern failed, we just skip all other patterns */ if (err != NET_DONE) { @@ -536,24 +537,24 @@ printf("luasocket: receive start\n"); } if (lua_isnumber(L, arg)) { long size = (long) lua_tonumber(L, arg); - receive_raw(L, sock, size, start, &err, &end); + err = receive_raw(L, sock, size); } else { mode = luaL_opt_string(L, arg, NULL); /* get next pattern */ switch (luaL_findstring(mode, modenames)) { /* DOS line mode */ case 0: - receive_dosline(L, sock, start, &err, &end); + err = receive_dosline(L, sock); break; /* Unix line mode */ case 1: - receive_unixline(L, sock, start, &err, &end); + err = receive_unixline(L, sock); break; - /* 'Til closed mode */ + /* until closed mode */ case 2: - receive_all(L, sock, start, &err, &end); + err = receive_all(L, sock); break; - /* else it must be a number, raw mode */ + /* else it is an error */ default: luaL_arg_check(L, 0, arg, "invalid receive pattern"); break; @@ -562,13 +563,9 @@ printf("luasocket: receive start\n"); } /* last return is an error code */ push_error(L, err); -#ifdef _DEBUG_BLOCK -printf("luasocket: receive end\n"); -#endif #ifdef _DEBUG -/* pass the time elapsed during function execution to Lua, so that -** the test script can make sure we respected the timeouts */ -lua_pushnumber(L, (end-start)/1000.0); + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, (sock->tm_end - sock->tm_start)/1000.0); #endif return lua_gettop(L) - top; } @@ -598,8 +595,8 @@ static int net_close(lua_State *L) \*-------------------------------------------------------------------------*/ static int client_gettable(lua_State *L) { - static const char *const net_api[] = {"receive","send","timeout","close", - "connect", NULL}; + static const char *const net_api[] = + {"receive","send","timeout","close", "connect", NULL}; const char *idx = luaL_check_string(L, 2); int server_tag, client_tag; pop_tags(L, &client_tag, &server_tag); @@ -700,12 +697,11 @@ static void handle_sigpipe(void) static p_sock create_sock(void) { p_sock sock = (p_sock) malloc(sizeof(t_sock)); - if (!sock) - return NULL; + if (!sock) return NULL; sock->sock = -1; - sock->r = -1; - sock->b = -1; - sock->blocking = 1; + sock->tm_block = -1; + sock->tm_return = -1; + sock->bf_first = sock->bf_last = 0; return sock; } @@ -777,63 +773,27 @@ static int fill_sockaddr(struct sockaddr_in *address, const char *hostname, } /*-------------------------------------------------------------------------*\ -* Determine the time limit to be passed to the select function, given -* the time elapsed since the beginning of the operation. +* Determines how much time we have left for the current io operation +* an IO write operation. * Input * sock: socket structure being used in operation -* elapsed: time elapsed since operation started * Returns -* time limit before function return in ms or -1 in case there is no -* time limit +* the number of ms left or -1 if there is no time limit \*-------------------------------------------------------------------------*/ -static int get_timeout(p_sock sock, int elapsed) +static int tm_gettimeleft(p_sock sock) { /* no timeout */ - if (sock->b < 0 && sock->r < 0) + if (sock->tm_block < 0 && sock->tm_return < 0) return -1; /* there is no block timeout, we use the return timeout */ - if (sock->b < 0) - return max(0, sock->r - elapsed); + else if (sock->tm_block < 0) + return max(sock->tm_return - tm_gettime() + sock->tm_start, 0); /* there is no return timeout, we use the block timeout */ - else if (sock->r < 0) - return sock->b; + else if (sock->tm_return < 0) + return sock->tm_block; /* both timeouts are specified */ - else - return min(sock->b, max(0, sock->r - elapsed)); -} - -/*-------------------------------------------------------------------------*\ -* Determines if we have a timeout condition or if we can proceed with -* an IO read operation. -* Input -* sock: socket structure being used in operation -* elapsed: time elapsed since operation started -* Returns -* 1 if we can proceed, 0 if a timeou has occured -\*-------------------------------------------------------------------------*/ -static int read_or_timeout(p_sock sock, int elapsed) -{ - fd_set set; /* file descriptor set */ - struct timeval to; /* timeout structure */ - int ms = get_timeout(sock, elapsed); - int err; - /* got timeout */ - if (ms == 0) - return 0; - FD_ZERO(&set); - FD_SET(sock->sock, &set); - /* we have a limit on the time we can wait */ - if (ms > 0) { - to.tv_sec = ms / 1000; - to.tv_usec = (ms % 1000) * 1000; - err = select(sock->sock+1, &set, NULL, NULL, &to); - set_nonblocking(sock); - /* we can wait forever */ - } else { - err = select(sock->sock+1, &set, NULL, NULL, NULL); - set_blocking(sock); - } - return (err > 0); + else return min(sock->tm_block, + max(sock->tm_return - tm_gettime() + sock->tm_start, 0)); } /*-------------------------------------------------------------------------*\ @@ -841,33 +801,110 @@ static int read_or_timeout(p_sock sock, int elapsed) * an IO write operation. * Input * sock: socket structure being used in operation -* elapsed: time elapsed since operation started +* mode: TM_RECEIVE or TM_SEND * Returns -* 1 if we can proceed, 0 if a timeou has occured +* 1 if we can proceed, 0 if a timeout has occured \*-------------------------------------------------------------------------*/ -static int write_or_timeout(p_sock sock, int elapsed) +static int tm_timedout(p_sock sock, int mode) { - fd_set set; /* file descriptor set */ - struct timeval to; /* timeout structure */ - int ms = get_timeout(sock, elapsed); - int err; - /* got timeout */ - if (ms == 0) - return 0; - FD_ZERO(&set); - FD_SET(sock->sock, &set); - /* we have a limit on the time we can wait */ - if (ms > 0) { - to.tv_sec = ms / 1000; - to.tv_usec = (ms % 1000) * 1000; - err = select(sock->sock+1, NULL, &set, NULL, &to); - set_nonblocking(sock); - /* we can wait forever */ - } else { - err = select(sock->sock+1, NULL, &set, NULL, NULL); - set_blocking(sock); + fd_set fds; + int ret, delta; + fd_set *preadfds = NULL, *pwritefds = NULL; + struct timeval tm; + struct timeval *ptm = NULL; + /* find out how much time we have left, in ms */ + int ms = tm_gettimeleft(sock); + /* fill file descriptor set */ + FD_ZERO(&fds); FD_SET(sock->sock, &fds); + /* fill timeval structure */ + tm.tv_sec = ms / 1000; + tm.tv_usec = (ms % 1000) * 1000; + /* define function parameters */ + if (ms > 0) ptm = &tm; /* ptm == NULL when we don't have timeout */ + if (mode == TM_RECEIVE) preadfds = &fds; + else pwritefds = &fds; + delta = tm_gettime(); + /* see if we can read or write or if we timedout */ + ret = select(sock->sock+1, preadfds, pwritefds, NULL, ptm); +#ifdef _DEBUG + /* store end time for this operation before calling select */ + sock->tm_end = tm_gettime(); +#endif + return ret <= 0; +} + +/*-------------------------------------------------------------------------*\ +* Marks the operation start time in sock structure +* Input +* sock: socket structure being used in operation +\*-------------------------------------------------------------------------*/ +static void tm_markstart(p_sock sock) +{ + sock->tm_start = tm_gettime(); +#ifdef _DEBUG + sock->tm_end = sock->tm_start; +#endif +} + +/*-------------------------------------------------------------------------*\ +* Determines of there is any data in the read buffer +* Input +* sock: socket structure being used in operation +* Returns +* 1 if empty, 0 if there is data +\*-------------------------------------------------------------------------*/ +static int bf_isempty(p_sock sock) +{ + return sock->bf_first >= sock->bf_last; +} + +/*-------------------------------------------------------------------------*\ +* Skip a given number of bytes in read buffer +* Input +* sock: socket structure being used in operation +* length: number of bytes to skip +\*-------------------------------------------------------------------------*/ +static void bf_skip(p_sock sock, int length) +{ + sock->bf_first += length; + if (bf_isempty(sock)) sock->bf_first = sock->bf_last = 0; +} + +/*-------------------------------------------------------------------------*\ +* Return any data avilable in buffer, or get more data from transport layer +* if there is none. +* Input +* sock: socket structure being used in operation +* Output +* length: number of bytes available in buffer +* Returns +* pointer to start of data +\*-------------------------------------------------------------------------*/ +static const unsigned char *bf_receive(p_sock sock, int *length) +{ + if (bf_isempty(sock)) { + int got = recv(sock->sock, sock->bf_buffer, LUASOCKET_BUFFERSIZE, 0); + sock->bf_first = 0; + if (got >= 0) sock->bf_last = got; + else sock->bf_last = 0; } - return (err > 0); + *length = sock->bf_last - sock->bf_first; + return sock->bf_buffer + sock->bf_first; +} + +/*-------------------------------------------------------------------------*\ +* Gets time in ms, relative to system startup. +* Returns +* time in ms. +\*-------------------------------------------------------------------------*/ +static int tm_gettime(void) +{ +#ifdef _WIN32 + return GetTickCount(); +#else + struct tms t; + return (times(&t)*1000)/CLK_TCK; +#endif } /*-------------------------------------------------------------------------*\ @@ -877,49 +914,24 @@ static int write_or_timeout(p_sock sock, int elapsed) * sock: socket structure being used in operation * data: buffer to be sent * wanted: number of bytes in buffer -* start: time the operation started, in ms * Output * err: operation error code. NET_DONE, NET_TIMEOUT or NET_CLOSED * Returns * Number of bytes written \*-------------------------------------------------------------------------*/ -static int send_raw(p_sock sock, const char *data, int wanted, - int start, int *err, int *end) +static int send_raw(p_sock sock, const char *data, int wanted, int *err) { int put = 0, total = 0; - *end = start; while (wanted > 0) { - if(!write_or_timeout(sock, time_since(start))) { -#ifdef _DEBUG -*end = get_time(); -#endif + if (tm_timedout(sock, TM_SEND)) { *err = NET_TIMEOUT; return total; } -#ifdef _DEBUG -/* the lua_pushlstring function can take a long time to pass a large block -** to Lua, therefore, we mark the time before passing the result. -** also, the call to write or read might take longer then the time we had -** left, so that the end of the operation is marked before the last call -** to the OS */ -*end = get_time(); -#endif put = send(sock->sock, data, wanted, 0); if (put <= 0) { -#ifdef WIN32 - /* on WinSock, a select over a socket on which there is a - ** non-blocking operation pending returns immediately, even - ** if the call would block. therefore, we have to do a busy - ** wait here. */ - if (WSAGetLastError() == WSAEWOULDBLOCK) - continue; -#endif *err = NET_CLOSED; return total; } -#ifdef _DEBUG_BLOCK -printf("luasocket: sent %d, wanted %d, %dms elapsed\n", put, wanted, time_since(start)); -#endif wanted -= put; data += put; total += put; @@ -934,180 +946,146 @@ printf("luasocket: sent %d, wanted %d, %dms elapsed\n", put, wanted, time_since( * Input * sock: socket structure being used in operation * wanted: number of bytes to be read -* start: time the operation started, in ms -* Output -* err: operation error code. NET_DONE, NET_TIMEOUT or NET_CLOSED * Returns -* Number of bytes read +* operation error code. NET_DONE, NET_TIMEOUT or NET_CLOSED \*-------------------------------------------------------------------------*/ -#define MIN(x,y) ((x)<(y)?(x):(y)) -static void receive_raw(lua_State *L, p_sock sock, int wanted, int start, - int *err, int *end) +static int receive_raw(lua_State *L, p_sock sock, int wanted) { int got = 0; - char *buffer = NULL; + const unsigned char *buffer = NULL; luaL_Buffer b; - *end = start; luaL_buffinit(L, &b); while (wanted > 0) { - if(!read_or_timeout(sock, time_since(start))) { -#ifdef _DEBUG -*end = get_time(); -#endif - *err = NET_TIMEOUT; + if (bf_isempty(sock) && tm_timedout(sock, TM_RECEIVE)) { luaL_pushresult(&b); - return; + return NET_TIMEOUT; } -#ifdef _DEBUG -*end = get_time(); -#endif - buffer = luaL_prepbuffer(&b); - got = recv(sock->sock, buffer, MIN(wanted, LUAL_BUFFERSIZE), 0); -#ifdef _DEBUG_BLOCK -printf("luasocket: wanted %d, got %d, %dms elapsed\n", wanted, got, time_since(start)); -#endif + buffer = bf_receive(sock, &got); if (got <= 0) { - *err = NET_CLOSED; luaL_pushresult(&b); - return; + return NET_CLOSED; } + got = min(got, wanted); + luaL_addlstring(&b, buffer, got); + bf_skip(sock, got); wanted -= got; - luaL_addsize(&b, got); } - *err = NET_DONE; luaL_pushresult(&b); + return NET_DONE; } /*-------------------------------------------------------------------------*\ * Reads everything until the connection is closed * Input * sock: socket structure being used in operation -* start: time the operation started, in ms -* Output -* err: operation error code. NET_DONE, NET_TIMEOUT or NET_CLOSED * Result -* a string is pushed into the Lua stack with the line just read +* operation error code. NET_DONE, NET_TIMEOUT or NET_CLOSED \*-------------------------------------------------------------------------*/ -static void receive_all(lua_State *L, p_sock sock, int start, - int *err, int *end) +static int receive_all(lua_State *L, p_sock sock) { - int got; - char *buffer; + int got = 0; + const unsigned char *buffer = NULL; luaL_Buffer b; - *end = start; luaL_buffinit(L, &b); for ( ;; ) { - buffer = luaL_prepbuffer(&b); - if (read_or_timeout(sock, time_since(start))) { -#ifdef _DEBUG -*end = get_time(); -#endif - got = recv(sock->sock, buffer, LUAL_BUFFERSIZE, 0); + if (bf_isempty(sock) && tm_timedout(sock, TM_RECEIVE)) { + buffer = bf_receive(sock, &got); if (got <= 0) { - *err = NET_DONE; - break; + luaL_pushresult(&b); + return NET_DONE; } - luaL_addsize(&b, got); + luaL_addlstring(&b, buffer, got); } else { - *err = NET_TIMEOUT; - break; + luaL_pushresult(&b); + return NET_TIMEOUT; } } - luaL_pushresult(&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. All operations are non-blocking and the -* function respects the timeout values in sock. +* are not returned by the function and are discarded from the stream. All +* operations are non-blocking and the function respects the timeout +* values in sock. * Input * sock: socket structure being used in operation -* wanted: number of bytes in buffer -* start: time the operation started, in ms -* Output -* data: pointer to an internal buffer containing the data read -* err: operation error code. NET_DONE, NET_TIMEOUT or NET_CLOSED * Result -* a string is pushed into the Lua stack with the line just read +* operation error code. NET_DONE, NET_TIMEOUT or NET_CLOSED \*-------------------------------------------------------------------------*/ -static void receive_dosline(lua_State *L, p_sock sock, int start, - int *err, int *end) +static int receive_dosline(lua_State *L, p_sock sock) { - char c = ' '; - long got = 0; + int got = 0; + const unsigned char *buffer = NULL; luaL_Buffer b; - *end = start; luaL_buffinit(L, &b); for ( ;; ) { - if (read_or_timeout(sock, time_since(start))) { -#ifdef _DEBUG -*end = get_time(); -#endif - got = recv(sock->sock, &c, 1, 0); - if (got <= 0) { - *err = NET_CLOSED; - break; - } - if (c != '\n') { - if (c != '\r') luaL_putchar(&b, c); - } else { - *err = NET_DONE; - break; + if (bf_isempty(sock) && tm_timedout(sock, TM_RECEIVE)) { + luaL_pushresult(&b); + return NET_TIMEOUT; + } + buffer = bf_receive(sock, &got); + if (got > 0) { + int len = 0, end = 1; + while (len < got) { + if (buffer[len] == '\n') { /* found eol */ + if (len > 0 && buffer[len-1] == '\r') { + end++; len--; + } + luaL_addlstring(&b, buffer, len); + bf_skip(sock, len + end); /* skip '\r\n' in stream */ + luaL_pushresult(&b); + return NET_DONE; + } + len++; } + luaL_addlstring(&b, buffer, got); + bf_skip(sock, got); } else { - *err = NET_TIMEOUT; - break; + luaL_pushresult(&b); + return NET_CLOSED; } } - luaL_pushresult(&b); } /*-------------------------------------------------------------------------*\ * Reads a line terminated by a LF character, which is not returned by -* the function. All operations are non-blocking and the function respects -* the timeout values in sock. +* the function, and is skipped in the stream. All operations are +* non-blocking and the function respects the timeout values in sock. * Input * sock: socket structure being used in operation -* wanted: number of bytes in buffer -* start: time the operation started, in ms -* Output -* data: pointer to an internal buffer containing the data read -* err: operation error code. NET_DONE, NET_TIMEOUT or NET_CLOSED * Returns -* Number of bytes read +* operation error code. NET_DONE, NET_TIMEOUT or NET_CLOSED \*-------------------------------------------------------------------------*/ -static void receive_unixline(lua_State *L, p_sock sock, int start, - int *err, int *end) +static int receive_unixline(lua_State *L, p_sock sock) { - char c = ' '; - long got = 0; - long size = 0; + int got = 0; + const unsigned char *buffer = NULL; luaL_Buffer b; - *end = start; luaL_buffinit(L, &b); for ( ;; ) { - if (read_or_timeout(sock, time_since(start))) { -#ifdef _DEBUG -*end = get_time(); -#endif - got = recv(sock->sock, &c, 1, 0); - if (got <= 0) { - *err = NET_CLOSED; - break; - } - if (c != '\n') { - luaL_putchar(&b, c); - size++; - } else { - *err = NET_DONE; - break; + if (bf_isempty(sock) && tm_timedout(sock, TM_RECEIVE)) { + luaL_pushresult(&b); + return NET_TIMEOUT; + } + buffer = bf_receive(sock, &got); + if (got > 0) { + int len = 0; + while (len < got) { + if (buffer[len] == '\n') { /* found eol */ + luaL_addlstring(&b, buffer, len); + bf_skip(sock, len + 1); /* skip '\n' in stream */ + luaL_pushresult(&b); + return NET_DONE; + } + len++; } + luaL_addlstring(&b, buffer, got); + bf_skip(sock, got); } else { - *err = NET_TIMEOUT; - break; + luaL_pushresult(&b); + return NET_CLOSED; } } - luaL_pushresult(&b); } /*-------------------------------------------------------------------------*\ @@ -1204,15 +1182,6 @@ static int wsock_open(void) return 1; } -/*-------------------------------------------------------------------------*\ -* Gets time in ms, relative to system startup. -* Returns -* time in ms. -\*-------------------------------------------------------------------------*/ -static int get_time(void) -{ - return GetTickCount(); -} /*-------------------------------------------------------------------------*\ * Put socket into blocking mode. @@ -1303,28 +1272,14 @@ static char *connect_strerror(void) /*=========================================================================*\ * BSD specific functions. \*=========================================================================*/ -/*-------------------------------------------------------------------------*\ -* Gets time in ms, relative to system startup. -* Returns -* time in ms. -\*-------------------------------------------------------------------------*/ -static int get_time(void) -{ - struct tms t; - return (times(&t)*1000)/CLK_TCK; -} - /*-------------------------------------------------------------------------*\ * Put socket into blocking mode. \*-------------------------------------------------------------------------*/ static void set_blocking(p_sock sock) { - if (!sock->blocking) { - int flags = fcntl(sock->sock, F_GETFL, 0); - flags &= (~(O_NONBLOCK)); - fcntl(sock->sock, F_SETFL, flags); - sock->blocking = 1; - } + int flags = fcntl(sock->sock, F_GETFL, 0); + flags &= (~(O_NONBLOCK)); + fcntl(sock->sock, F_SETFL, flags); } /*-------------------------------------------------------------------------*\ @@ -1332,12 +1287,9 @@ static void set_blocking(p_sock sock) \*-------------------------------------------------------------------------*/ static void set_nonblocking(p_sock sock) { - if (sock->blocking) { - int flags = fcntl(sock->sock, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(sock->sock, F_SETFL, flags); - sock->blocking = 0; - } + int flags = fcntl(sock->sock, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(sock->sock, F_SETFL, flags); } /*-------------------------------------------------------------------------*\