From 71f6bb60bf2b7457091c7106190f92ab7e51f7c6 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Thu, 26 Jun 2003 18:47:49 +0000 Subject: [PATCH] Finished implementation of LuaSocket 2.0 alpha on Linux. Some testing still needed. --- NEW | 4 +- TODO | 26 +++--- etc/check-links.lua | 2 +- etc/dict.lua | 2 +- etc/get.lua | 2 +- etc/tftp.lua | 2 +- samples/daytimeclnt.lua | 2 +- samples/echoclnt.lua | 2 +- samples/echosrvr.lua | 2 +- samples/listener.lua | 2 +- samples/talker.lua | 2 +- samples/tinyirc.lua | 2 +- src/auxiliar.c | 191 +++++++++++++++++++++++----------------- src/auxiliar.h | 48 +++++++--- src/buffer.c | 33 +++---- src/buffer.h | 27 +++--- src/ftp.lua | 2 +- src/http.lua | 2 +- src/inet.c | 43 +-------- src/inet.h | 23 +++-- src/io.c | 40 +++++++++ src/io.h | 29 ++++-- src/luasocket.c | 20 ++--- src/luasocket.h | 10 ++- src/select.c | 52 +++++++---- src/select.h | 18 +++- src/smtp.lua | 2 +- src/socket.h | 15 ++-- src/tcp.c | 85 +++++++++++++++++- src/tcp.h | 19 +++- src/timeout.c | 4 +- src/timeout.h | 8 +- src/udp.c | 61 +++++++++++-- src/udp.h | 17 +++- src/url.lua | 2 +- src/usocket.c | 96 ++++++++++++++------ src/usocket.h | 9 +- src/wsocket.c | 88 +++++++++++++----- src/wsocket.h | 10 +-- test/testclnt.lua | 30 ++++--- test/testsrvr.lua | 5 +- 41 files changed, 700 insertions(+), 339 deletions(-) diff --git a/NEW b/NEW index 879f12c..e5efc97 100644 --- a/NEW +++ b/NEW @@ -8,7 +8,9 @@ a given domain/family and protocol. Then connect or bind if needed. Then use IO functions. All functions return a non-nil value as first return value if successful. -All functions return nil followed by error message in case of error. +All functions return whatever could be retrieved followed by error message +in case of error. The best way to check for errors is to check for the +presence of an error message. WARNING: The send function was affected. Better error messages and parameter checking. diff --git a/TODO b/TODO index 25d2586..5c2492d 100644 --- a/TODO +++ b/TODO @@ -1,24 +1,26 @@ +- Melhorar a interface de setoptions (aceitar nada como true, por exemplo) - Inicializaccao das classes pode falhar? - Ajeitar melhor a hierarquia de classes. Ajeitar o file... +- GARBAGE COLLECTOR! +- Adicionar um método sock:setoption??? +- testar em várias plataformas +- adicionar exemplos de expansão: pipe, local, named pipe * Como mostrar um erro em lua_socketlibopen()... * O location do "redirect" pode ser relativo ao servidor atual (não pode, mas os servidores fazem merda...) -* - Ajeitar para Lua 4.1 +* Ajeitar para Lua 5.0 +* Padronizar os retornos de funccao +* Separar as classes em arquivos +* Retorno de sendto em datagram sockets pode ser refused +* Fazer compilar com g++ -- Padronizar os retornos de funccao - Thread-safe - proteger gethostby*.* com um mutex GLOBAL! - - proteger o atomizar o conjunto (timedout, receive), (timedout, send) -- Usar "require" nos módulos + - proteger ou atomizar o conjunto (timedout, receive), (timedout, send) + - inet_ntoa também é uma merda. - SSL -- Fazer compilar com g++ -- usar lua_verror -- separar as classes em arquivos -- criar mais uma classe, a de stream, entre p_sock e p_client -- criar um internal include file ls.h -- impedir que voe quando chamar accept(udpsocket()) -- trocar recv and send por read e write (ver se funciona) +- Proxy support pro http - checar operações em closed sockets - checar teste de writable socket com select @@ -34,6 +36,4 @@ - unix 92 bytes maximo no endereço, incluindo o zero - unix 9216 maximo de datagram size -- retorno de send/receive em datagram sockets pode ser refused... -- adicionar um método sock:setoption??? diff --git a/etc/check-links.lua b/etc/check-links.lua index 4c96fdc..c45131c 100644 --- a/etc/check-links.lua +++ b/etc/check-links.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- Little program that checks links in HTML files --- LuaSocket 1.5 sample files. +-- LuaSocket sample files -- Author: Diego Nehab -- RCS ID: $Id$ ----------------------------------------------------------------------------- diff --git a/etc/dict.lua b/etc/dict.lua index 89bdb4f..9926538 100644 --- a/etc/dict.lua +++ b/etc/dict.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- Little program to download DICT word definitions --- LuaSocket 1.5 sample files +-- LuaSocket sample files -- Author: Diego Nehab -- RCS ID: $Id$ ----------------------------------------------------------------------------- diff --git a/etc/get.lua b/etc/get.lua index e972d16..caaa607 100644 --- a/etc/get.lua +++ b/etc/get.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- Little program to download files from URLs --- LuaSocket 1.5 sample files +-- LuaSocket sample files -- Author: Diego Nehab -- RCS ID: $Id$ ----------------------------------------------------------------------------- diff --git a/etc/tftp.lua b/etc/tftp.lua index a0db68e..d1b5594 100644 --- a/etc/tftp.lua +++ b/etc/tftp.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- TFTP support for the Lua language --- LuaSocket 1.5 toolkit. +-- LuaSocket toolkit. -- Author: Diego Nehab -- Conforming to: RFC 783, LTN7 -- RCS ID: $Id$ diff --git a/samples/daytimeclnt.lua b/samples/daytimeclnt.lua index 85ddca1..5064fff 100644 --- a/samples/daytimeclnt.lua +++ b/samples/daytimeclnt.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- UDP sample: daytime protocol client --- LuaSocket 1.5 sample files. +-- LuaSocket sample files -- Author: Diego Nehab -- RCS ID: $Id$ ----------------------------------------------------------------------------- diff --git a/samples/echoclnt.lua b/samples/echoclnt.lua index bca0b4d..e028b86 100644 --- a/samples/echoclnt.lua +++ b/samples/echoclnt.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- UDP sample: echo protocol client --- LuaSocket 1.5 sample files +-- LuaSocket sample files -- Author: Diego Nehab -- RCS ID: $Id$ ----------------------------------------------------------------------------- diff --git a/samples/echosrvr.lua b/samples/echosrvr.lua index 18bd84e..127ccb8 100644 --- a/samples/echosrvr.lua +++ b/samples/echosrvr.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- UDP sample: echo protocol server --- LuaSocket 1.5 sample files +-- LuaSocket sample files -- Author: Diego Nehab -- RCS ID: $Id$ ----------------------------------------------------------------------------- diff --git a/samples/listener.lua b/samples/listener.lua index 4846419..dff4d25 100644 --- a/samples/listener.lua +++ b/samples/listener.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- TCP sample: Little program to dump lines received at a given port --- LuaSocket 1.5 sample files +-- LuaSocket sample files -- Author: Diego Nehab -- RCS ID: $Id$ ----------------------------------------------------------------------------- diff --git a/samples/talker.lua b/samples/talker.lua index c7a239a..1b0652f 100644 --- a/samples/talker.lua +++ b/samples/talker.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- TCP sample: Little program to send text lines to a given host/port --- LuaSocket 1.5 sample files +-- LuaSocket sample files -- Author: Diego Nehab -- RCS ID: $Id$ ----------------------------------------------------------------------------- diff --git a/samples/tinyirc.lua b/samples/tinyirc.lua index 0b20303..0ad00ab 100644 --- a/samples/tinyirc.lua +++ b/samples/tinyirc.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- Select sample: simple text line server --- LuaSocket 1.5 sample files. +-- LuaSocket sample files. -- Author: Diego Nehab -- RCS ID: $Id$ ----------------------------------------------------------------------------- diff --git a/src/auxiliar.c b/src/auxiliar.c index 96138f1..8b2fa37 100644 --- a/src/auxiliar.c +++ b/src/auxiliar.c @@ -1,142 +1,167 @@ /*=========================================================================*\ * Auxiliar routines for class hierarchy manipulation +* LuaSocket toolkit * * RCS ID: $Id$ \*=========================================================================*/ +#include + +#include "luasocket.h" #include "auxiliar.h" /*=========================================================================*\ * Exported functions \*=========================================================================*/ /*-------------------------------------------------------------------------*\ -* Creates a new class. A class has methods given by the func array and the -* field 'class' tells the object class. The table 'group' list the class -* groups the object belongs to. +* Initializes the module \*-------------------------------------------------------------------------*/ -void aux_newclass(lua_State *L, const char *name, luaL_reg *func) +void aux_open(lua_State *L) { - lua_pushstring(L, name); + /* create namespace table */ + lua_pushstring(L, LUASOCKET_LIBNAME); lua_newtable(L); - lua_pushstring(L, "__index"); - lua_newtable(L); - luaL_openlib(L, NULL, func, 0); - lua_pushstring(L, "class"); - lua_pushstring(L, name); - lua_rawset(L, -3); - lua_pushstring(L, "group"); - lua_newtable(L); - lua_rawset(L, -3); - lua_rawset(L, -3); - lua_rawset(L, LUA_REGISTRYINDEX); -} - -/*-------------------------------------------------------------------------*\ -* Add group to object list of groups. -\*-------------------------------------------------------------------------*/ -void aux_add2group(lua_State *L, const char *name, const char *group) -{ - lua_pushstring(L, name); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushstring(L, "__index"); - lua_rawget(L, -2); - lua_pushstring(L, "group"); - lua_rawget(L, -2); - lua_pushstring(L, group); +#ifdef LUASOCKET_DEBUG + lua_pushstring(L, "debug"); lua_pushnumber(L, 1); lua_rawset(L, -3); - lua_pop(L, 3); +#endif + lua_settable(L, LUA_GLOBALSINDEX); + /* make sure modules know what is our namespace */ + lua_pushstring(L, "LUASOCKET_LIBNAME"); + lua_pushstring(L, LUASOCKET_LIBNAME); + lua_settable(L, LUA_GLOBALSINDEX); } /*-------------------------------------------------------------------------*\ -* Get a userdata making sure the object belongs to a given class. +* Creates a new class with given methods \*-------------------------------------------------------------------------*/ -void *aux_checkclass(lua_State *L, const char *name, int objidx) +void aux_newclass(lua_State *L, const char *classname, luaL_reg *func) { - void *data = aux_getclassudata(L, name, objidx); + luaL_newmetatable(L, classname); /* mt */ + lua_pushstring(L, "__index"); /* mt,"__index" */ + lua_newtable(L); /* mt,"__index",it */ + luaL_openlib(L, NULL, func, 0); +#ifdef LUASOCKET_DEBUG + lua_pushstring(L, "class"); /* mt,"__index",it,"class" */ + lua_pushstring(L, classname); /* mt,"__index",it,"class",classname */ + lua_rawset(L, -3); /* mt,"__index",it */ +#endif + /* get __gc method from class and use it for garbage collection */ + lua_pushstring(L, "__gc"); /* mt,"__index",it,"__gc" */ + lua_pushstring(L, "__gc"); /* mt,"__index",it,"__gc","__gc" */ + lua_rawget(L, -3); /* mt,"__index",it,"__gc",fn */ + lua_rawset(L, -5); /* mt,"__index",it */ + lua_rawset(L, -3); /* mt */ + lua_pop(L, 1); +} + +/*-------------------------------------------------------------------------*\ +* Insert class into group +\*-------------------------------------------------------------------------*/ +void aux_add2group(lua_State *L, const char *classname, const char *groupname) +{ + luaL_getmetatable(L, classname); + lua_pushstring(L, groupname); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pop(L, 1); +} + +/*-------------------------------------------------------------------------*\ +* Make sure argument is a boolean +\*-------------------------------------------------------------------------*/ +int aux_checkboolean(lua_State *L, int objidx) +{ + if (!lua_isboolean(L, objidx)) + luaL_typerror(L, objidx, lua_typename(L, LUA_TBOOLEAN)); + return lua_toboolean(L, objidx); +} + +/*-------------------------------------------------------------------------*\ +* Calls appropriate option handler +\*-------------------------------------------------------------------------*/ +int aux_meth_setoption(lua_State *L, luaL_reg *opt) +{ + const char *name = luaL_checkstring(L, 2); /* obj, name, args */ + while (opt->name && strcmp(name, opt->name)) + opt++; + if (!opt->func) { + char msg[45]; + sprintf(msg, "unknown option `%.35s'", name); + luaL_argerror(L, 2, msg); + } + lua_remove(L, 2); /* obj, args */ + lua_pushcfunction(L, opt->func); /* obj, args, func */ + lua_insert(L, 1); /* func, obj, args */ + lua_call(L, lua_gettop(L)-1, LUA_MULTRET); + return lua_gettop(L); +} + +/*-------------------------------------------------------------------------*\ +* Return userdata pointer if object belongs to a given class, abort with +* error otherwise +\*-------------------------------------------------------------------------*/ +void *aux_checkclass(lua_State *L, const char *classname, int objidx) +{ + void *data = aux_getclassudata(L, classname, objidx); if (!data) { char msg[45]; - sprintf(msg, "%.35s expected", name); + sprintf(msg, "%.35s expected", classname); luaL_argerror(L, objidx, msg); } return data; } /*-------------------------------------------------------------------------*\ -* Get a userdata making sure the object belongs to a given group. +* Return userdata pointer if object belongs to a given group, abort with +* error otherwise \*-------------------------------------------------------------------------*/ -void *aux_checkgroup(lua_State *L, const char *group, int objidx) +void *aux_checkgroup(lua_State *L, const char *groupname, int objidx) { - void *data = aux_getgroupudata(L, group, objidx); + void *data = aux_getgroupudata(L, groupname, objidx); if (!data) { char msg[45]; - sprintf(msg, "%.35s expected", group); + sprintf(msg, "%.35s expected", groupname); luaL_argerror(L, objidx, msg); } return data; } /*-------------------------------------------------------------------------*\ -* Set object class. +* Set object class \*-------------------------------------------------------------------------*/ -void aux_setclass(lua_State *L, const char *name, int objidx) +void aux_setclass(lua_State *L, const char *classname, int objidx) { - lua_pushstring(L, name); - lua_rawget(L, LUA_REGISTRYINDEX); + luaL_getmetatable(L, classname); if (objidx < 0) objidx--; lua_setmetatable(L, objidx); } -/*=========================================================================*\ -* Internal functions -\*=========================================================================*/ /*-------------------------------------------------------------------------*\ -* Get a userdata if object belongs to a given group. +* Get a userdata pointer if object belongs to a given group. Return NULL +* otherwise \*-------------------------------------------------------------------------*/ -void *aux_getgroupudata(lua_State *L, const char *group, int objidx) +void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) { - if (!lua_getmetatable(L, objidx)) + if (!lua_getmetatable(L, objidx)) return NULL; - lua_pushstring(L, "__index"); - lua_rawget(L, -2); - if (!lua_istable(L, -1)) { - lua_pop(L, 2); - return NULL; - } - lua_pushstring(L, "group"); - lua_rawget(L, -2); - if (!lua_istable(L, -1)) { - lua_pop(L, 3); - return NULL; - } - lua_pushstring(L, group); + lua_pushstring(L, groupname); lua_rawget(L, -2); if (lua_isnil(L, -1)) { - lua_pop(L, 4); + lua_pop(L, 2); return NULL; + } else { + lua_pop(L, 2); + return lua_touserdata(L, objidx); } - lua_pop(L, 4); - return lua_touserdata(L, objidx); } /*-------------------------------------------------------------------------*\ -* Get a userdata if object belongs to a given class. +* Get a userdata pointer if object belongs to a given class. Return NULL +* otherwise \*-------------------------------------------------------------------------*/ -void *aux_getclassudata(lua_State *L, const char *group, int objidx) +void *aux_getclassudata(lua_State *L, const char *classname, int objidx) { - if (!lua_getmetatable(L, objidx)) - return NULL; - lua_pushstring(L, "__index"); - lua_rawget(L, -2); - if (!lua_istable(L, -1)) { - lua_pop(L, 2); - return NULL; - } - lua_pushstring(L, "class"); - lua_rawget(L, -2); - if (lua_isnil(L, -1)) { - lua_pop(L, 3); - return NULL; - } - lua_pop(L, 3); - return lua_touserdata(L, objidx); + return luaL_checkudata(L, objidx, classname); } + diff --git a/src/auxiliar.h b/src/auxiliar.h index 66be31d..324e800 100644 --- a/src/auxiliar.h +++ b/src/auxiliar.h @@ -1,22 +1,37 @@ +#ifndef AUX_H +#define AUX_H /*=========================================================================*\ * Auxiliar routines for class hierarchy manipulation +* LuaSocket toolkit +* +* A LuaSocket class is a name associated with Lua metatables. A LuaSocket +* group is a name associated to a class. A class can belong to any number +* of groups. This module provides the functionality to: +* +* - create new classes +* - add classes to groups +* - set the class of object +* - check if an object belongs to a given class or group +* +* LuaSocket class names follow the convention {}. Modules +* can define any number of classes and groups. The module tcp.c, for +* example, defines the classes tcp{master}, tcp{client} and tcp{server} and +* the groups tcp{client, server} and tcp{any}. Module functions can then +* perform type-checking on it's arguments by either class or group. +* +* LuaSocket metatables define the __index metamethod as being a table. This +* table has one field for each method supported by the class. In DEBUG +* mode, it also has one field with the class name. +* +* The mapping from class name to the corresponding metatable and the +* reverse mapping are done using lauxlib. * * RCS ID: $Id$ \*=========================================================================*/ -#ifndef AUX_H -#define AUX_H #include #include -void aux_newclass(lua_State *L, const char *name, luaL_reg *func); -void aux_add2group(lua_State *L, const char *name, const char *group); -void *aux_checkclass(lua_State *L, const char *name, int objidx); -void *aux_checkgroup(lua_State *L, const char *group, int objidx); -void *aux_getclassudata(lua_State *L, const char *group, int objidx); -void *aux_getgroupudata(lua_State *L, const char *group, int objidx); -void aux_setclass(lua_State *L, const char *name, int objidx); - /* min and max macros */ #ifndef MIN #define MIN(x, y) ((x) < (y) ? x : y) @@ -25,4 +40,15 @@ void aux_setclass(lua_State *L, const char *name, int objidx); #define MAX(x, y) ((x) > (y) ? x : y) #endif -#endif +void aux_open(lua_State *L); +void aux_newclass(lua_State *L, const char *classname, luaL_reg *func); +void aux_add2group(lua_State *L, const char *classname, const char *group); +void aux_setclass(lua_State *L, const char *classname, int objidx); +void *aux_checkclass(lua_State *L, const char *classname, int objidx); +void *aux_checkgroup(lua_State *L, const char *groupname, int objidx); +void *aux_getclassudata(lua_State *L, const char *groupname, int objidx); +void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx); +int aux_meth_setoption(lua_State *L, luaL_reg *opt); +int aux_checkboolean(lua_State *L, int objidx); + +#endif /* AUX_H */ diff --git a/src/buffer.c b/src/buffer.c index ab059bb..c860f35 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1,12 +1,12 @@ /*=========================================================================*\ -* Buffered input/output routines +* Input/Output interface for Lua programs +* LuaSocket toolkit * * RCS ID: $Id$ \*=========================================================================*/ #include #include -#include "error.h" #include "auxiliar.h" #include "buffer.h" @@ -42,7 +42,7 @@ void buf_init(p_buf buf, p_io io, p_tm tm) } /*-------------------------------------------------------------------------*\ -* Send data through buffered object +* object:send() interface \*-------------------------------------------------------------------------*/ int buf_meth_send(lua_State *L, p_buf buf) { @@ -59,7 +59,7 @@ int buf_meth_send(lua_State *L, p_buf buf) total += sent; } lua_pushnumber(L, total); - error_push(L, err); + io_pusherror(L, err); #ifdef LUASOCKET_DEBUG /* push time elapsed during operation as the last return value */ lua_pushnumber(L, (tm_gettime() - tm_getstart(tm))/1000.0); @@ -68,7 +68,7 @@ int buf_meth_send(lua_State *L, p_buf buf) } /*-------------------------------------------------------------------------*\ -* Receive data from a buffered object +* object:receive() interface \*-------------------------------------------------------------------------*/ int buf_meth_receive(lua_State *L, p_buf buf) { @@ -101,13 +101,13 @@ int buf_meth_receive(lua_State *L, p_buf buf) luaL_argcheck(L, 0, arg, "invalid receive pattern"); break; } - /* raw pattern */ + /* get a fixed number of bytes */ } else err = recvraw(L, buf, (size_t) lua_tonumber(L, arg)); } /* push nil for each pattern after an error */ for ( ; arg <= top; arg++) lua_pushnil(L); /* last return is an error code */ - error_push(L, err); + io_pusherror(L, err); #ifdef LUASOCKET_DEBUG /* push time elapsed during operation as the last return value */ lua_pushnumber(L, (tm_gettime() - tm_getstart(tm))/1000.0); @@ -127,9 +127,10 @@ int buf_isempty(p_buf buf) * Internal functions \*=========================================================================*/ /*-------------------------------------------------------------------------*\ -* Sends a raw block of data through a buffered object. +* 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; @@ -145,7 +146,7 @@ static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent) } /*-------------------------------------------------------------------------*\ -* Reads a raw block of data from a buffered object. +* Reads a fixed number of bytes (buffered) \*-------------------------------------------------------------------------*/ static int recvraw(lua_State *L, p_buf buf, size_t wanted) @@ -167,7 +168,7 @@ int recvraw(lua_State *L, p_buf buf, size_t wanted) } /*-------------------------------------------------------------------------*\ -* Reads everything until the connection is closed +* Reads everything until the connection is closed (buffered) \*-------------------------------------------------------------------------*/ static int recvall(lua_State *L, p_buf buf) @@ -187,12 +188,12 @@ int recvall(lua_State *L, p_buf buf) /*-------------------------------------------------------------------------*\ * 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. +* are not returned by the function and are discarded from the buffer \*-------------------------------------------------------------------------*/ static int recvline(lua_State *L, p_buf buf) { - int err = 0; + int err = IO_DONE; luaL_Buffer b; luaL_buffinit(L, &b); while (err == IO_DONE) { @@ -215,7 +216,8 @@ int recvline(lua_State *L, p_buf buf) } /*-------------------------------------------------------------------------*\ -* Skips a given number of bytes in read buffer +* 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) @@ -227,7 +229,7 @@ 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. +* if buffer is empty \*-------------------------------------------------------------------------*/ static int buf_get(p_buf buf, const char **data, size_t *count) @@ -245,3 +247,4 @@ int buf_get(p_buf buf, const char **data, size_t *count) *data = buf->data + buf->first; return err; } + diff --git a/src/buffer.h b/src/buffer.h index 1502ef0..12b90a0 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1,21 +1,31 @@ +#ifndef BUF_H +#define BUF_H /*=========================================================================*\ -* Buffered input/output routines +* Input/Output interface for Lua programs +* LuaSocket toolkit +* +* Line patterns require buffering. Reading one character at a time involves +* too many system calls and is very slow. This module implements the +* LuaSocket interface for input/output on connected objects, as seen by +* Lua programs. +* +* Input is buffered. Output is *not* buffered because there was no simple +* way of making sure the buffered output data would ever be sent. +* +* The module is built on top of the I/O abstraction defined in io.h and the +* timeout management is done with the timeout.h interface. * * RCS ID: $Id$ \*=========================================================================*/ -#ifndef BUF_H -#define BUF_H - #include + #include "io.h" #include "timeout.h" /* buffer size in bytes */ #define BUF_SIZE 8192 -/*-------------------------------------------------------------------------*\ -* Buffer control structure -\*-------------------------------------------------------------------------*/ +/* buffer control structure */ typedef struct t_buf_ { p_io io; /* IO driver used for this buffer */ p_tm tm; /* timeout management for this buffer */ @@ -24,9 +34,6 @@ typedef struct t_buf_ { } t_buf; typedef t_buf *p_buf; -/*-------------------------------------------------------------------------*\ -* Exported functions -\*-------------------------------------------------------------------------*/ void buf_open(lua_State *L); void buf_init(p_buf buf, p_io io, p_tm tm); int buf_meth_send(lua_State *L, p_buf buf); diff --git a/src/ftp.lua b/src/ftp.lua index c48f2c7..9d75d2a 100644 --- a/src/ftp.lua +++ b/src/ftp.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- FTP support for the Lua language --- LuaSocket 1.5 toolkit. +-- LuaSocket toolkit. -- Author: Diego Nehab -- Conforming to: RFC 959, LTN7 -- RCS ID: $Id$ diff --git a/src/http.lua b/src/http.lua index d531a2f..4ef2c87 100644 --- a/src/http.lua +++ b/src/http.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- HTTP/1.1 client support for the Lua language. --- LuaSocket 1.5 toolkit. +-- LuaSocket toolkit. -- Author: Diego Nehab -- Conforming to: RFC 2616, LTN7 -- RCS ID: $Id$ diff --git a/src/inet.c b/src/inet.c index b7f3ae5..312a45b 100644 --- a/src/inet.c +++ b/src/inet.c @@ -1,8 +1,10 @@ /*=========================================================================*\ * Internet domain functions +* LuaSocket toolkit * * RCS ID: $Id$ \*=========================================================================*/ +#include #include #include @@ -16,7 +18,6 @@ \*=========================================================================*/ static int inet_global_toip(lua_State *L); static int inet_global_tohostname(lua_State *L); - static void inet_pushresolved(lua_State *L, struct hostent *hp); static luaL_reg func[] = { @@ -43,11 +44,6 @@ void inet_open(lua_State *L) /*-------------------------------------------------------------------------*\ * Returns all information provided by the resolver given a host name * or ip address -* Lua Input: address -* address: ip address or hostname to dns lookup -* Lua Returns -* On success: first IP address followed by a resolved table -* On error: nil, followed by an error message \*-------------------------------------------------------------------------*/ static int inet_global_toip(lua_State *L) { @@ -72,11 +68,6 @@ static int inet_global_toip(lua_State *L) /*-------------------------------------------------------------------------*\ * Returns all information provided by the resolver given a host name * or ip address -* Lua Input: address -* address: ip address or host name to reverse dns lookup -* Lua Returns -* On success: canonic name followed by a resolved table -* On error: nil, followed by an error message \*-------------------------------------------------------------------------*/ static int inet_global_tohostname(lua_State *L) { @@ -102,11 +93,6 @@ static int inet_global_tohostname(lua_State *L) \*=========================================================================*/ /*-------------------------------------------------------------------------*\ * Retrieves socket peer name -* Input: -* sock: socket -* Lua Returns -* On success: ip address and port of peer -* On error: nil \*-------------------------------------------------------------------------*/ int inet_meth_getpeername(lua_State *L, p_sock ps) { @@ -123,11 +109,6 @@ int inet_meth_getpeername(lua_State *L, p_sock ps) /*-------------------------------------------------------------------------*\ * Retrieves socket local name -* Input: -* sock: socket -* Lua Returns -* On success: local ip address and port -* On error: nil \*-------------------------------------------------------------------------*/ int inet_meth_getsockname(lua_State *L, p_sock ps) { @@ -147,8 +128,6 @@ int inet_meth_getsockname(lua_State *L, p_sock ps) \*=========================================================================*/ /*-------------------------------------------------------------------------*\ * Passes all resolver information to Lua as a table -* Input -* hp: hostent structure returned by resolver \*-------------------------------------------------------------------------*/ static void inet_pushresolved(lua_State *L, struct hostent *hp) { @@ -185,12 +164,6 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) /*-------------------------------------------------------------------------*\ * Tries to connect to remote address (address, port) -* Input -* ps: pointer to socket -* address: host name or ip address -* port: port number to bind to -* Returns -* NULL in case of success, error message otherwise \*-------------------------------------------------------------------------*/ const char *inet_tryconnect(p_sock ps, const char *address, unsigned short port) @@ -224,12 +197,6 @@ const char *inet_tryconnect(p_sock ps, const char *address, /*-------------------------------------------------------------------------*\ * Tries to bind socket to (address, port) -* Input -* sock: pointer to socket -* address: host name or ip address -* port: port number to bind to -* Returns -* NULL in case of success, error message otherwise \*-------------------------------------------------------------------------*/ const char *inet_trybind(p_sock ps, const char *address, unsigned short port, int backlog) @@ -264,10 +231,6 @@ const char *inet_trybind(p_sock ps, const char *address, unsigned short port, /*-------------------------------------------------------------------------*\ * Tries to create a new inet socket -* Input -* sock: pointer to socket -* Returns -* NULL if successfull, error message on error \*-------------------------------------------------------------------------*/ const char *inet_trycreate(p_sock ps, int type) { @@ -299,3 +262,5 @@ int inet_aton(const char *cp, struct in_addr *inp) return 1; } #endif + + diff --git a/src/inet.h b/src/inet.h index 60be9e4..244a310 100644 --- a/src/inet.h +++ b/src/inet.h @@ -1,25 +1,30 @@ +#ifndef INET_H +#define INET_H /*=========================================================================*\ * Internet domain functions +* LuaSocket toolkit +* +* This module implements the creation and connection of internet domain +* sockets, on top of the socket.h interface, and the interface of with the +* resolver. +* +* The function inet_aton is provided for the platforms where it is not +* available. The module also implements the interface of the internet +* getpeername and getsockname functions as seen by Lua programs. +* +* The Lua functions toip and tohostname are also implemented here. * * RCS ID: $Id$ \*=========================================================================*/ -#ifndef INET_H -#define INET_H - #include #include "socket.h" -/*-------------------------------------------------------------------------*\ -* Exported functions -\*-------------------------------------------------------------------------*/ void inet_open(lua_State *L); - const char *inet_tryconnect(p_sock ps, 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); - int inet_meth_getpeername(lua_State *L, p_sock ps); int inet_meth_getsockname(lua_State *L, p_sock ps); @@ -27,4 +32,4 @@ int inet_meth_getsockname(lua_State *L, p_sock ps); int inet_aton(const char *cp, struct in_addr *inp); #endif -#endif /* INET_H_ */ +#endif /* INET_H */ diff --git a/src/io.c b/src/io.c index 902124a..2d62147 100644 --- a/src/io.c +++ b/src/io.c @@ -1,8 +1,48 @@ +/*=========================================================================*\ +* Input/Output abstraction +* LuaSocket toolkit +* +* RCS ID: $Id$ +\*=========================================================================*/ #include "io.h" +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes C structure +\*-------------------------------------------------------------------------*/ void io_init(p_io io, p_send send, p_recv recv, void *ctx) { io->send = send; io->recv = recv; io->ctx = ctx; } + +/*-------------------------------------------------------------------------*\ +* Translate error codes to Lua +\*-------------------------------------------------------------------------*/ +void io_pusherror(lua_State *L, 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; + } +} + diff --git a/src/io.h b/src/io.h index 30d445b..f56094a 100644 --- a/src/io.h +++ b/src/io.h @@ -1,16 +1,30 @@ #ifndef IO_H #define IO_H - +/*=========================================================================*\ +* Input/Output abstraction +* LuaSocket toolkit +* +* This module defines the interface that LuaSocket expects from the +* transport layer for streamed input/output. The idea is that if any +* transport implements this interface, then the buffer.c functions +* automatically work on it. +* +* The module socket.h implements this interface, and thus the module tcp.h +* is very simple. +* +* RCS ID: $Id$ +\*=========================================================================*/ #include +#include /* IO error codes */ enum { - IO_DONE, /* operation completed successfully */ - IO_TIMEOUT, /* operation timed out */ - IO_CLOSED, /* the connection has been closed */ - IO_ERROR, /* something wrong... */ - IO_REFUSED, /* transfer has been refused */ - IO_LIMITED /* maximum number of bytes reached */ + IO_DONE, /* operation completed successfully */ + IO_TIMEOUT, /* operation timed out */ + IO_CLOSED, /* the connection has been closed */ + IO_ERROR, /* something wrong... */ + IO_REFUSED, /* transfer has been refused */ + IO_LIMITED /* maximum number of bytes reached */ }; /* interface to send function */ @@ -39,6 +53,7 @@ typedef struct t_io_ { } t_io; typedef t_io *p_io; +void io_pusherror(lua_State *L, int code); void io_init(p_io io, p_send send, p_recv recv, void *ctx); #endif /* IO_H */ diff --git a/src/luasocket.c b/src/luasocket.c index 5541d7f..96deac1 100644 --- a/src/luasocket.c +++ b/src/luasocket.c @@ -1,4 +1,5 @@ /*=========================================================================*\ +* LuaSocket toolkit * Networking support for the Lua language * Diego Nehab * 26/11/1999 @@ -7,7 +8,7 @@ * connectivity of the Lua language. The Lua interface to networking * functions follows the Sockets API closely, trying to simplify all tasks * involved in setting up both client and server connections. The provided -* IO routines, however, follow the Lua style, being very similar to the +* IO routines, however, follow the Lua style, being very similar to the * standard Lua read and write functions. * * RCS ID: $Id$ @@ -24,6 +25,7 @@ \*=========================================================================*/ #include "luasocket.h" +#include "auxiliar.h" #include "timeout.h" #include "buffer.h" #include "socket.h" @@ -38,23 +40,11 @@ /*-------------------------------------------------------------------------*\ * Initializes all library modules. \*-------------------------------------------------------------------------*/ -LUASOCKET_API int luaopen_socketlib(lua_State *L) +LUASOCKET_API int luaopen_socket(lua_State *L) { if (!sock_open()) return 0; - /* create namespace table */ - lua_pushstring(L, LUASOCKET_LIBNAME); - lua_newtable(L); -#ifdef LUASOCKET_DEBUG - lua_pushstring(L, "debug"); - lua_pushnumber(L, 1); - lua_settable(L, -3); -#endif - lua_settable(L, LUA_GLOBALSINDEX); - /* make sure modules know what is our namespace */ - lua_pushstring(L, "LUASOCKET_LIBNAME"); - lua_pushstring(L, LUASOCKET_LIBNAME); - lua_settable(L, LUA_GLOBALSINDEX); /* initialize all modules */ + aux_open(L); tm_open(L); buf_open(L); inet_open(L); diff --git a/src/luasocket.h b/src/luasocket.h index 6c25af2..7756605 100644 --- a/src/luasocket.h +++ b/src/luasocket.h @@ -1,17 +1,19 @@ +#ifndef LUASOCKET_H +#define LUASOCKET_H /*=========================================================================*\ +* LuaSocket toolkit * Networking support for the Lua language * Diego Nehab * 9/11/1999 * * RCS ID: $Id$ \*=========================================================================*/ -#ifndef LUASOCKET_H -#define LUASOCKET_H +#include /*-------------------------------------------------------------------------*\ * Current luasocket version \*-------------------------------------------------------------------------*/ -#define LUASOCKET_VERSION "LuaSocket 1.5 (alpha)" +#define LUASOCKET_VERSION "LuaSocket 2.0 (alpha)" /*-------------------------------------------------------------------------*\ * Library's namespace @@ -28,6 +30,6 @@ /*-------------------------------------------------------------------------*\ * Initializes the library. \*-------------------------------------------------------------------------*/ -LUASOCKET_API int luaopen_socketlib(lua_State *L); +LUASOCKET_API int luaopen_socket(lua_State *L); #endif /* LUASOCKET_H */ diff --git a/src/select.c b/src/select.c index 3cabbd1..9769667 100644 --- a/src/select.c +++ b/src/select.c @@ -1,5 +1,7 @@ /*=========================================================================*\ * Select implementation +* LuaSocket toolkit +* * RCS ID: $Id$ \*=========================================================================*/ #include @@ -12,6 +14,9 @@ #include "auxiliar.h" #include "select.h" +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ static int meth_set(lua_State *L); static int meth_isset(lua_State *L); static int c_select(lua_State *L); @@ -31,6 +36,12 @@ static luaL_reg func[] = { {NULL, NULL} }; +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ void select_open(lua_State *L) { /* get select auxiliar lua function from lua code and register @@ -45,6 +56,9 @@ void select_open(lua_State *L) aux_newclass(L, "select{fd_set}", set); } +/*=========================================================================*\ +* Global Lua functions +\*=========================================================================*/ /*-------------------------------------------------------------------------*\ * Waits for a set of sockets until a condition is met or timeout. \*-------------------------------------------------------------------------*/ @@ -63,10 +77,10 @@ static int global_select(lua_State *L) lua_pushvalue(L, lua_upvalueindex(1)); lua_insert(L, 1); /* pass fd_set objects */ - read_fd_set = lua_newuserdata(L, sizeof(fd_set)); + read_fd_set = (fd_set *) lua_newuserdata(L, sizeof(fd_set)); FD_ZERO(read_fd_set); aux_setclass(L, "select{fd_set}", -1); - write_fd_set = lua_newuserdata(L, sizeof(fd_set)); + write_fd_set = (fd_set *) lua_newuserdata(L, sizeof(fd_set)); FD_ZERO(write_fd_set); aux_setclass(L, "select{fd_set}", -1); /* pass select auxiliar C function */ @@ -76,20 +90,9 @@ static int global_select(lua_State *L) return 3; } -static int c_select(lua_State *L) -{ - int max_fd = (int) lua_tonumber(L, 1); - fd_set *read_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 2); - fd_set *write_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 3); - int timeout = lua_isnil(L, 4) ? -1 : (int)(lua_tonumber(L, 4) * 1000); - struct timeval tv; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - lua_pushnumber(L, select(max_fd, read_fd_set, write_fd_set, NULL, - timeout < 0 ? NULL : &tv)); - return 1; -} - +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ static int meth_set(lua_State *L) { fd_set *set = (fd_set *) aux_checkclass(L, "select{fd_set}", 1); @@ -107,6 +110,23 @@ static int meth_isset(lua_State *L) return 1; } +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +static int c_select(lua_State *L) +{ + int max_fd = (int) lua_tonumber(L, 1); + fd_set *read_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 2); + fd_set *write_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 3); + int timeout = lua_isnil(L, 4) ? -1 : (int)(lua_tonumber(L, 4) * 1000); + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + lua_pushnumber(L, select(max_fd, read_fd_set, write_fd_set, NULL, + timeout < 0 ? NULL : &tv)); + return 1; +} + static void check_obj_tab(lua_State *L, int tabidx) { if (tabidx < 0) tabidx = lua_gettop(L) + tabidx + 1; diff --git a/src/select.h b/src/select.h index 9521fae..0e1eeb4 100644 --- a/src/select.h +++ b/src/select.h @@ -1,9 +1,19 @@ -/*=========================================================================*\ -* Select implementation -* RCS ID: $Id$ -\*=========================================================================*/ #ifndef SELECT_H #define SELECT_H +/*=========================================================================*\ +* Select implementation +* LuaSocket toolkit +* +* To make the code as simple as possible, the select function is +* implemented int Lua, with a few helper functions written in C. +* +* Each object that can be passed to the select function has to be in the +* group select{able} and export two methods: fd() and dirty(). Fd returns +* the descriptor to be passed to the select function. Dirty() should return +* true if there is data ready for reading (required for buffered input). +* +* RCS ID: $Id$ +\*=========================================================================*/ void select_open(lua_State *L); diff --git a/src/smtp.lua b/src/smtp.lua index 604f79b..209825b 100644 --- a/src/smtp.lua +++ b/src/smtp.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- SMTP support for the Lua language. --- LuaSocket 1.5 toolkit +-- LuaSocket toolkit -- Author: Diego Nehab -- Conforming to: RFC 821, LTN7 -- RCS ID: $Id$ diff --git a/src/socket.h b/src/socket.h index 70ebc52..c7db5f2 100644 --- a/src/socket.h +++ b/src/socket.h @@ -1,11 +1,16 @@ +#ifndef SOCK_H +#define SOCK_H /*=========================================================================*\ * Socket compatibilization module +* LuaSocket toolkit +* +* BSD Sockets and WinSock are similar, but there are a few irritating +* differences. Also, not all *nix platforms behave the same. This module +* (and the associated usocket.h and wsocket.h) factor these differences and +* creates a interface compatible with the io.h module. * * RCS ID: $Id$ \*=========================================================================*/ -#ifndef SOCK_H -#define SOCK_H - #include "io.h" /*=========================================================================*\ @@ -32,7 +37,6 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len); const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len); void sock_listen(p_sock ps, int backlog); - 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, @@ -41,11 +45,8 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent, SA *addr, socklen_t addr_len, int timeout); 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); -void sock_setreuseaddr(p_sock ps); - const char *sock_hoststrerror(void); const char *sock_createstrerror(void); const char *sock_bindstrerror(void); diff --git a/src/tcp.c b/src/tcp.c index dc7683d..28f38f5 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -1,5 +1,6 @@ /*=========================================================================*\ * TCP object +* LuaSocket toolkit * * RCS ID: $Id$ \*=========================================================================*/ @@ -13,7 +14,6 @@ #include "auxiliar.h" #include "socket.h" #include "inet.h" -#include "error.h" #include "tcp.h" /*=========================================================================*\ @@ -28,9 +28,13 @@ static int meth_getpeername(lua_State *L); static int meth_receive(lua_State *L); static int meth_accept(lua_State *L); static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); static int meth_timeout(lua_State *L); static int meth_fd(lua_State *L); static int meth_dirty(lua_State *L); +static int opt_nodelay(lua_State *L); +static int opt_keepalive(lua_State *L); +static int opt_linger(lua_State *L); /* tcp object methods */ static luaL_reg tcp[] = { @@ -45,11 +49,21 @@ static luaL_reg tcp[] = { {"getsockname", meth_getsockname}, {"timeout", meth_timeout}, {"close", meth_close}, + {"setoption", meth_setoption}, + {"__gc", meth_close}, {"fd", meth_fd}, {"dirty", meth_dirty}, {NULL, NULL} }; +/* socket option handlers */ +static luaL_reg opt[] = { + {"keepalive", opt_keepalive}, + {"nodelay", opt_nodelay}, + {"linger", opt_linger}, + {NULL, NULL} +}; + /* functions in library namespace */ static luaL_reg func[] = { {"tcp", global_create}, @@ -71,6 +85,7 @@ void tcp_open(lua_State *L) aux_add2group(L, "tcp{server}", "tcp{any}"); aux_add2group(L, "tcp{client}", "tcp{client, server}"); aux_add2group(L, "tcp{server}", "tcp{client, server}"); + /* both server and client objects are selectable */ aux_add2group(L, "tcp{client}", "select{able}"); aux_add2group(L, "tcp{server}", "select{able}"); /* define library functions */ @@ -96,19 +111,81 @@ static int meth_receive(lua_State *L) return buf_meth_receive(L, &tcp->buf); } +/*-------------------------------------------------------------------------*\ +* Option handlers +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) +{ + return aux_meth_setoption(L, opt); +} + +static int opt_boolean(lua_State *L, int level, int name) +{ + p_tcp tcp = (p_tcp) aux_checkgroup(L, "tcp{any}", 1); + int val = aux_checkboolean(L, 2); + if (setsockopt(tcp->sock, level, name, (char *) &val, sizeof(val)) < 0) { + lua_pushnil(L); + lua_pushstring(L, "setsockopt failed"); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +/* disables the Nagle algorithm */ +static int opt_nodelay(lua_State *L) +{ + struct protoent *pe = getprotobyname("TCP"); + if (!pe) { + lua_pushnil(L); + lua_pushstring(L, "getprotobyname"); + return 2; + } + return opt_boolean(L, pe->p_proto, TCP_NODELAY); +} + +static int opt_keepalive(lua_State *L) +{ + return opt_boolean(L, SOL_SOCKET, SO_KEEPALIVE); +} + +int opt_linger(lua_State *L) +{ + p_tcp tcp = (p_tcp) aux_checkclass(L, "tcp{client}", 1); + struct linger li; + if (!lua_istable(L, 2)) + luaL_typerror(L, 2, lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "onoff"); + lua_gettable(L, 2); + if (!lua_isnumber(L, -1)) luaL_argerror(L, 2, "invalid onoff field"); + li.l_onoff = (int) lua_tonumber(L, -1); + lua_pushstring(L, "linger"); + lua_gettable(L, 2); + if (!lua_isnumber(L, -1)) luaL_argerror(L, 2, "invalid linger field"); + li.l_linger = (int) lua_tonumber(L, -1); + if (setsockopt(tcp->sock, SOL_SOCKET, SO_LINGER, + (char *) &li, sizeof(li) < 0)) { + lua_pushnil(L); + lua_pushstring(L, "setsockopt failed"); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + /*-------------------------------------------------------------------------*\ * Select support methods \*-------------------------------------------------------------------------*/ static int meth_fd(lua_State *L) { - p_tcp tcp = (p_tcp) aux_checkclass(L, "tcp{client, server}", 1); + p_tcp tcp = (p_tcp) aux_checkgroup(L, "tcp{client, server}", 1); lua_pushnumber(L, tcp->sock); return 1; } static int meth_dirty(lua_State *L) { - p_tcp tcp = (p_tcp) aux_checkclass(L, "tcp{client, server}", 1); + p_tcp tcp = (p_tcp) aux_checkgroup(L, "tcp{client, server}", 1); lua_pushboolean(L, !buf_isempty(&tcp->buf)); return 1; } @@ -207,7 +284,7 @@ static int meth_accept(lua_State *L) if (client->sock == SOCK_INVALID) { if (tm_get(tm) == 0) { lua_pushnil(L); - error_push(L, IO_TIMEOUT); + io_pusherror(L, IO_TIMEOUT); return 2; } } else break; diff --git a/src/tcp.h b/src/tcp.h index f4319f3..82b88a9 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -1,6 +1,21 @@ #ifndef TCP_H #define TCP_H - +/*=========================================================================*\ +* TCP object +* LuaSocket toolkit +* +* The tcp.h module is basicly a glue that puts together modules buffer.h, +* timeout.h socket.h and inet.h to provide the LuaSocket TCP (AF_INET, +* SOCK_STREAM) support. +* +* Three classes are defined: master, client and server. The master class is +* a newly created tcp object, that has not been bound or connected. Server +* objects are tcp objects bound to some local address. Client objects are +* tcp objects either connected to some address or returned by the accept +* method of a server object. +* +* RCS ID: $Id$ +\*=========================================================================*/ #include #include "buffer.h" @@ -17,4 +32,4 @@ typedef t_tcp *p_tcp; void tcp_open(lua_State *L); -#endif +#endif /* TCP_H */ diff --git a/src/timeout.c b/src/timeout.c index 1553069..6a30e3a 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -1,8 +1,6 @@ /*=========================================================================*\ * Timeout management functions -* Global Lua functions: -* _sleep -* _time +* LuaSocket toolkit * * RCS ID: $Id$ \*=========================================================================*/ diff --git a/src/timeout.h b/src/timeout.h index 43476cb..32eb836 100644 --- a/src/timeout.h +++ b/src/timeout.h @@ -1,11 +1,11 @@ +#ifndef TM_H +#define TM_H /*=========================================================================*\ * Timeout management functions +* LuaSocket toolkit * * RCS ID: $Id$ \*=========================================================================*/ -#ifndef TM_H -#define TM_H - #include /* timeout control structure */ @@ -28,4 +28,4 @@ int tm_get(p_tm tm); int tm_gettime(void); int tm_meth_timeout(lua_State *L, p_tm tm); -#endif +#endif /* TM_H */ diff --git a/src/udp.c b/src/udp.c index 79831e7..b772b2e 100644 --- a/src/udp.c +++ b/src/udp.c @@ -1,5 +1,6 @@ /*=========================================================================*\ * UDP object +* LuaSocket toolkit * * RCS ID: $Id$ \*=========================================================================*/ @@ -13,7 +14,6 @@ #include "auxiliar.h" #include "socket.h" #include "inet.h" -#include "error.h" #include "udp.h" /*=========================================================================*\ @@ -29,9 +29,12 @@ 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_setoption(lua_State *L); static int meth_timeout(lua_State *L); static int meth_fd(lua_State *L); static int meth_dirty(lua_State *L); +static int opt_dontroute(lua_State *L); +static int opt_broadcast(lua_State *L); /* udp object methods */ static luaL_reg udp[] = { @@ -45,11 +48,20 @@ static luaL_reg udp[] = { {"receivefrom", meth_receivefrom}, {"timeout", meth_timeout}, {"close", meth_close}, + {"setoption", meth_setoption}, + {"__gc", meth_close}, {"fd", meth_fd}, {"dirty", meth_dirty}, {NULL, NULL} }; +/* socket options */ +static luaL_reg opt[] = { + {"dontroute", opt_dontroute}, + {"broadcast", opt_broadcast}, + {NULL, NULL} +}; + /* functions in library namespace */ static luaL_reg func[] = { {"udp", global_create}, @@ -91,7 +103,9 @@ static int meth_send(lua_State *L) err = sock_send(&udp->sock, data, count, &sent, tm_get(tm)); if (err == IO_DONE) lua_pushnumber(L, sent); else lua_pushnil(L); - error_push(L, err); + /* 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); return 2; } @@ -118,7 +132,9 @@ static int meth_sendto(lua_State *L) (SA *) &addr, sizeof(addr), tm_get(tm)); if (err == IO_DONE) lua_pushnumber(L, sent); else lua_pushnil(L); - error_push(L, err == IO_CLOSED ? IO_REFUSED : err); + /* 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); return 2; } @@ -137,7 +153,7 @@ static int meth_receive(lua_State *L) err = sock_recv(&udp->sock, buffer, count, &got, tm_get(tm)); if (err == IO_DONE) lua_pushlstring(L, buffer, got); else lua_pushnil(L); - error_push(L, err); + io_pusherror(L, err); return 2; } @@ -164,7 +180,7 @@ static int meth_receivefrom(lua_State *L) return 3; } else { lua_pushnil(L); - error_push(L, err); + io_pusherror(L, err); return 2; } } @@ -174,14 +190,14 @@ static int meth_receivefrom(lua_State *L) \*-------------------------------------------------------------------------*/ static int meth_fd(lua_State *L) { - p_udp udp = (p_udp) aux_checkclass(L, "udp{any}", 1); + p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); lua_pushnumber(L, udp->sock); return 1; } static int meth_dirty(lua_State *L) { - p_udp udp = (p_udp) aux_checkclass(L, "udp{any}", 1); + p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); (void) udp; lua_pushboolean(L, 0); return 1; @@ -202,6 +218,37 @@ static int meth_getsockname(lua_State *L) return inet_meth_getsockname(L, &udp->sock); } +/*-------------------------------------------------------------------------*\ +* Option handlers +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) +{ + return aux_meth_setoption(L, opt); +} + +static int opt_boolean(lua_State *L, int level, int name) +{ + p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); + int val = aux_checkboolean(L, 2); + if (setsockopt(udp->sock, level, name, (char *) &val, sizeof(val)) < 0) { + lua_pushnil(L); + lua_pushstring(L, "setsockopt failed"); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int opt_dontroute(lua_State *L) +{ + return opt_boolean(L, SOL_SOCKET, SO_DONTROUTE); +} + +static int opt_broadcast(lua_State *L) +{ + return opt_boolean(L, SOL_SOCKET, SO_BROADCAST); +} + /*-------------------------------------------------------------------------*\ * Just call tm methods \*-------------------------------------------------------------------------*/ diff --git a/src/udp.h b/src/udp.h index a6f17e2..699e31a 100644 --- a/src/udp.h +++ b/src/udp.h @@ -1,6 +1,19 @@ #ifndef UDP_H #define UDP_H - +/*=========================================================================*\ +* UDP object +* LuaSocket toolkit +* +* The udp.h module provides LuaSocket with support for UDP protocol +* (AF_INET, SOCK_DGRAM). +* +* Two classes are defined: connected and unconnected. UDP objects are +* originally unconnected. They can be "connected" to a given address +* with a call to the setpeername function. The same function can be used to +* break the connection. +* +* RCS ID: $Id$ +\*=========================================================================*/ #include #include "timeout.h" @@ -16,4 +29,4 @@ typedef t_udp *p_udp; void udp_open(lua_State *L); -#endif +#endif /* UDP_H */ diff --git a/src/url.lua b/src/url.lua index 06de9d3..65da57a 100644 --- a/src/url.lua +++ b/src/url.lua @@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- URI parsing, composition and relative URL resolution --- LuaSocket 1.5 toolkit. +-- LuaSocket toolkit. -- Author: Diego Nehab -- Conforming to: RFC 2396, LTN7 -- RCS ID: $Id$ diff --git a/src/usocket.c b/src/usocket.c index 062a0ff..cdd550c 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -1,49 +1,78 @@ +/*=========================================================================*\ +* Socket compatibilization module for Unix +* LuaSocket toolkit +* +* RCS ID: $Id$ +\*=========================================================================*/ #include #include "socket.h" +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ int sock_open(void) { - /* instals a handler to ignore sigpipe. */ - struct sigaction new; - memset(&new, 0, sizeof(new)); - new.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &new, NULL); + /* instals a handler to ignore sigpipe or it will crash us */ + struct sigaction ignore; + memset(&ignore, 0, sizeof(ignore)); + ignore.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &ignore, NULL); return 1; } +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ void sock_destroy(p_sock ps) { close(*ps); + *ps = SOCK_INVALID; } +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ const char *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(); *ps = sock; sock_setnonblocking(ps); - sock_setreuseaddr(ps); + setsockopt(*ps, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(val)); return NULL; } +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len) { if (connect(*ps, addr, addr_len) < 0) return sock_connectstrerror(); else return NULL; } +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len) { if (bind(*ps, addr, addr_len) < 0) return sock_bindstrerror(); else return NULL; } +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ void sock_listen(p_sock ps, int backlog) { listen(*ps, backlog); } +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, int timeout) { @@ -65,6 +94,9 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, else return IO_DONE; } +/*-------------------------------------------------------------------------*\ +* Send with timeout +\*-------------------------------------------------------------------------*/ int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, int timeout) { @@ -99,6 +131,9 @@ int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, } } +/*-------------------------------------------------------------------------*\ +* 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) { @@ -133,6 +168,9 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent, } } +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) { t_sock sock = *ps; @@ -160,6 +198,9 @@ int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) } } +/*-------------------------------------------------------------------------*\ +* 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) { @@ -188,6 +229,29 @@ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got, } } +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void sock_setblocking(p_sock ps) +{ + int flags = fcntl(*ps, F_GETFL, 0); + flags &= (~(O_NONBLOCK)); + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void sock_setnonblocking(p_sock ps) +{ + int flags = fcntl(*ps, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +\*-------------------------------------------------------------------------*/ const char *sock_hoststrerror(void) { switch (h_errno) { @@ -238,23 +302,3 @@ const char *sock_connectstrerror(void) default: return "unknown error"; } } - -void sock_setreuseaddr(p_sock ps) -{ - int val = 1; - setsockopt(*ps, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); -} - -void sock_setblocking(p_sock ps) -{ - int flags = fcntl(*ps, F_GETFL, 0); - flags &= (~(O_NONBLOCK)); - fcntl(*ps, F_SETFL, flags); -} - -void sock_setnonblocking(p_sock ps) -{ - int flags = fcntl(*ps, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(*ps, F_SETFL, flags); -} diff --git a/src/usocket.h b/src/usocket.h index 034ae74..85b7caa 100644 --- a/src/usocket.h +++ b/src/usocket.h @@ -1,10 +1,11 @@ +#ifndef USOCKET_H +#define USOCKET_H /*=========================================================================*\ * Socket compatibilization module for Unix +* LuaSocket toolkit * * RCS ID: $Id$ \*=========================================================================*/ -#ifndef USOCKET_H -#define USOCKET_H /*=========================================================================*\ * BSD include files @@ -30,9 +31,11 @@ /* IP stuff*/ #include #include +/* TCP options (nagle algorithm disable) */ +#include #ifdef __APPLE__ -/* for some reason socklen_t is not defined in mac os x */ +/* for some reason socklen_t is not defined in Mac Os X */ typedef int socklen_t; #endif diff --git a/src/wsocket.c b/src/wsocket.c index 56e65ec..2ce828e 100644 --- a/src/wsocket.c +++ b/src/wsocket.c @@ -1,14 +1,21 @@ +/*=========================================================================*\ +* Socket compatibilization module for Win32 +* LuaSocket toolkit +* +* RCS ID: $Id$ +\*=========================================================================*/ #include #include "socket.h" +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ int sock_open(void) { - WORD wVersionRequested; WSADATA wsaData; - int err; - wVersionRequested = MAKEWORD(2, 0); - err = WSAStartup(wVersionRequested, &wsaData ); + WORD wVersionRequested = MAKEWORD(2, 0); + int err = WSAStartup(wVersionRequested, &wsaData ); if (err != 0) return 0; if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) { WSACleanup(); @@ -17,38 +24,58 @@ int sock_open(void) return 1; } +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ void sock_destroy(p_sock ps) { closesocket(*ps); + *ps = SOCK_INVALID; } +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ const char *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(); *ps = sock; sock_setnonblocking(ps); - sock_setreuseaddr(ps); + setsockopt(*ps, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(val)); return NULL; } +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len) { if (connect(*ps, addr, addr_len) < 0) return sock_connectstrerror(); else return NULL; } +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len) { if (bind(*ps, addr, addr_len) < 0) return sock_bindstrerror(); else return NULL; } +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ void sock_listen(p_sock ps, int backlog) { listen(*ps, backlog); } +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, int timeout) { @@ -70,6 +97,9 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, else return IO_DONE; } +/*-------------------------------------------------------------------------*\ +* Send with timeout +\*-------------------------------------------------------------------------*/ int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, int timeout) { @@ -104,6 +134,9 @@ int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, } } +/*-------------------------------------------------------------------------*\ +* 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) { @@ -138,6 +171,9 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent, } } +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) { t_sock sock = *ps; @@ -165,6 +201,9 @@ int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) } } +/*-------------------------------------------------------------------------*\ +* 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) { @@ -193,6 +232,27 @@ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got, } } +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void sock_setblocking(p_sock ps) +{ + u_long argp = 0; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void sock_setnonblocking(p_sock ps) +{ + u_long argp = 1; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +\*-------------------------------------------------------------------------*/ const char *sock_hoststrerror(void) { switch (WSAGetLastError()) { @@ -241,21 +301,3 @@ const char *sock_connectstrerror(void) default: return "unknown error"; } } - -void sock_setreuseaddr(p_sock ps) -{ - int val = 1; - setsockopt(*ps, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); -} - -void sock_setblocking(p_sock ps) -{ - u_long argp = 0; - ioctlsocket(*ps, FIONBIO, &argp); -} - -void sock_setnonblocking(p_sock ps) -{ - u_long argp = 1; - ioctlsocket(*ps, FIONBIO, &argp); -} diff --git a/src/wsocket.h b/src/wsocket.h index ceecae7..d77841e 100644 --- a/src/wsocket.h +++ b/src/wsocket.h @@ -1,16 +1,16 @@ +#ifndef WSOCKET_H +#define WSOCKET_H /*=========================================================================*\ * Socket compatibilization module for Win32 +* LuaSocket toolkit * * RCS ID: $Id$ \*=========================================================================*/ -#ifndef WSOCKET_H -#define WSOCKET_H /*=========================================================================*\ -* WinSock2 include files +* WinSock include files \*=========================================================================*/ -#include -#include +#include typedef int socklen_t; typedef int ssize_t; diff --git a/test/testclnt.lua b/test/testclnt.lua index b2b4b18..e38c248 100644 --- a/test/testclnt.lua +++ b/test/testclnt.lua @@ -17,12 +17,14 @@ function warn(...) io.write("WARNING: ", s, "\n") end +pad = string.rep(" ", 8192) + function remote(...) local s = string.format(unpack(arg)) s = string.gsub(s, "\n", ";") s = string.gsub(s, "%s+", " ") s = string.gsub(s, "^%s*", "") - control:send(s, "\n") + control:send(pad, s, "\n") control:receive() end @@ -82,16 +84,19 @@ function reconnect() remote [[ if data then data:close() data = nil end data = server:accept() + data:setoption("nodelay", true) ]] data, err = socket.connect(host, port) if not data then fail(err) else pass("connected!") end + data:setoption("nodelay", true) end pass("attempting control connection...") control, err = socket.connect(host, port) if err then fail(err) else pass("connected!") end +control:setoption("nodelay", true) ------------------------------------------------------------------------ test("method registration") @@ -157,16 +162,21 @@ remote "data:send(str); data:close()" end -test_mixed(1) -test_mixed(17) -test_mixed(200) -test_mixed(4091) -test_mixed(80199) -test_mixed(4091) -test_mixed(200) -test_mixed(17) -test_mixed(1) +--test_mixed(1) +--test_mixed(17) +--test_mixed(200) +--test_mixed(4091) +--test_mixed(80199) +--test_mixed(4091) +--test_mixed(200) +--test_mixed(17) +--test_mixed(1) +test_mixed(4091) +test_mixed(4091) +test_mixed(4091) +test_mixed(4091) +test_mixed(4091) ------------------------------------------------------------------------ test("character line") reconnect() diff --git a/test/testsrvr.lua b/test/testsrvr.lua index 3c40840..39fe274 100644 --- a/test/testsrvr.lua +++ b/test/testsrvr.lua @@ -3,9 +3,11 @@ port = port or "8080" server, error = socket.bind(host, port) if not server then print("server: " .. tostring(error)) os.exit() end +ack = "\n" while 1 do print("server: waiting for client connection..."); control = server:accept() + control:setoption("nodelay", true) while 1 do command, error = control:receive() if error then @@ -13,13 +15,12 @@ while 1 do print("server: closing connection...") break end - sent, error = control:send("\n") + sent, error = control:send(ack) if error then control:close() print("server: closing connection...") break end - print(command); (loadstring(command))() end end