pax_global_header00006660000000000000000000000064121740207320014510gustar00rootroot0000000000000052 comment=3ddb7c8e86a103126b63bd3e385285e0b0781e74 lua-event-0.4.3/000077500000000000000000000000001217402073200134145ustar00rootroot00000000000000lua-event-0.4.3/.gitignore000066400000000000000000000000461217402073200154040ustar00rootroot00000000000000bin Makefile *.make obj test/luaevent lua-event-0.4.3/CHANGELOG000066400000000000000000000076661217402073200146450ustar00rootroot000000000000000.4.4 - WIP * Updated license to MIT license 0.4.3 - 2013-03-11 * Fixes unchecked stack increase resulting in potential overflow 0.4.2 - 2013-03-10 * Adds error catching / re-raise 0.4.1 - 2012-04-12 * Fixes event_callback handling to handle the no-return-value case better * Cleans up timertest formatting ====== 0.4.0 - 2012-04-10 * Fixes buffer event tests to work with current bufferevent behavior * Fixed buffer event and related tests to be compatible with Lua 5.2 * Makes core segment compatible with Lua 5.2 * Remove use of 'module' in sources * Remove assumption that luaevent will set globals - Only 'core' will modify global table (creating luaevent.core) * Fixes bug in merge for receivePartial ====== 0.3.2 - 2011-07-06 * Fixed recorded version in Lua 0.3.1 - 2011-01-15 * Merged all of luaevent-prosody (0.1.1) changes (missed updates) 0.3.0 - 2011-01-15 * Fixed libevent 2.0 compilation errors * Merged all of luaevent-prosody (0.1.0) changes * Removes low-level read access to watermark & timeout values that break API * Switches to watermark write accessor function to avoid API break ====== luaevent-prosody 0.1.1 - 2010-02-25 * Fix overflow with high event timeouts ====== luaevent-prosody 0.1.0 - 2010-02-15 * Fixed stack slot leak in event callbacks * Fixed stack slot leak when error occurs in callback * Various compilation fixes for Windows/ANSI C * Silence some compiler warnings + Add base:loopexit() method, with timeout support + Add base:method() to discover backend in use + Add core.libevent_version() to detect libevent version + Add _NAME and _VERSION fields to module table + base:addevent() now accepts integer fd to watch + Switched from premake to standard Makefile for building ====== 0.2.0 - 2007-09-21 + Reorganized project to better fit GIT + Refactored and cleaned sources * Broke event_callback into its own source file + Added timer/timeout capabilities + minor test + Added event_buffer object * Can 'add' a sequence of strings/event_buffers * Can 'get_data', 'length','drain','close' and create new instances * Can 'readline', 'read' from file des, 'write' to file des * Added lunit tests for basic functions (read/write from/to FD) ====== 0.1.2 - 2007-08-18 + Setup system to use new coro management as described in COROUTINE_MANAGEMENT The callbacks are called from the event_loop 'thread' rather than that which they are created in. This will prevent the self-resume problem as well as dead-thread problems. - Recognized issues to fix in next release: * Socket/event closing needs to be cleaned * luaevent.lua needs refactoring * luaevent.[ch] need to be cleaned up ====== 0.1.1 - 2007-06-13 + Fixed event-handling code to cancel events on nothing being returned + Added socket/object cleanup. + Filed bug to libevent about the strange valgrind-released errors - Recognized following issues: Timeouts needed Need to handle events setup from inside a coroutine... need to get a global Lua state from a thread... ====== 0.1.0 - 2007-06-10 22:00 EST Completed mostly working version * Moved to a mode where addevent calls a callback rather than it being instantiated within. If the callback returns -1, then no event is ever setup, Otherwise the integer value is used to setup the event. This allows for using coroutine.wrap rather than a cooked-up wrapper * Tests work, although there are a few remaining issues: * Need to figure a good way of preserving the event object, not sure if current method is good enough, since the socket is the only anchor, and it is only held inside the coro.. circular reference, something that Lua 'handles' well. * Doing more than the maximum sockets the process is allows causes strangeness to occur in libevent.. somehow it is getting around to epoll_add which is causing valgrind to barf. * Added cheap protection code for failures in callback handlers ====== 0.0.0 - 2007-06-10 12:00 EST Initial public version, was broken due to self-resume coroutines lua-event-0.4.3/LICENSE000066400000000000000000000003171217402073200144220ustar00rootroot00000000000000LuaEvent - Binding of libevent to Lua to offer event-based IO programming Copyright (C) 2007,2012,2013 Thomas Harning Licensed under the MIT license. See doc/COPYING for more details. lua-event-0.4.3/Makefile000066400000000000000000000023311217402073200150530ustar00rootroot00000000000000.PHONY: all install clean dist dist-all dist-bzip2 dist-gzip dist-zip DIST_DIR=dist # Utilities INSTALL = install INSTALL_PROGRAM = $(INSTALL) INSTALL_DATA = $(INSTALL) -m 644 # Flags CFLAGS = -Wall -fpic LDFLAGS = -shared # Directories LUA_INC_DIR ?= /usr/include/lua5.1 INSTALL_DIR_LUA ?= /usr/share/lua/5.1 INSTALL_DIR_BIN ?= /usr/lib/lua/5.1 # Files LIB = core.so all: $(CC) $(CFLAGS) -c -Iinclude -I$(LUA_INC_DIR) src/*.c $(CC) $(LDFLAGS) -o $(LIB) *.o -levent dist dist-all: distdir dist-bzip2 dist-gzip dist-zip distdir: mkdir -p $(DIST_DIR) VERSION=luaevent-$(shell git describe --abbrev=4 HEAD 2>/dev/null) dist-bzip2: distdir git archive --format=tar --prefix=$(VERSION)/ HEAD | bzip2 -9v > $(DIST_DIR)/$(VERSION).tar.bz2 dist-gzip: distdir git archive --format=tar --prefix=$(VERSION)/ HEAD | gzip -9v > $(DIST_DIR)/$(VERSION).tar.gz dist-zip: distdir git archive --format=zip --prefix=$(VERSION)/ HEAD > $(DIST_DIR)/$(VERSION).zip install: all mkdir -p $(DESTDIR)$(INSTALL_DIR_LUA) $(INSTALL_DATA) lua/luaevent.lua $(DESTDIR)$(INSTALL_DIR_LUA)/luaevent.lua mkdir -p $(DESTDIR)$(INSTALL_DIR_BIN)/luaevent/ $(INSTALL_PROGRAM) $(LIB) $(DESTDIR)$(INSTALL_DIR_BIN)/luaevent/$(LIB) clean: rm -f *.so rm -f *.o lua-event-0.4.3/README000066400000000000000000000013261217402073200142760ustar00rootroot00000000000000Description: This is a binding of libevent to Lua. It will serve as a drop-in replacement for copas, and eventually support more features (async DNS, HTTP, RPC...). Contact information: Lead: Thomas Harning Major contributions from prosody project: Matthew Wild: mwild1@gmail.com Project page: http://luaforge.net/projects/luaevent/ GIT Repositories: See: https://github.com/harningt/luaevent Build Requirements: libevent (recommend >= 1.4) Usage Requirements: libevent (recommend >= 1.4) LuaSocket License: MIT See test/ directory for examples of usage. Dependencies: libevent - http://monkey.org/~provos/libevent/ LuaSocket - http://www.cs.princeton.edu/~diego/professional/luasocket/ lua-event-0.4.3/doc/000077500000000000000000000000001217402073200141615ustar00rootroot00000000000000lua-event-0.4.3/doc/COPYING000066400000000000000000000021541217402073200152160ustar00rootroot00000000000000LuaEvent Licensed under the MIT license. Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lua-event-0.4.3/doc/COROUTINE_MANAGEMENT000066400000000000000000000024671217402073200171600ustar00rootroot00000000000000Due to the issue w/ self-resuming threads and crashing out threads, a management system needs to be in place. Example thread system: MAIN EVENT_LOOP --------running--- WAITING ON READ WAITING ON WRITE WAITING ON CONNECT Since main and the other 'waiting' threads are yielded, it is unsafe to call things arbitrarily on them or resume them from themselves... However the EVENT_LOOP one is running and thus can execute the callbacks (which can resume the threads) Each of the 'waiting' events are attached to an event and contain a pointer, this pointer can be setup to point to a per event_base item which will be updated w/ the lua_State of whatever calls EVENT_LOOP... this will guarantee that the thread will be resumed from the currently running EVENT_LOOP Other system that's more complicated and less likely: MAIN EVENT_LOOP a -----running--- WAITING ON READ a WAITING ON WRITE a EVENT_LOOP b ----yielded WAITING ON READ b Since there can only be one event_loop running per event_base, you do not have to worry about cross-pollination of the different waits... NOTES: If the event_loop thread goes away... then the waiting coroutines will have no way to get back... though in this case, they are dead in the water anyways.. until a new event_loop starts... in which case the lua_State references has been updated...lua-event-0.4.3/doc/PLAN000066400000000000000000000005521217402073200146400ustar00rootroot00000000000000-- Listener Scenario create socket, perform bind, set listening +add read-event listener +start loop -- Comm Scenario provided socket -inside loop begin coro read - data not ready yield sock, needRead end coro coro parent == callback (coro parent call coro(sock, event) if not ok, go back.. end if newEvent ~= event then unset event, reset newEvent end)lua-event-0.4.3/doc/api.mdwn000066400000000000000000000011431217402073200156200ustar00rootroot00000000000000LuaEvent API Documentation ---- Modules: * [[modules/luaevent.core]] - Interface to core libevent functions * [[modules/luaevent.core.buffer]] - Interface to libevent's evbuffer * [[modules/luaevent.core.bufferevent]] - Interface to libevent's bufferevent * [[modules/luaevent]] - Higher level wrapper mimicking [copas](http://www.luaforge.net/projects/copas) Note: Any reference to a "File Descriptor" may in fact be one of the following: * Integer value of file descriptor * lightuserdata 'handle' (cast to a native integer) * [LuaSocket](http://www.luaforge.net/projects/luasocket)-based socket handle lua-event-0.4.3/doc/index.mdwn000066400000000000000000000006441217402073200161630ustar00rootroot00000000000000LuaEvent is a Lua wrapper around [libevent](http://monkey.org/~provos/libevent/). It provides an interface with which you can write applications that wait on file-descriptor events (primarily socket-based). [[API_Reference|api]] ---- Related links for this project: * LuaForge @ * GitRepo @ * ohloh @ lua-event-0.4.3/doc/modules/000077500000000000000000000000001217402073200156315ustar00rootroot00000000000000lua-event-0.4.3/doc/modules/luaevent.core.buffer.mdwn000066400000000000000000000043351217402073200225470ustar00rootroot00000000000000---- Functions: [[toc levels=1]] ## buffer.new * Instantiates a new buffer object ## buffer:add * Successively concatenates each of the arguments onto the buffer * Input: `(...)` * Sequence of strings or buffer instances * Side Effects: Buffers 'add'ed are emptied of their contents (per libevent semantics) * Output: Amount of data added (QUESTION: Should add return the buffer itself so that chaining can be easy) ## buffer:length (__len) * Output: Length of the remaining buffer contents ## buffer:get\_data (__tostring) * Input: * `()` and `__tostring` - Returns all data in the buffer * `(len)` - Returns data up to `len` bytes long * `(begin, len)` - Returns data beginning at `begin` up to `len` bytes long * If `begin < 0`, wraps at data length. Ex: (-1, 1) returns last byte, (-2, 2) returns last 2 bytes * Output: A copy of contents from the buffer ## buffer:read * Reads data from a file-descriptor/socket into the buffer directly * Input: `(fd, length)` * `fd` - File descriptor to read from * `length` - Amount of data to attempt to read into the buffer * Output: Length of data actually read into the buffer * Side Effects: fd/socket 'drain'ed of data ## buffer:write * Attempts to write out all buffer's data to a file-descriptor/socket * Input: `(fd, length)` * `fd` - File descriptor to write to * `socket` - [LuaSocket](http://www.luaforge.net/projects/luasocket)-based socket handle * Output: Amount of data written * Side Effects: buffer 'drain'ed of written data ## buffer:readline * Reads a line terminated by either '\r\n', '\n\r', '\r', or, '\n' * Output: * If no terminator found: nil * If terminator found: Line returned without terminators * NOTE: If a '\r' or '\n' are the last characters in the buffer, then the data is returned even if the potential later data would contain the paired '\n' or '\r'. (TODO: Ask libevent list on how this is handled...) ## buffer:drain * Removes data from the buffer * Input: `(amt)` * If `amt < 0` drains all data due to auto-casting to unsigned int and capping... TODO: Add code to check this condition explicitly for safety * If `amt >= 0`, drain up to amt from the buffer (no problem w/ too-large values) ## buffer:close (__gc) * Immediately frees/closes a buffer. Note that lua-event-0.4.3/doc/modules/luaevent.core.bufferevent.mdwn000066400000000000000000000042071217402073200236070ustar00rootroot00000000000000---- Functions: [[toc levels=1]] ## Read/Write/Error Callback: * Input: `(bufferevent, what)` * `bufferevent` - Reference to the bufferevent that raised the callback * `what` - What happened: * == `EVBUFFER_READ` - Buffer contains at least low-watermark length and no more than high-watermark length * == `EVBUFFER_WRITE` - Buffer ready to write to * (other) - Error condition * May be or-ed/added with `EVBUFFER_READ`/`EVBUFFER_WRITE` to specify where it happened * `EVBUFFER_ERROR` - Marks error condition (need to look at 'errno' for error.. not exposed yet) * `EVBUFFER_TIMEOUT` - Marks activity timeout * `EVBUFFER_EOF` - Marks disconnection/end-of-file condition ## bufferevent.new * Input: `(fd, read, write, error)` * `fd` - File descriptor to watch * `read` - (may be nil) - callback to call when data in buffer is above the low watermark * `write` - (may be nil) - callback to call when the output buffer contains less data than the low watermark * `error` - callback to call when there is an erroneous condition ## bufferevent (__gc) * Releases the bufferevent * Disconnects event buffers since they were owned by the bufferevent object in 'C' land * Disconnects all references so that any erroneous callbacks don't cause failures ## bufferevent:get_read * Obtains the input buffer associated w/ the bufferevent ## bufferevent:get_write * Obtains the output buffer associated w/ the bufferevent ## bufferevent:set_read_watermarks * Input: `(low, high)` * `low` - Size of buffer at which an event would be fired * `high` - Maximum size of buffer to read to ## bufferevent:set_write_watermarks * Input: `(low, high)` * `low` - When buffer is below this, the event will be fired * `high` - N/A to libevent, user app may use this ## bufferevent:set_timeouts * Sets timeouts for the bufferevent's events * Input: `(read, write)` * `read` - Read readiness timeout * `write` - Write readiness timeout ## bufferevent:enable/disable * Input: `event flag` * `event flag` - `EV_READ`, `EV_WRITE`, or `EV_READ|EV_WRITE` * Enables/Disables events from being triggered in the next round (some events may get triggered after disable is called) lua-event-0.4.3/doc/modules/luaevent.core.mdwn000066400000000000000000000046161217402073200213010ustar00rootroot00000000000000---- Constants: * `LEAVE` - When returned will cause event callback to be cancelled * `EV_READ` * Marks read readiness/event capture. * Read readiness can also mean that an 'accept' operation will succeed, or a disconnection on the other end is detected (read will return nothing). * `EV_WRITE` * Marks write readiness/event capture. * Can also mark the successful completion of a non-blocking connect if `SO_ERROR`@`SOL_SOCKET` is zero. * `EV_SIGNAL` * Marks signal received/event capture * `EV_TIMEOUT` * Timeout occurred while waiting for an event * `EV_PERSIST` * Marks an event as persistent and not one-shot * `EV_*` * Can be OR'd together to capture multiple events that make sense. (Should not OR `EV_READ`/`EV_WRITE` with `EV_SIGNAL`) * Can be received OR'd together. For example: `EV_READ` | `EV_TIMEOUT` means that a timeout occurred while waiting for a read event. * `EVBUFFER_READ` * Marks that the input buffer has data available > low watermark * `EVBUFFER_WRITE` * Marks that the output buffer level is below low watermark * `EVBUFFER_EOF` * Received tagged with either read/write based on location received in the error callback * `EVBUFFER_ERROR` * An error occurred (tagged w/ either read/write) and the error is in `errno` * `EVBUFFER_TIMEOUT` * A timeout occurred (tagged w/ either read/write) Functions: [[toc levels=1]] ## luaevent.core.new * Allocates a new event 'core' (`event base`) ## event_callback fn * Input: `(event)` * Output: `(newEvent, [newTimeout])` * `newEvent` - New event to register, typically `LEAVE`, `EV_READ`, `EV_WRITE`, or `EV_READ`|`EV_WRITE` * `newTimeout` - New timeout value to use ## core:addevent * Adds a new event to the eventloop * Input: `(fd, event, event_callback, [timeout])` * `fd` - File descriptor to read from / or NIL for pure timeout event * `event` - `EV_*` flagset to mark what events to capture * `EV_SIGNAL` and `EV_PERSIST` is unavailable currently * `EV_PERSIST` is used internally. * `event_callback` - Callback to call... (see above) * `timeout` - Time in seconds to timeout (decimal values supported) * Output: `event_callback` object * Has a `close` and `__gc` FN which will erase the callback, so preserve this until done. ## core:loop * Begins the event loop and doesn't return until there are no events left ## core:close (__gc) * Closes the event base * Do not allow this to be called while `core:loop` is running lua-event-0.4.3/doc/modules/luaevent.mdwn000066400000000000000000000000001217402073200203310ustar00rootroot00000000000000lua-event-0.4.3/include/000077500000000000000000000000001217402073200150375ustar00rootroot00000000000000lua-event-0.4.3/include/buffer_event.h000066400000000000000000000027001217402073200176610ustar00rootroot00000000000000/* LuaEvent Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef BUFFER_EVENT_H #define BUFFER_EVENT_H #include "luaevent.h" typedef struct { struct bufferevent* ev; le_base* base; } le_bufferevent; void buffer_event_register(lua_State* L, int coreIndex); int is_buffer_event(lua_State* L, int idx); le_bufferevent* buffer_event_check(lua_State* L, int idx); #endif lua-event-0.4.3/include/event_buffer.h000066400000000000000000000027441217402073200176710ustar00rootroot00000000000000/* LuaEvent Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef EVENT_BUFFER_H #define EVENT_BUFFER_H #include "luaevent.h" typedef struct { struct evbuffer* buffer; } le_buffer; void event_buffer_register(lua_State* L, int coreIndex); int is_event_buffer(lua_State* L, int idx); le_buffer* event_buffer_check(lua_State* L, int idx); int event_buffer_push(lua_State* L, struct evbuffer* buffer); #endif lua-event-0.4.3/include/event_callback.h000066400000000000000000000027621217402073200201540ustar00rootroot00000000000000/* LuaEvent Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef EVENT_CALLBACK #define EVENT_CALLBACK #include "luaevent.h" typedef struct { struct event ev; le_base* base; int callbackRef; struct timeval timeout; } le_callback; void event_callback_register(lua_State* L); le_callback* event_callback_push(lua_State* L, int baseIdx, int callbackIdx); void luaevent_callback(int fd, short event, void* p); #endif lua-event-0.4.3/include/luaevent.h000066400000000000000000000040101217402073200170260ustar00rootroot00000000000000/* LuaEvent Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef LUAEVENT_H #define LUAEVENT_H #include /* Workarounds for Lua 5.2 */ #if (LUA_VERSION_NUM == 502) #undef lua_equal #define lua_equal(L,idx1,idx2) lua_compare(L, (idx1), (idx2), LUA_OPEQ) #undef lua_getfenv #define lua_getfenv lua_getuservalue #undef lua_setfenv #define lua_setfenv lua_setuservalue #undef lua_objlen #define lua_objlen lua_rawlen #undef luaL_register #define luaL_register(L, n, f) \ { if ((n) == NULL) luaL_setfuncs(L, f, 0); else luaL_newlib(L, f); } #endif #include #ifdef _WIN32 #include #else #include #endif #include typedef struct { struct event_base* base; lua_State* loop_L; int errorMessage; } le_base; le_base* event_base_get(lua_State* L, int idx); void load_timeval(double time, struct timeval *tv); int getSocketFd(lua_State* L, int idx); int luaopen_luaevent_core(lua_State* L); #endif lua-event-0.4.3/include/utility.h000066400000000000000000000025671217402073200167250ustar00rootroot00000000000000/* LuaEvent Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTILITY_H #define UTILITY_H #include void le_weak_ref(lua_State* L, void* ptr, int idx); void le_weak_unref(lua_State* L, void* ptr); void le_weak_get(lua_State* L, void* ptr); void le_register_utility(lua_State* L); #endif lua-event-0.4.3/lua/000077500000000000000000000000001217402073200141755ustar00rootroot00000000000000lua-event-0.4.3/lua/luaevent.lua000066400000000000000000000107241217402073200165270ustar00rootroot00000000000000--[[ LuaEvent Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] local _M = {} local core = require("luaevent.core") _M.core = core _M._NAME = "luaevent" _M._VERSION = "0.4.4" local EV_READ = core.EV_READ local EV_WRITE = core.EV_WRITE local base = core.new() local function addevent(...) return base:addevent(...) end local function getWrapper() local running = coroutine.running() return function(...) return select(2, coroutine.resume(running, ...)) end end -- Weak keys.. the keys are the client sockets local clientTable = setmetatable({}, {'__mode', 'kv'}) local function socketWait(sock, event) if not clientTable[sock] then clientTable[sock] = addevent(sock, event, getWrapper()) end coroutine.yield(event) end function _M.send(sock, data, start, stop) local s, err local from = start or 1 local sent = 0 repeat from = from + sent s, err, sent = sock:send(data, from, stop) if s or err ~= "timeout" then return s, err, sent end socketWait(sock, EV_WRITE) until false end function _M.receive(sock, pattern, part) local s, err pattern = pattern or '*l' repeat s, err, part = sock:receive(pattern, part) if s or err ~= "timeout" then return s, err, part end socketWait(sock, EV_READ) until false end -- same as above but with special treatment when reading chunks, -- unblocks on any data received. function _M.receivePartial(client, pattern) local s, err, part pattern = pattern or "*l" repeat s, err, part = client:receive(pattern) if s or ( (type(pattern)=="number") and part~="" and part ~=nil ) or err ~= "timeout" then return s, err, part end socketWait(client, EV_READ) until false end function _M.connect(sock, ...) sock:settimeout(0) local ret, err = sock:connect(...) if ret or err ~= "timeout" then return ret, err end socketWait(sock, EV_WRITE) ret, err = sock:connect(...) if err == "already connected" then return 1 end return ret, err end -- Deprecated.. function _M.flush(sock) end local function clientCoroutine(sock, handler) -- Figure out what to do ...... return handler(sock) end local function handleClient(co, client, handler) local ok, res, event = coroutine.resume(co, client, handler) end local function serverCoroutine(sock, callback) local listenItem = addevent(sock, EV_READ, getWrapper()) repeat local event = coroutine.yield(EV_READ) -- Get new socket local client = sock:accept() if client then client:settimeout(0) local co = coroutine.create(clientCoroutine) handleClient(co, client, callback) end until false end function _M.addserver(sock, callback) local coro = coroutine.create(serverCoroutine) assert(coroutine.resume(coro, sock, callback)) end function _M.addthread(func, ...) return coroutine.resume(coroutine.create(func), ...) end local _skt_mt = {__index = { connect = function(self, ...) return _M.connect(self.socket, ...) end, send = function (self, data) return _M.send(self.socket, data) end, receive = function (self, pattern) if (self.timeout==0) then return _M.receivePartial(self.socket, pattern) end return _M.receive(self.socket, pattern) end, flush = function (self) return _M.flush(self.socket) end, settimeout = function (self,time) self.timeout=time return end, close = function(self) clientTable[self.socket]:close() self.socket:close() end }} function _M.wrap(sock) return setmetatable({socket = sock}, _skt_mt) end _M.loop = function(...) base:loop(...) end return _M lua-event-0.4.3/makeDocs.sh000077500000000000000000000001121217402073200154730ustar00rootroot00000000000000#!/bin/sh ikiwiki doc/ html/ --no-usedirs --plugin=goodstuff --plugin=toc lua-event-0.4.3/src/000077500000000000000000000000001217402073200142035ustar00rootroot00000000000000lua-event-0.4.3/src/buffer_event.c000066400000000000000000000166411217402073200170310ustar00rootroot00000000000000/* LuaEvent Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "buffer_event.h" #include "utility.h" #include #include "event_buffer.h" #define BUFFER_EVENT_MT "BUFFER_EVENT_MT" /* Locations of READ/WRITE buffers in the fenv */ #define READ_BUFFER_LOCATION 4 #define WRITE_BUFFER_LOCATION 5 /* Obtains an le_bufferevent structure from a given index */ static le_bufferevent* buffer_event_get(lua_State* L, int idx) { return (le_bufferevent*)luaL_checkudata(L, idx, BUFFER_EVENT_MT); } /* Obtains an le_bufferevent structure from a given index AND checks that it hadn't been prematurely freed */ le_bufferevent* buffer_event_check(lua_State* L, int idx) { le_bufferevent* buf = (le_bufferevent*)luaL_checkudata(L, idx, BUFFER_EVENT_MT); if(!buf->ev) luaL_argerror(L, idx, "Attempt to use closed buffer_event object"); return buf; } /* Checks if the given index contains an le_buffer object */ int is_buffer_event(lua_State* L, int idx) { int ret; lua_getmetatable(L, idx); luaL_getmetatable(L, BUFFER_EVENT_MT); ret = lua_rawequal(L, -2, -1); lua_pop(L, 2); return ret; } static void handle_callback(le_bufferevent* le_ev, short what, int callbackIndex) { lua_State* L = le_ev->base->loop_L; le_weak_get(L, le_ev); lua_getfenv(L, -1); lua_rawgeti(L, -1, callbackIndex); lua_remove(L, -2); lua_pushvalue(L, -2); lua_remove(L, -3); /* func, bufferevent */ lua_pushinteger(L, what); /* What to do w/ errors...? */ if(!lua_pcall(L, 2, 0, 0)) { /* FIXME: Perhaps luaevent users should be * able to set an error handler? */ lua_pop(L, 1); /* Pop error message */ } } static void buffer_event_readcb(struct bufferevent *ev, void *ptr) { handle_callback((le_bufferevent*)ptr, EVBUFFER_READ, 1); } static void buffer_event_writecb(struct bufferevent *ev, void *ptr) { handle_callback((le_bufferevent*)ptr, EVBUFFER_WRITE, 2); } static void buffer_event_errorcb(struct bufferevent *ev, short what, void *ptr) { handle_callback((le_bufferevent*)ptr, what, 3); } /* LUA: new(fd, read, write, error) Pushes a new bufferevent instance on the stack Accepts: base, fd, read, write, error cb Requires base, fd and error cb */ static int buffer_event_push(lua_State* L) { le_bufferevent *ev; le_base* base = event_base_get(L, 1); /* NOTE: Should probably reference the socket as well... */ int fd = getSocketFd(L, 2); luaL_checktype(L, 5, LUA_TFUNCTION); if(!lua_isnil(L, 3)) luaL_checktype(L, 3, LUA_TFUNCTION); if(!lua_isnil(L, 4)) luaL_checktype(L, 4, LUA_TFUNCTION); ev= (le_bufferevent*)lua_newuserdata(L, sizeof(le_bufferevent)); luaL_getmetatable(L, BUFFER_EVENT_MT); lua_setmetatable(L, -2); ev->ev = bufferevent_new(fd, buffer_event_readcb, buffer_event_writecb, buffer_event_errorcb, ev); lua_createtable(L, 5, 0); lua_pushvalue(L, 3); lua_rawseti(L, -2, 1); // Read lua_pushvalue(L, 4); lua_rawseti(L, -2, 2); // Write lua_pushvalue(L, 5); lua_rawseti(L, -2, 3); // Err event_buffer_push(L, ev->ev->input); lua_rawseti(L, -2, READ_BUFFER_LOCATION); event_buffer_push(L, ev->ev->output); lua_rawseti(L, -2, WRITE_BUFFER_LOCATION); lua_setfenv(L, -2); ev->base = base; return 1; } /* LUA: __gc and buffer:close() Releases the buffer resources */ static int buffer_event_gc(lua_State* L) { le_bufferevent* ev = buffer_event_get(L, 1); if(ev->ev) { le_buffer *read, *write; bufferevent_free(ev->ev); ev->ev = NULL; /* Also clear out the associated input/output event_buffers * since they would have already been freed.. */ lua_getfenv(L, 1); lua_rawgeti(L, -1, READ_BUFFER_LOCATION); lua_rawgeti(L, -2, WRITE_BUFFER_LOCATION); read = event_buffer_check(L, -2); write = event_buffer_check(L, -1); /* Erase Lua's link to the buffers */ lua_pushnil(L); /* LS: ..., fenv, readBuf, writeBuf, nil */ lua_rawseti(L, -4, READ_BUFFER_LOCATION); lua_pushnil(L); lua_rawseti(L, -4, WRITE_BUFFER_LOCATION); /* Erase their knowledge of the buffer so that the GC won't try to double-free */ read->buffer = NULL; write->buffer = NULL; } return 0; } static int buffer_event_get_read(lua_State* L) { (void)buffer_event_get(L, 1); lua_getfenv(L, 1); lua_rawgeti(L, -1, READ_BUFFER_LOCATION); return 1; } static int buffer_event_get_write(lua_State* L) { (void)buffer_event_get(L, 1); lua_getfenv(L, 1); lua_rawgeti(L, -1, WRITE_BUFFER_LOCATION); return 1; } static int buffer_event_set_read_watermarks(lua_State* L) { int low, high; le_bufferevent* ev = buffer_event_get(L, 1); if(!ev->ev) return 0; low = lua_tonumber(L, 2); high = lua_tonumber(L, 3); bufferevent_setwatermark(ev->ev, EV_READ, low, high); return 0; } static int buffer_event_set_write_watermarks(lua_State* L) { int low, high; le_bufferevent* ev = buffer_event_get(L, 1); if(!ev->ev) return 0; low = lua_tonumber(L, 2); high = lua_tonumber(L, 3); bufferevent_setwatermark(ev->ev, EV_WRITE, low, high); return 0; } static int buffer_event_set_timeouts(lua_State* L) { int timeout_read, timeout_write; le_bufferevent* ev = buffer_event_get(L, 1); if(!ev->ev) return 0; timeout_read = lua_tointeger(L, 2); timeout_write = lua_tointeger(L, 3); bufferevent_settimeout(ev->ev, timeout_read, timeout_write); return 0; } static int buffer_event_enable(lua_State* L) { le_bufferevent* ev = buffer_event_get(L, 1); if(!ev->ev) return 0; lua_pushinteger(L, bufferevent_enable(ev->ev, luaL_checkinteger(L, 2))); return 1; } static int buffer_event_disable(lua_State* L) { le_bufferevent* ev = buffer_event_get(L, 1); if(!ev->ev) return 0; lua_pushinteger(L, bufferevent_disable(ev->ev, luaL_checkinteger(L, 2))); return 1; } static luaL_Reg buffer_event_funcs[] = { {"get_read", buffer_event_get_read}, {"get_write", buffer_event_get_write}, {"set_read_watermarks", buffer_event_set_read_watermarks}, {"set_write_watermarks", buffer_event_set_write_watermarks}, {"set_timeouts", buffer_event_set_timeouts}, {"enable", buffer_event_enable}, {"disable", buffer_event_disable}, {NULL, NULL} }; static luaL_Reg funcs[] = { {"new", buffer_event_push}, {NULL, NULL} }; void buffer_event_register(lua_State* L, int coreIndex) { luaL_newmetatable(L, BUFFER_EVENT_MT); lua_pushcfunction(L, buffer_event_gc); lua_setfield(L, -2, "__gc"); lua_newtable(L); luaL_register(L, NULL, buffer_event_funcs); lua_setfield(L, -2, "__index"); lua_pop(L, 1); lua_newtable(L); luaL_register(L, NULL, funcs); lua_setfield(L, coreIndex, "bufferevent"); } lua-event-0.4.3/src/event_buffer.c000066400000000000000000000215631217402073200170300ustar00rootroot00000000000000/* LuaEvent Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "event_buffer.h" #include #define EVENT_BUFFER_MT "EVENT_BUFFER_MT" #define BUFFER_ADD_CHECK_INPUT_FIRST 1 /* Obtains an le_buffer structure from a given index */ static le_buffer* event_buffer_get(lua_State* L, int idx) { return (le_buffer*)luaL_checkudata(L, idx, EVENT_BUFFER_MT); } /* Obtains an le_buffer structure from a given index AND checks that it hadn't been prematurely freed */ le_buffer* event_buffer_check(lua_State* L, int idx) { le_buffer* buf = (le_buffer*)luaL_checkudata(L, idx, EVENT_BUFFER_MT); if(!buf->buffer) luaL_argerror(L, idx, "Attempt to use closed event_buffer object"); return buf; } /* Checks if the given index contains an le_buffer object */ int is_event_buffer(lua_State* L, int idx) { int ret; lua_getmetatable(L, idx); luaL_getmetatable(L, EVENT_BUFFER_MT); ret = lua_rawequal(L, -2, -1); lua_pop(L, 2); return ret; } /* TODO: Use lightuserdata mapping to locate hanging object instances */ /* Pushes the specified evbuffer object onto the stack, attaching a metatable to it */ int event_buffer_push(lua_State* L, struct evbuffer* buffer) { le_buffer *buf = (le_buffer*)lua_newuserdata(L, sizeof(le_buffer)); buf->buffer = buffer; luaL_getmetatable(L, EVENT_BUFFER_MT); lua_setmetatable(L, -2); return 1; } /* LUA: new() Pushes a new evbuffer instance on the stack */ static int event_buffer_push_new(lua_State* L) { return event_buffer_push(L, evbuffer_new()); } /* LUA: __gc and buffer:close() Releases the buffer resources */ static int event_buffer_gc(lua_State* L) { le_buffer* buf = event_buffer_get(L, 1); if(buf->buffer) { evbuffer_free(buf->buffer); buf->buffer = NULL; } return 0; } /* LUA: buffer:add(...) progressively adds items to the buffer if arg[*] is string, treat as a string:format call if arg[*] is a buffer, perform event_add_buffer expects at least 1 other argument returns number of bytes added */ static int event_buffer_add(lua_State* L) { le_buffer* buf = event_buffer_check(L, 1); struct evbuffer* buffer = buf->buffer; int oldLength = EVBUFFER_LENGTH(buffer); int last = lua_gettop(L); int i; if(last == 1) luaL_error(L, "Not enough arguments to add: expects at least 1 additional operand"); for(i = 2; i <= last; i++) { if(!lua_isstring(L, i) && !is_event_buffer(L, i)) luaL_argerror(L, i, "Argument is not a string or buffer object"); if(lua_equal(L, 1, i)) luaL_argerror(L, i, "Cannot add buffer to itself"); /* Optionally perform checks and data loading separately to avoid overfilling the buffer */ #if BUFFER_ADD_CHECK_INPUT_FIRST } for(i = 2; i <= last; i++) { #endif if(lua_isstring(L, i)) { size_t len; const char* data = lua_tolstring(L, i, &len); if(0 != evbuffer_add(buffer, data, len)) luaL_error(L, "Failed to add data to the buffer"); } else { le_buffer* buf2 = event_buffer_check(L, i); if(0 != evbuffer_add_buffer(buffer, buf2->buffer)) luaL_error(L, "Failed to move buffer-data to the buffer"); } } lua_pushinteger(L, EVBUFFER_LENGTH(buffer) - oldLength); return 1; } /* LUA: buffer:length() Returns the length of the buffer contents */ static int event_buffer_get_length(lua_State* L) { le_buffer* buf = event_buffer_check(L, 1); lua_pushinteger(L, EVBUFFER_LENGTH(buf->buffer)); return 1; } /* MAYBE: Could add caching */ /* LUA: buffer:get_data () - Returns all data in buffer (len) - Returns data up to 'len' bytes long (begin,len) - Returns data beginning at 'begin' up to 'len' bytes long If begin < 0, wraps at data length ex: (-1, 1) returns last character (-2,2) returns last 2 chars [length meaning does not get inverted] */ static int event_buffer_get_data(lua_State* L) { le_buffer* buf = event_buffer_check(L, 1); int begin, len; switch(lua_gettop(L)) { case 1: /* Obtain full data */ begin = 0; len = EVBUFFER_LENGTH(buf->buffer); break; case 2: begin = 0; len = luaL_checkinteger(L, 2); if(len > EVBUFFER_LENGTH(buf->buffer)) len = EVBUFFER_LENGTH(buf->buffer); break; case 3: default: /* - 1 to map it to Lua's 1-based indexing * If begin < 0 add length to cause position wrapping */ begin = luaL_checkinteger(L, 2); if(begin < 0) begin += EVBUFFER_LENGTH(buf->buffer); else begin--; len = luaL_checkinteger(L, 3); /* If length is less than zero, capture entire remaining string */ if(len < 0) len = EVBUFFER_LENGTH(buf->buffer); if(begin > EVBUFFER_LENGTH(buf->buffer)) begin = EVBUFFER_LENGTH(buf->buffer); if(begin + len > EVBUFFER_LENGTH(buf->buffer)) len = EVBUFFER_LENGTH(buf->buffer) - begin; break; } lua_pushlstring(L, (const char*)EVBUFFER_DATA(buf->buffer) + begin, len); return 1; } /* LUA: buffer:readline() Returns a line terminated by either '\r\n','\n\r' or '\r' or '\n' Returns nil and leaves data alone if no terminator is found Newline is not present in the captured string. */ static int event_buffer_readline(lua_State* L) { le_buffer* buf = event_buffer_check(L, 1); char* line = evbuffer_readline(buf->buffer); if(!line) return 0; lua_pushstring(L, line); free(line); return 1; } /* LUA: buffer:drain(amt) Drains 'amt' bytes from the buffer If amt < 0, drains all data (Due to auto-casting to unsigned int and automatic capping) */ static int event_buffer_drain(lua_State* L) { le_buffer* buf = event_buffer_check(L, 1); size_t len = luaL_checkinteger(L, 2); evbuffer_drain(buf->buffer, len); return 0; } /* LUA: buffer:write (integer/lightuserdata fd) - Attempts to write all the data out to the FD (socket) - Attempts to write all the data out to the socket object */ static int event_buffer_write(lua_State* L) { le_buffer* buf = event_buffer_check(L, 1); int ret; if(lua_isnumber(L, 2)) { ret = evbuffer_write(buf->buffer, lua_tointeger(L, 2)); } else if(lua_islightuserdata(L, 2)) { ret = evbuffer_write(buf->buffer, (int)(long)lua_touserdata(L, 2)); } else if(lua_isuserdata(L, 2)) { ret = evbuffer_write(buf->buffer, getSocketFd(L, 2)); } else { ret = 0; /* Shush uninitialized warning */ luaL_argerror(L, 2, "Unexpected data type. Expects: integer/lightuserdata/socket"); } lua_pushinteger(L, ret); return 1; } /* LUA: buffer:read (integer/lightuserdata fd, len) - Attempts to read up to 'len' out of the FD (socket, len) - Attempts to read up to 'len' out of the socket object */ static int event_buffer_read(lua_State* L) { le_buffer* buf = event_buffer_check(L, 1); int len = luaL_checkinteger(L, 3); int ret; if(lua_isnumber(L, 2)) { ret = evbuffer_read(buf->buffer, lua_tointeger(L, 2), len); } else if(lua_islightuserdata(L, 2)) { ret = evbuffer_read(buf->buffer, (int)(long)lua_touserdata(L, 2), len); } else if(lua_isuserdata(L, 2)) { ret = evbuffer_read(buf->buffer, getSocketFd(L, 2), len); } else { ret = 0; /* Shush uninitialized warning */ luaL_argerror(L, 2, "Unexpected data type. Expects: integer/lightuserdata/socket"); } lua_pushinteger(L, ret); return 1; } static luaL_Reg buffer_funcs[] = { {"add", event_buffer_add}, {"length", event_buffer_get_length}, {"get_data", event_buffer_get_data}, {"readline", event_buffer_readline}, {"drain", event_buffer_drain}, {"close", event_buffer_gc}, {"read", event_buffer_read}, {"write", event_buffer_write}, {NULL, NULL} }; static luaL_Reg funcs[] = { {"new", event_buffer_push_new}, {NULL, NULL} }; void event_buffer_register(lua_State* L, int coreIndex) { luaL_newmetatable(L, EVENT_BUFFER_MT); lua_pushcfunction(L, event_buffer_gc); lua_setfield(L, -2, "__gc"); lua_pushcfunction(L, event_buffer_get_length); lua_setfield(L, -2, "__len"); lua_pushcfunction(L, event_buffer_get_data); lua_setfield(L, -2, "__tostring"); lua_newtable(L); luaL_register(L, NULL, buffer_funcs); lua_setfield(L, -2, "__index"); lua_pop(L, 1); lua_newtable(L); luaL_register(L, NULL, funcs); lua_setfield(L, coreIndex, "buffer"); } lua-event-0.4.3/src/event_callback.c000066400000000000000000000100771217402073200173110ustar00rootroot00000000000000/* LuaEvent Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "event_callback.h" #include #include #include #define EVENT_CALLBACK_ARG_MT "EVENT_CALLBACK_ARG_MT" void freeCallbackArgs(le_callback* arg, lua_State* L) { if(arg->base) { arg->base = NULL; event_del(&arg->ev); luaL_unref(L, LUA_REGISTRYINDEX, arg->callbackRef); } } /* le_callback is allocated at the beginning of the coroutine in which it is used, no need to manually de-allocate */ /* Index for coroutine is fd as integer for *nix, as lightuserdata for Win */ void luaevent_callback(int fd, short event, void* p) { le_callback* cb = p; lua_State* L; int ret; struct timeval new_tv = { 0, 0 }; le_base* base; assert(cb); if(!cb->base) return; /* Event has already been collected + destroyed */ assert(cb->base->loop_L); L = cb->base->loop_L; lua_rawgeti(L, LUA_REGISTRYINDEX, cb->callbackRef); lua_pushinteger(L, event); /* cb->base may be NULL after the pcall, if the event is destroyed */ base = cb->base; if(lua_pcall(L, 1, 2, 0)) { base->errorMessage = luaL_ref(L, LUA_REGISTRYINDEX); event_base_loopbreak(base->base); lua_pop(L, 1); return; } if(!cb->base) { lua_pop(L, 2); return; /* event was destroyed during callback */ } /* If nothing is returned, re-use the old event value */ ret = luaL_optinteger(L, -2, event); /* Clone the old timeout value in case a new one wasn't set */ memcpy(&new_tv, &cb->timeout, sizeof(new_tv)); if(lua_isnumber(L, -1)) { double newTimeout = lua_tonumber(L, -1); if(newTimeout > 0) { load_timeval(newTimeout, &new_tv); } } lua_pop(L, 2); if(ret == -1) { freeCallbackArgs(cb, L); } else { struct event *ev = &cb->ev; int newEvent = ret; if( newEvent != event || (cb->timeout.tv_sec != new_tv.tv_sec || cb->timeout.tv_usec != new_tv.tv_usec) ) { struct timeval *ptv = &cb->timeout; cb->timeout = new_tv; if(!cb->timeout.tv_sec && !cb->timeout.tv_usec) ptv = NULL; event_del(ev); event_set(ev, fd, EV_PERSIST | newEvent, luaevent_callback, cb); /* Assume cannot set a new timeout.. */ event_add(ev, ptv); } } } static int luaevent_cb_gc(lua_State* L) { le_callback* arg = luaL_checkudata(L, 1, EVENT_CALLBACK_ARG_MT); freeCallbackArgs(arg, L); return 0; } le_callback* event_callback_push(lua_State* L, int baseIdx, int callbackIdx) { le_callback* cb; le_base *base = event_base_get(L, baseIdx); luaL_checktype(L, callbackIdx, LUA_TFUNCTION); cb = lua_newuserdata(L, sizeof(*cb)); luaL_getmetatable(L, EVENT_CALLBACK_ARG_MT); lua_setmetatable(L, -2); lua_pushvalue(L, callbackIdx); cb->callbackRef = luaL_ref(L, LUA_REGISTRYINDEX); cb->base = base; memset(&cb->timeout, 0, sizeof(cb->timeout)); return cb; } void event_callback_register(lua_State* L) { luaL_newmetatable(L, EVENT_CALLBACK_ARG_MT); lua_pushcfunction(L, luaevent_cb_gc); lua_setfield(L, -2, "__gc"); lua_newtable(L); lua_pushcfunction(L, luaevent_cb_gc); lua_setfield(L, -2, "close"); lua_setfield(L, -2, "__index"); lua_pop(L, 1); } lua-event-0.4.3/src/luaevent.c000066400000000000000000000132001217402073200161660ustar00rootroot00000000000000/* LuaEvent Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "event_callback.h" #include "event_buffer.h" #include "buffer_event.h" #include #include #include #define EVENT_BASE_MT "EVENT_BASE_MT" le_base* event_base_get(lua_State* L, int idx) { return (le_base*)luaL_checkudata(L, idx, EVENT_BASE_MT); } int luaevent_newbase(lua_State* L) { le_base *base = (le_base*)lua_newuserdata(L, sizeof(le_base)); base->loop_L = NULL; /* No running loop */ base->base = event_init(); luaL_getmetatable(L, EVENT_BASE_MT); lua_setmetatable(L, -2); return 1; } int luaevent_libevent_version(lua_State* L) { lua_pushstring(L, event_get_version()); return 1; } static int luaevent_base_gc(lua_State* L) { le_base *base = event_base_get(L, 1); if(base->base) { event_base_free(base->base); base->base = NULL; } return 0; } int getSocketFd(lua_State* L, int idx) { int fd; if(lua_isnumber(L, idx)) { fd = lua_tonumber(L, idx); } else { luaL_checktype(L, idx, LUA_TUSERDATA); lua_getfield(L, idx, "getfd"); if(lua_isnil(L, -1)) return luaL_error(L, "Socket type missing 'getfd' method"); lua_pushvalue(L, idx); lua_call(L, 1, 1); fd = lua_tointeger(L, -1); lua_pop(L, 1); } return fd; } void load_timeval(double time, struct timeval *tv) { tv->tv_sec = (int) time; tv->tv_usec = (int)( (time - tv->tv_sec) * 1000000 ); } /* sock, event, callback, timeout */ static int luaevent_addevent(lua_State* L) { int fd, event; le_callback* arg = event_callback_push(L, 1, 4); struct timeval *tv = &arg->timeout; if(lua_isnil(L, 2) && lua_isnumber(L, 5)) { fd = -1; /* Per event_timer_set.... */ } else { fd = getSocketFd(L, 2); } event = luaL_checkinteger(L, 3); if(lua_isnumber(L, 5)) { double time = lua_tonumber(L, 5); load_timeval(time, tv); } else { tv = NULL; } /* Setup event... */ event_set(&arg->ev, fd, event | EV_PERSIST, luaevent_callback, arg); event_base_set(arg->base->base, &arg->ev); event_add(&arg->ev, tv); return 1; } static int luaevent_loop(lua_State* L) { int ret; le_base *base = event_base_get(L, 1); base->loop_L = L; base->errorMessage = LUA_NOREF; ret = event_base_loop(base->base, 0); if(base->errorMessage != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, base->errorMessage); luaL_unref(L, LUA_REGISTRYINDEX, base->errorMessage); base->errorMessage = LUA_NOREF; return lua_error(L); } lua_pushinteger(L, ret); return 1; } static int luaevent_loopexit(lua_State*L) { int ret; le_base *base = event_base_get(L, 1); struct timeval tv = { 0, 0 }; if(lua_gettop(L) >= 2) /* Optional timeout before exiting the loop */ load_timeval(luaL_checknumber(L, 2), &tv); ret = event_base_loopexit(base->base, &tv); lua_pushinteger(L, ret); return 1; } static int luaevent_method(lua_State* L) { #ifdef _EVENT_VERSION le_base *base = event_base_get(L, 1); if(strcmp(_EVENT_VERSION, "1.3")<0) lua_pushstring(L, event_base_get_method(base->base)); else #endif lua_pushstring(L, event_get_method()); return 1; } static luaL_Reg base_funcs[] = { { "addevent", luaevent_addevent }, { "loop", luaevent_loop }, { "loopexit", luaevent_loopexit }, { "method", luaevent_method }, { NULL, NULL } }; static luaL_Reg funcs[] = { { "new", luaevent_newbase }, { "libevent_version", luaevent_libevent_version }, { NULL, NULL } }; typedef struct { const char* name; int value; } namedInteger; static namedInteger consts[] = { {"LEAVE", -1}, {"EV_READ", EV_READ}, {"EV_WRITE", EV_WRITE}, {"EV_TIMEOUT", EV_TIMEOUT}, {"EV_SIGNAL", EV_SIGNAL}, {"EV_PERSIST", EV_PERSIST}, /* bufferevent */ {"EVBUFFER_READ", EVBUFFER_READ}, {"EVBUFFER_WRITE", EVBUFFER_WRITE}, {"EVBUFFER_EOF", EVBUFFER_EOF}, {"EVBUFFER_ERROR", EVBUFFER_ERROR}, {"EVBUFFER_TIMEOUT", EVBUFFER_TIMEOUT}, {NULL, 0} }; void setNamedIntegers(lua_State* L, namedInteger* p) { while(p->name) { lua_pushinteger(L, p->value); lua_setfield(L, -2, p->name); p++; } } /* Verified ok */ int luaopen_luaevent_core(lua_State* L) { #ifdef _WIN32 WORD wVersionRequested = MAKEWORD(2, 2); WSADATA wsaData; WSAStartup(wVersionRequested, &wsaData); #endif event_init( ); /* Setup metatable */ luaL_newmetatable(L, EVENT_BASE_MT); lua_newtable(L); luaL_register(L, NULL, base_funcs); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, luaevent_base_gc); lua_setfield(L, -2, "__gc"); lua_pop(L, 1); lua_newtable(L); luaL_register(L, NULL, funcs); setNamedIntegers(L, consts); /* Register external items */ event_callback_register(L); event_buffer_register(L, lua_gettop(L)); buffer_event_register(L, lua_gettop(L)); return 1; } lua-event-0.4.3/src/utility.c000066400000000000000000000041621217402073200160550ustar00rootroot00000000000000/* LuaEvent Copyright (C) 2007,2012,2013 Thomas Harning Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "utility.h" #include #define WEAK_REF_LOCATION le_register_utility static void get_weakref_table(lua_State* L) { lua_pushlightuserdata(L, WEAK_REF_LOCATION); lua_gettable(L, LUA_REGISTRYINDEX); } void le_weak_ref(lua_State* L, void* ptr, int idx) { get_weakref_table(L); lua_pushlightuserdata(L, ptr); if(idx < 0) idx-=2; lua_pushvalue(L, idx); lua_settable(L, -3); } void le_weak_unref(lua_State* L, void* ptr) { get_weakref_table(L); lua_pushlightuserdata(L, ptr); lua_pushnil(L); lua_settable(L, -3); } void le_weak_get(lua_State* L, void* ptr) { get_weakref_table(L); lua_pushlightuserdata(L, ptr); lua_gettable(L, -2); } static void push_weak_table(lua_State* L, const char* mode) { lua_newtable(L); lua_createtable(L,0,1); lua_pushstring(L,mode); lua_setfield(L,-2,"__mode"); lua_setmetatable(L,-2); } void le_register_utility(lua_State* L) { lua_pushlightuserdata(L, WEAK_REF_LOCATION); push_weak_table(L, "v"); lua_settable(L, LUA_REGISTRYINDEX); } lua-event-0.4.3/test/000077500000000000000000000000001217402073200143735ustar00rootroot00000000000000lua-event-0.4.3/test/basic.lua000066400000000000000000000005001217402073200161520ustar00rootroot00000000000000local luaevent = require("luaevent") print("Version:", luaevent._NAME.." "..luaevent._VERSION) print("libevent version:", luaevent.core.libevent_version()) print("") base = luaevent.core.new() print("Testing creating base object:", type(base) == "userdata" and "OK" or "FAIL") print("libevent backend:", base:method()) lua-event-0.4.3/test/event_buffer-tests.lua000066400000000000000000000123771217402073200207220ustar00rootroot00000000000000local core = require("luaevent.core") local buffer = core.buffer require("lunit") --lunit.import("all") bufferTests = lunit.TestCase("bufferTests") function bufferTests:setup() self.buffer = buffer.new() self.buffer2 = buffer.new() end function bufferTests:teardown() self.buffer:close() self.buffer2:close() end local function testDataEqual(expected, actual, msg) msg = msg or '' lunit.assert_equal(expected, actual:get_data(), "Buffer not the same: " .. msg) lunit.assert_equal(#expected, actual:length(), "Buffer length not the same: " .. msg) lunit.assert_equal(expected, tostring(actual), "Buffer (from tostring) not the same: " .. msg) lunit.assert_equal(#expected, #actual, "Buffer length (from #) not zero: " .. msg) end function bufferTests:test_empty() testDataEqual("", self.buffer, "Buffer not empty") testDataEqual("", self.buffer2, "Buffer2 not empty") end function bufferTests:test_addSimpleString() self.buffer:add("Hello") testDataEqual("Hello", self.buffer) self.buffer:add("Hello") testDataEqual("HelloHello", self.buffer) end function bufferTests:test_addMultipleStrings() self.buffer:add("Hello", "Hello") testDataEqual("HelloHello", self.buffer) end function bufferTests:test_addDigits() self.buffer:add(1,2,3,4) testDataEqual("1234", self.buffer) self.buffer:add(100) testDataEqual("1234100", self.buffer) self.buffer:add(1.1) testDataEqual("12341001.1", self.buffer) end function bufferTests:test_addBuffer() self.buffer:add(self.buffer2) testDataEqual("", self.buffer) testDataEqual("", self.buffer2) self.buffer2:add("Hello") testDataEqual("Hello", self.buffer2) self.buffer:add(self.buffer2) testDataEqual("Hello", self.buffer) testDataEqual("", self.buffer2) lunit.assert_error("Cannot self-add buffers", function() self.buffer:add(self.buffer) end) lunit.assert_error("Cannot self-add buffers", function() self.buffer2:add(self.buffer2) end) testDataEqual("Hello", self.buffer, "Failures should not affect data content") testDataEqual("", self.buffer2, "Failures should not affect data content") end function bufferTests:test_addBadValues_fail() lunit.assert_error("Should not be able to add no values", function() self.buffer:add() end) lunit.assert_error("Should not be able to add boolean true", function() self.buffer:add(true) end) lunit.assert_error("Should not be able to add boolean false", function() self.buffer:add(false) end) lunit.assert_error("Should not be able to add functions", function() self.buffer:add(function() end) end) lunit.assert_error("Should not be able to add threads", function() self.buffer:add(coroutine.create(function() end)) end) lunit.assert_error("Should not be able to add non-buffer userdata", function() self.buffer:add(newproxy()) end) lunit.assert_error("Should not be able to add nil values", function() self.buffer:add(nil) end) lunit.assert_error("Multiples including valid values should not affect failure results", function() self.buffer:add("Hello", 1, bufferb, true, false, function() end, newproxy(), nil) end) testDataEqual("", self.buffer, "Buffer not empty after failing additions") end function bufferTests:test_drain() self.buffer:add("123456789") testDataEqual("123456789", self.buffer) lunit.assert_error("Cannot call drain w/ no args", function() self.buffer:drain() end) self.buffer:drain(1) testDataEqual("23456789", self.buffer) self.buffer:drain(4) testDataEqual("6789", self.buffer) lunit.assert_pass("Should be able to apply draining beyond actual buffer length", function() self.buffer:drain(5) end) testDataEqual("", self.buffer) self.buffer:add("123456789") testDataEqual("123456789", self.buffer) lunit.assert_pass([[Should be able to apply negative draining to cause draining `all data` (see source comments for why)]], function() self.buffer:drain(-1) end) testDataEqual("", self.buffer) end function bufferTests:test_getPartial() self.buffer:add("123456789") lunit.assert_equal("1234", self.buffer:get_data(4)) lunit.assert_equal("1234", self.buffer:get_data(1,4)) lunit.assert_equal("5678", self.buffer:get_data(5,4)) lunit.assert_equal("5", self.buffer:get_data(5,1)) lunit.assert_equal("56789", self.buffer:get_data(5,100000000), "Data length is capped at max obtainable") lunit.assert_equal("56789", self.buffer:get_data(5,-100), "Negative sizes capture entire remaining string") lunit.assert_equal("9", self.buffer:get_data(-1, 1, "Negative position causes wraparound")) lunit.assert_equal("89", self.buffer:get_data(-2,2, "Negative wraparound does not cause length inversion")) end local lineData = [[1 2 3]] local splitLineData = { "1","2",nil } local mixedLineData = "1\r2\n3\r\n4\n\r5\r\r6\n\n7\r\n\r8\r\n\r9" local splitMixedLineData = { "1","2","3","4","5","6","7","8", nil } function bufferTests:test_readline() self.buffer:add(lineData) testDataEqual(lineData, self.buffer) for _, data in ipairs(splitLineData) do lunit.assert_equal(data, self.buffer:readline()) end testDataEqual("3", self.buffer, "Failed readline doesn't affect buffer contents") self.buffer:drain(-1) testDataEqual("", self.buffer) self.buffer:add(mixedLineData) testDataEqual(mixedLineData, self.buffer) for _, data in ipairs(splitMixedLineData) do lunit.assert_equal(data, self.buffer:readline()) end testDataEqual("9", self.buffer) end lunit.run() lua-event-0.4.3/test/lunit.lua000066400000000000000000000434201217402073200162340ustar00rootroot00000000000000 --[[-------------------------------------------------------------------------- This file is part of lunit 0.4pre (alpha). For Details about lunit look at: http://www.nessie.de/mroth/lunit/ Author: Michael Roth Copyright (c) 2004 Michael Roth Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --]]-------------------------------------------------------------------------- ----------------------- -- Intialize package -- ----------------------- local P = { } lunit = P -- Import local type = type local print = print local ipairs = ipairs local pairs = pairs local string = string local table = table local pcall = pcall local xpcall = xpcall local traceback = debug.traceback local error = error local setmetatable = setmetatable local rawset = rawset local orig_assert = assert local getfenv = getfenv local setfenv = setfenv local tostring = tostring if not setfenv then _ENV = P else -- Start package scope setfenv(1, P) end -------------------------------- -- Private data and functions -- -------------------------------- local run_testcase local do_assert, check_msg local stats = { } local testcases = { } local stats_inc, tc_mt -------------------------- -- Type check functions -- -------------------------- function is_nil(x) return type(x) == "nil" end function is_boolean(x) return type(x) == "boolean" end function is_number(x) return type(x) == "number" end function is_string(x) return type(x) == "string" end function is_table(x) return type(x) == "table" end function is_function(x) return type(x) == "function" end function is_thread(x) return type(x) == "thread" end function is_userdata(x) return type(x) == "userdata" end ---------------------- -- Assert functions -- ---------------------- function assert(assertion, msg) stats_inc("assertions") check_msg("assert", msg) do_assert(not not assertion, "assertion failed (was: "..tostring(assertion)..")", msg) -- (convert assertion to bool) return assertion end function assert_fail(msg) stats_inc("assertions") check_msg("assert_fail", msg) do_assert(false, "failure", msg) end function assert_true(actual, msg) stats_inc("assertions") check_msg("assert_true", msg) do_assert(is_boolean(actual), "true expected but was a "..type(actual), msg) do_assert(actual == true, "true expected but was false", msg) return actual end function assert_false(actual, msg) stats_inc("assertions") check_msg("assert_false", msg) do_assert(is_boolean(actual), "false expected but was a "..type(actual), msg) do_assert(actual == false, "false expected but was true", msg) return actual end function assert_equal(expected, actual, msg) stats_inc("assertions") check_msg("assert_equal", msg) do_assert(expected == actual, "expected '"..tostring(expected).."' but was '"..tostring(actual).."'", msg) return actual end function assert_not_equal(unexpected, actual, msg) stats_inc("assertions") check_msg("assert_not_equal", msg) do_assert(unexpected ~= actual, "'"..tostring(expected).."' not expected but was one", msg) return actual end function assert_match(pattern, actual, msg) stats_inc("assertions") check_msg("assert_match", msg) do_assert(is_string(pattern), "assert_match expects the pattern as a string") do_assert(is_string(actual), "expected a string to match pattern '"..pattern.."' but was a '"..type(actual).."'", msg) do_assert(not not string.find(actual, pattern), "expected '"..actual.."' to match pattern '"..pattern.."' but doesn't", msg) return actual end function assert_not_match(pattern, actual, msg) stats_inc("assertions") check_msg("assert_not_match", msg) do_assert(is_string(actual), "expected a string to not match pattern '"..pattern.."' but was a '"..type(actual).."'", msg) do_assert(string.find(actual, pattern) == nil, "expected '"..actual.."' to not match pattern '"..pattern.."' but it does", msg) return actual end function assert_nil(actual, msg) stats_inc("assertions") check_msg("assert_nil", msg) do_assert(is_nil(actual), "nil expected but was a "..type(actual), msg) return actual end function assert_not_nil(actual, msg) stats_inc("assertions") check_msg("assert_not_nil", msg) do_assert(not is_nil(actual), "nil not expected but was one", msg) return actual end function assert_boolean(actual, msg) stats_inc("assertions") check_msg("assert_boolean", msg) do_assert(is_boolean(actual), "boolean expected but was a "..type(actual), msg) return actual end function assert_not_boolean(actual, msg) stats_inc("assertions") check_msg("assert_not_boolean", msg) do_assert(not is_boolean(actual), "boolean not expected but was one", msg) return actual end function assert_number(actual, msg) stats_inc("assertions") check_msg("assert_number", msg) do_assert(is_number(actual), "number expected but was a "..type(actual), msg) return actual end function assert_not_number(actual, msg) stats_inc("assertions") check_msg("assert_not_number", msg) do_assert(not is_number(actual), "number not expected but was one", msg) return actual end function assert_string(actual, msg) stats_inc("assertions") check_msg("assert_string", msg) do_assert(is_string(actual), "string expected but was a "..type(actual), msg) return actual end function assert_not_string(actual, msg) stats_inc("assertions") check_msg("assert_not_string", msg) do_assert(not is_string(actual), "string not expected but was one", msg) return actual end function assert_table(actual, msg) stats_inc("assertions") check_msg("assert_table", msg) do_assert(is_table(actual), "table expected but was a "..type(actual), msg) return actual end function assert_not_table(actual, msg) stats_inc("assertions") check_msg("assert_not_table", msg) do_assert(not is_table(actual), "table not expected but was one", msg) return actual end function assert_function(actual, msg) stats_inc("assertions") check_msg("assert_function", msg) do_assert(is_function(actual), "function expected but was a "..type(actual), msg) return actual end function assert_not_function(actual, msg) stats_inc("assertions") check_msg("assert_not_function", msg) do_assert(not is_function(actual), "function not expected but was one", msg) return actual end function assert_thread(actual, msg) stats_inc("assertions") check_msg("assert_thread", msg) do_assert(is_thread(actual), "thread expected but was a "..type(actual), msg) return actual end function assert_not_thread(actual, msg) stats_inc("assertions") check_msg("assert_not_thread", msg) do_assert(not is_thread(actual), "thread not expected but was one", msg) return actual end function assert_userdata(actual, msg) stats_inc("assertions") check_msg("assert_userdata", msg) do_assert(is_userdata(actual), "userdata expected but was a "..type(actual), msg) return actual end function assert_not_userdata(actual, msg) stats_inc("assertions") check_msg("assert_not_userdata", msg) do_assert(not is_userdata(actual), "userdata not expected but was one", msg) return actual end function assert_error(msg, func) stats_inc("assertions") if is_nil(func) then func, msg = msg, nil end check_msg("assert_error", msg) do_assert(is_function(func), "assert_error expects a function as the last argument but it was a "..type(func)) local ok, errmsg = pcall(func) do_assert(ok == false, "error expected but no error occurred", msg) end function assert_pass(msg, func) stats_inc("assertions") if is_nil(func) then func, msg = msg, nil end check_msg("assert_pass", msg) do_assert(is_function(func), "assert_pass expects a function as the last argument but it was a "..type(func)) local ok, errmsg = pcall(func) if not ok then do_assert(ok == true, "no error expected but error was: "..errmsg, msg) end end ----------------------------------------------------------- -- Assert implementation that assumes it was called from -- -- lunit code which was called directly from user code. -- ----------------------------------------------------------- function do_assert(assertion, base_msg, user_msg) orig_assert(is_boolean(assertion)) orig_assert(is_string(base_msg)) orig_assert(is_string(user_msg) or is_nil(user_msg)) if not assertion then if user_msg then error(base_msg..": "..user_msg, 3) else error(base_msg.."!", 3) end end end ------------------------------------------- -- Checks the msg argument in assert_xxx -- ------------------------------------------- function check_msg(name, msg) orig_assert(is_string(name)) if not (is_nil(msg) or is_string(msg)) then error("lunit."..name.."() expects the optional message as a string but it was a "..type(msg).."!" ,3) end end ------------------------------------- -- Creates a new TestCase 'Object' -- ------------------------------------- function TestCase(name) do_assert(is_string(name), "lunit.TestCase() needs a string as an argument") local tc = { __lunit_name = name; __lunit_setup = nil; __lunit_tests = { }; __lunit_teardown = nil; } setmetatable(tc, tc_mt) table.insert(testcases, tc) return tc end tc_mt = { __newindex = function(tc, key, value) rawset(tc, key, value) if is_string(key) and is_function(value) then local name = string.lower(key) if string.find(name, "^test") or string.find(name, "test$") then table.insert(tc.__lunit_tests, key) elseif name == "setup" then tc.__lunit_setup = value elseif name == "teardown" then tc.__lunit_teardown = value end end end } ----------------------------------------- -- Wrap Functions in a TestCase object -- ----------------------------------------- function wrap(name, ...) if is_function(name) then table.insert(arg, 1, name) name = "Anonymous Testcase" end local tc = TestCase(name) for index, test in ipairs(arg) do tc["Test #"..index] = test end return tc end ---------------------------------- -- Runs the complete Test Suite -- ---------------------------------- function run() --------------------------- -- Initialize statistics -- --------------------------- stats.testcases = 0 -- Total number of Test Cases stats.tests = 0 -- Total number of all Tests in all Test Cases stats.run = 0 -- Number of Tests run stats.notrun = 0 -- Number of Tests not run stats.failed = 0 -- Number of Tests failed stats.warnings = 0 -- Number of Warnings (teardown) stats.errors = 0 -- Number of Errors (setup) stats.passed = 0 -- Number of Test passed stats.assertions = 0 -- Number of all assertions made in all Test in all Test Cases -------------------------------- -- Count Test Cases and Tests -- -------------------------------- stats.testcases = #testcases -- table.getn(testcases) for _, tc in ipairs(testcases) do stats_inc("tests" , #tc.__lunit_tests) --table.getn(tc.__lunit_tests)) end ------------------ -- Print Header -- ------------------ print() print("#### Test Suite with "..stats.tests.." Tests in "..stats.testcases.." Test Cases loaded.") ------------------------ -- Run all Test Cases -- ------------------------ for _, tc in ipairs(testcases) do run_testcase(tc) end ------------------ -- Print Footer -- ------------------ print() print("#### Test Suite finished.") local msg_assertions = stats.assertions.." Assertions checked. " local msg_passed = stats.passed == stats.tests and "All Tests passed" or stats.passed.." Tests passed" local msg_failed = stats.failed > 0 and ", "..stats.failed.." failed" or "" local msg_run = stats.notrun > 0 and ", "..stats.notrun.." not run" or "" local msg_warn = stats.warnings > 0 and ", "..stats.warnings.." warnings" or "" print() print(msg_assertions..msg_passed..msg_failed..msg_run..msg_warn.."!") ----------------- -- Return code -- ----------------- if stats.passed == stats.tests then return 0 else return 1 end end ----------------------------- -- Runs a single Test Case -- ----------------------------- function run_testcase(tc) orig_assert(is_table(tc)) orig_assert(is_table(tc.__lunit_tests)) orig_assert(is_string(tc.__lunit_name)) orig_assert(is_nil(tc.__lunit_setup) or is_function(tc.__lunit_setup)) orig_assert(is_nil(tc.__lunit_teardown) or is_function(tc.__lunit_teardown)) ---------------------------------- -- Protected call to a function -- ---------------------------------- local function call(errprefix, func) orig_assert(is_string(errprefix)) orig_assert(is_function(func)) local ok, errmsg = xpcall(function() func(tc) end, traceback) if not ok then print() print(errprefix..": "..errmsg) end return ok end ------------------------------------ -- Calls setup() on the Test Case -- ------------------------------------ local function setup(testname) if tc.__lunit_setup then return call("ERROR: "..testname..": setup() failed", tc.__lunit_setup) else return true end end ------------------------------------------ -- Calls a single Test on the Test Case -- ------------------------------------------ local function run(testname) orig_assert(is_string(testname)) orig_assert(is_function(tc[testname])) local ok = call("FAIL: "..testname, tc[testname]) if not ok then stats_inc("failed") else stats_inc("passed") end return ok end --------------------------------------- -- Calls teardown() on the Test Case -- --------------------------------------- local function teardown(testname) if tc.__lunit_teardown then if not call("WARNING: "..testname..": teardown() failed", tc.__lunit_teardown) then stats_inc("warnings") end end end --------------------------------- -- Run all Tests on a TestCase -- --------------------------------- print() --print("#### Running '"..tc.__lunit_name.."' ("..table.getn(tc.__lunit_tests).." Tests)...") print("#### Running '"..tc.__lunit_name.."' ("..#tc.__lunit_tests.." Tests)...") for _, testname in ipairs(tc.__lunit_tests) do if setup(testname) then run(testname) stats_inc("run") teardown(testname) else print("WARN: Skipping '"..testname.."'...") stats_inc("notrun") end end end --------------------- -- Import function -- --------------------- function import(name) do_assert(is_string(name), "lunit.import() expects a single string as argument") local user_env = getfenv(2) -------------------------------------------------- -- Installs a specific function in the user env -- -------------------------------------------------- local function install(funcname) user_env[funcname] = P[funcname] end ---------------------------------------------------------- -- Install functions matching a pattern in the user env -- ---------------------------------------------------------- local function install_pattern(pattern) for funcname, _ in pairs(P) do if string.find(funcname, pattern) then install(funcname) end end end ------------------------------------------------------------ -- Installs assert() and all assert_xxx() in the user env -- ------------------------------------------------------------ local function install_asserts() install_pattern("^assert.*") end ------------------------------------------- -- Installs all is_xxx() in the user env -- ------------------------------------------- local function install_tests() install_pattern("^is_.+") end if name == "asserts" or name == "assertions" then install_asserts() elseif name == "tests" or name == "checks" then install_tests() elseif name == "all" then install_asserts() install_tests() install("TestCase") elseif string.find(name, "^assert.*") and P[name] then install(name) elseif string.find(name, "^is_.+") and P[name] then install(name) elseif name == "TestCase" then install("TestCase") else error("luniit.import(): invalid function '"..name.."' to import", 2) end end -------------------------------------------------- -- Installs a private environment on the caller -- -------------------------------------------------- function setprivfenv() local new_env = { } local new_env_mt = { __index = getfenv(2) } setmetatable(new_env, new_env_mt) setfenv(2, new_env) end -------------------------------------------------- -- Increments a counter in the statistics table -- -------------------------------------------------- function stats_inc(varname, value) orig_assert(is_table(stats)) orig_assert(is_string(varname)) orig_assert(is_nil(value) or is_number(value)) if not stats[varname] then return end stats[varname] = stats[varname] + (value or 1) end lua-event-0.4.3/test/test.lua000066400000000000000000000015021217402073200160530ustar00rootroot00000000000000-- Tests Copas with a simple Echo server -- -- Run the test file and the connect to the server by telnet on the used port -- to stop the test just send the command "quit" local luaevent = require("luaevent") local socket = require("socket") local oldPrint = print print = function(...) oldPrint("SRV", ...) end local function echoHandler(skt) while true do local data,ret = luaevent.receive(skt, 10) --print("GOT: ", data, ret) if data == "quit" or ret == 'closed' then break end luaevent.send(skt, data) collectgarbage() end skt:close() --print("DONE") end local server = assert(socket.bind("localhost", 20000)) server:settimeout(0) local coro = coroutine.create coroutine.create = function(...) local ret = coro(...) return ret end luaevent.addserver(server, echoHandler) luaevent.loop() lua-event-0.4.3/test/testClient.lua000066400000000000000000000007151217402073200172170ustar00rootroot00000000000000local luaevent = require("luaevent") local socket = require("socket") local oldPrint = print print = function(...) oldPrint("CLT", ...) end local function func() print("ACTIVATED") local sock = socket.tcp() --sock: sock = luaevent.wrap(sock) print(assert(sock:connect("localhost", 20000))) for i = 1, 100 do assert(sock:send("Greet me ")) assert(sock:receive(10)) collectgarbage() end print("COMPLETE") end luaevent.addthread(func) luaevent.loop() lua-event-0.4.3/test/timertest.lua000066400000000000000000000010621217402073200171150ustar00rootroot00000000000000local core = require("luaevent.core") c = core.new() local f = 100 local function createEvent() return c:addevent(nil, core.EV_TIMEOUT, function(ev) io.write(".." .. f) f = f - 1 if f < 0 then return -1 end collectgarbage() end, 0.01) end ev = createEvent() print("TESTING Garbage-collect-safe version") c:loop() assert(f < 0, "DID NOT FINISH LOOPING") io.write("\n") print("TESTING Garbage-collect unsafe version") f = 100 createEvent() c:loop() assert(f >= 0, "Did not perform expected collection") io.write("\n") print("Completed both tests")