281 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			281 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								/*=========================================================================*\
							 | 
						||
| 
								 | 
							
								* Input/Output interface for Lua programs
							 | 
						||
| 
								 | 
							
								* LuaSocket toolkit
							 | 
						||
| 
								 | 
							
								\*=========================================================================*/
							 | 
						||
| 
								 | 
							
								#include "lua.h"
							 | 
						||
| 
								 | 
							
								#include "lauxlib.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "buffer.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace NS_SLUA {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*=========================================================================*\
							 | 
						||
| 
								 | 
							
								* Internal function prototypes
							 | 
						||
| 
								 | 
							
								\*=========================================================================*/
							 | 
						||
| 
								 | 
							
								static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b);
							 | 
						||
| 
								 | 
							
								static int recvline(p_buffer buf, luaL_Buffer *b);
							 | 
						||
| 
								 | 
							
								static int recvall(p_buffer buf, luaL_Buffer *b);
							 | 
						||
| 
								 | 
							
								static int buffer_get(p_buffer buf, const char **data, size_t *count);
							 | 
						||
| 
								 | 
							
								static void buffer_skip(p_buffer buf, size_t count);
							 | 
						||
| 
								 | 
							
								static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* min and max macros */
							 | 
						||
| 
								 | 
							
								#ifndef MIN
							 | 
						||
| 
								 | 
							
								#define MIN(x, y) ((x) < (y) ? x : y)
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								#ifndef MAX
							 | 
						||
| 
								 | 
							
								#define MAX(x, y) ((x) > (y) ? x : y)
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*=========================================================================*\
							 | 
						||
| 
								 | 
							
								* Exported functions
							 | 
						||
| 
								 | 
							
								\*=========================================================================*/
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* Initializes module
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								int buffer_open(lua_State *L) {
							 | 
						||
| 
								 | 
							
								    (void) L;
							 | 
						||
| 
								 | 
							
								    return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* Initializes C structure 
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								void buffer_init(p_buffer buf, p_io io, p_timeout tm) {
							 | 
						||
| 
								 | 
							
								    buf->first = buf->last = 0;
							 | 
						||
| 
								 | 
							
								    buf->io = io;
							 | 
						||
| 
								 | 
							
								    buf->tm = tm;
							 | 
						||
| 
								 | 
							
								    buf->received = buf->sent = 0;
							 | 
						||
| 
								 | 
							
								    buf->birthday = timeout_gettime();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* object:getstats() interface
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								int buffer_meth_getstats(lua_State *L, p_buffer buf) {
							 | 
						||
| 
								 | 
							
								    lua_pushnumber(L, (lua_Number) buf->received);
							 | 
						||
| 
								 | 
							
								    lua_pushnumber(L, (lua_Number) buf->sent);
							 | 
						||
| 
								 | 
							
								    lua_pushnumber(L, timeout_gettime() - buf->birthday);
							 | 
						||
| 
								 | 
							
								    return 3;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* object:setstats() interface
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								int buffer_meth_setstats(lua_State *L, p_buffer buf) {
							 | 
						||
| 
								 | 
							
								    buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received); 
							 | 
						||
| 
								 | 
							
								    buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent); 
							 | 
						||
| 
								 | 
							
								    if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4);
							 | 
						||
| 
								 | 
							
								    lua_pushnumber(L, 1);
							 | 
						||
| 
								 | 
							
								    return 1;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* object:send() interface
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								int buffer_meth_send(lua_State *L, p_buffer buf) {
							 | 
						||
| 
								 | 
							
								    int top = lua_gettop(L);
							 | 
						||
| 
								 | 
							
								    int err = IO_DONE;
							 | 
						||
| 
								 | 
							
								    size_t size = 0, sent = 0;
							 | 
						||
| 
								 | 
							
								    const char *data = luaL_checklstring(L, 2, &size);
							 | 
						||
| 
								 | 
							
								    long start = (long) luaL_optnumber(L, 3, 1);
							 | 
						||
| 
								 | 
							
								    long end = (long) luaL_optnumber(L, 4, -1);
							 | 
						||
| 
								 | 
							
								#ifdef LUASOCKET_DEBUG
							 | 
						||
| 
								 | 
							
								    p_timeout tm = timeout_markstart(buf->tm);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								    if (start < 0) start = (long) (size+start+1);
							 | 
						||
| 
								 | 
							
								    if (end < 0) end = (long) (size+end+1);
							 | 
						||
| 
								 | 
							
								    if (start < 1) start = (long) 1;
							 | 
						||
| 
								 | 
							
								    if (end > (long) size) end = (long) size;
							 | 
						||
| 
								 | 
							
								    if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent);
							 | 
						||
| 
								 | 
							
								    /* check if there was an error */
							 | 
						||
| 
								 | 
							
								    if (err != IO_DONE) {
							 | 
						||
| 
								 | 
							
								        lua_pushnil(L);
							 | 
						||
| 
								 | 
							
								        lua_pushstring(L, buf->io->error(buf->io->ctx, err)); 
							 | 
						||
| 
								 | 
							
								        lua_pushnumber(L, (lua_Number) (sent+start-1));
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        lua_pushnumber(L, (lua_Number) (sent+start-1));
							 | 
						||
| 
								 | 
							
								        lua_pushnil(L);
							 | 
						||
| 
								 | 
							
								        lua_pushnil(L);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								#ifdef LUASOCKET_DEBUG
							 | 
						||
| 
								 | 
							
								    /* push time elapsed during operation as the last return value */
							 | 
						||
| 
								 | 
							
								    lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm));
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								    return lua_gettop(L) - top;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* object:receive() interface
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								int buffer_meth_receive(lua_State *L, p_buffer buf) {
							 | 
						||
| 
								 | 
							
								    int err = IO_DONE, top = lua_gettop(L);
							 | 
						||
| 
								 | 
							
								    luaL_Buffer b;
							 | 
						||
| 
								 | 
							
								    size_t size;
							 | 
						||
| 
								 | 
							
								    const char *part = luaL_optlstring(L, 3, "", &size);
							 | 
						||
| 
								 | 
							
								#ifdef LUASOCKET_DEBUG
							 | 
						||
| 
								 | 
							
								    p_timeout tm = timeout_markstart(buf->tm);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								    /* initialize buffer with optional extra prefix 
							 | 
						||
| 
								 | 
							
								     * (useful for concatenating previous partial results) */
							 | 
						||
| 
								 | 
							
								    luaL_buffinit(L, &b);
							 | 
						||
| 
								 | 
							
								    luaL_addlstring(&b, part, size);
							 | 
						||
| 
								 | 
							
								    /* receive new patterns */
							 | 
						||
| 
								 | 
							
								    if (!lua_isnumber(L, 2)) {
							 | 
						||
| 
								 | 
							
								        const char *p= luaL_optstring(L, 2, "*l");
							 | 
						||
| 
								 | 
							
								        if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b);
							 | 
						||
| 
								 | 
							
								        else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); 
							 | 
						||
| 
								 | 
							
								        else luaL_argcheck(L, 0, 2, "invalid receive pattern");
							 | 
						||
| 
								 | 
							
								    /* get a fixed number of bytes (minus what was already partially 
							 | 
						||
| 
								 | 
							
								     * received) */
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        double n = lua_tonumber(L, 2); 
							 | 
						||
| 
								 | 
							
								        size_t wanted = (size_t) n;
							 | 
						||
| 
								 | 
							
								        luaL_argcheck(L, n >= 0, 2, "invalid receive pattern");
							 | 
						||
| 
								 | 
							
								        if (size == 0 || wanted > size)
							 | 
						||
| 
								 | 
							
								            err = recvraw(buf, wanted-size, &b);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /* check if there was an error */
							 | 
						||
| 
								 | 
							
								    if (err != IO_DONE) {
							 | 
						||
| 
								 | 
							
								        /* we can't push anyting in the stack before pushing the
							 | 
						||
| 
								 | 
							
								         * contents of the buffer. this is the reason for the complication */
							 | 
						||
| 
								 | 
							
								        luaL_pushresult(&b);
							 | 
						||
| 
								 | 
							
								        lua_pushstring(L, buf->io->error(buf->io->ctx, err)); 
							 | 
						||
| 
								 | 
							
								        lua_pushvalue(L, -2); 
							 | 
						||
| 
								 | 
							
								        lua_pushnil(L);
							 | 
						||
| 
								 | 
							
								        lua_replace(L, -4);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        luaL_pushresult(&b);
							 | 
						||
| 
								 | 
							
								        lua_pushnil(L);
							 | 
						||
| 
								 | 
							
								        lua_pushnil(L);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								#ifdef LUASOCKET_DEBUG
							 | 
						||
| 
								 | 
							
								    /* push time elapsed during operation as the last return value */
							 | 
						||
| 
								 | 
							
								    lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm));
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								    return lua_gettop(L) - top;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* Determines if there is any data in the read buffer
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								int buffer_isempty(p_buffer buf) {
							 | 
						||
| 
								 | 
							
								    return buf->first >= buf->last;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*=========================================================================*\
							 | 
						||
| 
								 | 
							
								* Internal functions
							 | 
						||
| 
								 | 
							
								\*=========================================================================*/
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* Sends a block of data (unbuffered)
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								#define STEPSIZE 8192
							 | 
						||
| 
								 | 
							
								static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) {
							 | 
						||
| 
								 | 
							
								    p_io io = buf->io;
							 | 
						||
| 
								 | 
							
								    p_timeout tm = buf->tm;
							 | 
						||
| 
								 | 
							
								    size_t total = 0;
							 | 
						||
| 
								 | 
							
								    int err = IO_DONE;
							 | 
						||
| 
								 | 
							
								    while (total < count && err == IO_DONE) {
							 | 
						||
| 
								 | 
							
								        size_t done = 0;
							 | 
						||
| 
								 | 
							
								        size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE;
							 | 
						||
| 
								 | 
							
								        err = io->send(io->ctx, data+total, step, &done, tm);
							 | 
						||
| 
								 | 
							
								        total += done;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    *sent = total;
							 | 
						||
| 
								 | 
							
								    buf->sent += total;
							 | 
						||
| 
								 | 
							
								    return err;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* Reads a fixed number of bytes (buffered)
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) {
							 | 
						||
| 
								 | 
							
								    int err = IO_DONE;
							 | 
						||
| 
								 | 
							
								    size_t total = 0;
							 | 
						||
| 
								 | 
							
								    while (err == IO_DONE) {
							 | 
						||
| 
								 | 
							
								        size_t count; const char *data;
							 | 
						||
| 
								 | 
							
								        err = buffer_get(buf, &data, &count);
							 | 
						||
| 
								 | 
							
								        count = MIN(count, wanted - total);
							 | 
						||
| 
								 | 
							
								        luaL_addlstring(b, data, count);
							 | 
						||
| 
								 | 
							
								        buffer_skip(buf, count);
							 | 
						||
| 
								 | 
							
								        total += count;
							 | 
						||
| 
								 | 
							
								        if (total >= wanted) break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return err;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* Reads everything until the connection is closed (buffered)
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								static int recvall(p_buffer buf, luaL_Buffer *b) {
							 | 
						||
| 
								 | 
							
								    int err = IO_DONE;
							 | 
						||
| 
								 | 
							
								    size_t total = 0;
							 | 
						||
| 
								 | 
							
								    while (err == IO_DONE) {
							 | 
						||
| 
								 | 
							
								        const char *data; size_t count;
							 | 
						||
| 
								 | 
							
								        err = buffer_get(buf, &data, &count);
							 | 
						||
| 
								 | 
							
								        total += count;
							 | 
						||
| 
								 | 
							
								        luaL_addlstring(b, data, count);
							 | 
						||
| 
								 | 
							
								        buffer_skip(buf, count);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (err == IO_CLOSED) {
							 | 
						||
| 
								 | 
							
								        if (total > 0) return IO_DONE;
							 | 
						||
| 
								 | 
							
								        else return IO_CLOSED;
							 | 
						||
| 
								 | 
							
								    } else return err;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF 
							 | 
						||
| 
								 | 
							
								* are not returned by the function and are discarded from the buffer
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								static int recvline(p_buffer buf, luaL_Buffer *b) {
							 | 
						||
| 
								 | 
							
								    int err = IO_DONE;
							 | 
						||
| 
								 | 
							
								    while (err == IO_DONE) {
							 | 
						||
| 
								 | 
							
								        size_t count, pos; const char *data;
							 | 
						||
| 
								 | 
							
								        err = buffer_get(buf, &data, &count);
							 | 
						||
| 
								 | 
							
								        pos = 0;
							 | 
						||
| 
								 | 
							
								        while (pos < count && data[pos] != '\n') {
							 | 
						||
| 
								 | 
							
								            /* we ignore all \r's */
							 | 
						||
| 
								 | 
							
								            if (data[pos] != '\r') luaL_addchar(b, data[pos]);
							 | 
						||
| 
								 | 
							
								            pos++;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (pos < count) { /* found '\n' */
							 | 
						||
| 
								 | 
							
								            buffer_skip(buf, pos+1); /* skip '\n' too */
							 | 
						||
| 
								 | 
							
								            break; /* we are done */
							 | 
						||
| 
								 | 
							
								        } else /* reached the end of the buffer */
							 | 
						||
| 
								 | 
							
								            buffer_skip(buf, pos);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return err;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* Skips a given number of bytes from read buffer. No data is read from the
							 | 
						||
| 
								 | 
							
								* transport layer
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								static void buffer_skip(p_buffer buf, size_t count) {
							 | 
						||
| 
								 | 
							
								    buf->received += count;
							 | 
						||
| 
								 | 
							
								    buf->first += count;
							 | 
						||
| 
								 | 
							
								    if (buffer_isempty(buf)) 
							 | 
						||
| 
								 | 
							
								        buf->first = buf->last = 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*-------------------------------------------------------------------------*\
							 | 
						||
| 
								 | 
							
								* Return any data available in buffer, or get more data from transport layer
							 | 
						||
| 
								 | 
							
								* if buffer is empty
							 | 
						||
| 
								 | 
							
								\*-------------------------------------------------------------------------*/
							 | 
						||
| 
								 | 
							
								static int buffer_get(p_buffer buf, const char **data, size_t *count) {
							 | 
						||
| 
								 | 
							
								    int err = IO_DONE;
							 | 
						||
| 
								 | 
							
								    p_io io = buf->io;
							 | 
						||
| 
								 | 
							
								    p_timeout tm = buf->tm;
							 | 
						||
| 
								 | 
							
								    if (buffer_isempty(buf)) {
							 | 
						||
| 
								 | 
							
								        size_t got;
							 | 
						||
| 
								 | 
							
								        err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm);
							 | 
						||
| 
								 | 
							
								        buf->first = 0;
							 | 
						||
| 
								 | 
							
								        buf->last = got;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    *count = buf->last - buf->first;
							 | 
						||
| 
								 | 
							
								    *data = buf->data + buf->first;
							 | 
						||
| 
								 | 
							
								    return err;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								} // end NS_SLUA
							 |