| 1 | --[[ |
| 2 | LuaEvent - Copyright (C) 2007 Thomas Harning <harningt@gmail.com> |
| 3 | Licensed as LGPL - See doc/COPYING for details. |
| 4 | ]] |
| 5 | module("luaevent", package.seeall) |
| 6 | require("luaevent.core") |
| 7 | |
| 8 | _NAME = "luaevent"; |
| 9 | _VERSION = "0.3.2"; |
| 10 | |
| 11 | local EV_READ = luaevent.core.EV_READ |
| 12 | local EV_WRITE = luaevent.core.EV_WRITE |
| 13 | local base = luaevent.core.new() |
| 14 | |
| 15 | local function addevent(...) |
| 16 | return base:addevent(...) |
| 17 | end |
| 18 | |
| 19 | local function getWrapper() |
| 20 | local running = coroutine.running() |
| 21 | return function(...) |
| 22 | return select(2, coroutine.resume(running, ...)) |
| 23 | end |
| 24 | end |
| 25 | -- Weak keys.. the keys are the client sockets |
| 26 | local clientTable = setmetatable({}, {'__mode', 'kv'}) |
| 27 | local function socketWait(sock, event) |
| 28 | if not clientTable[sock] then clientTable[sock] = addevent(sock, event, getWrapper()) end |
| 29 | coroutine.yield(event) |
| 30 | end |
| 31 | |
| 32 | |
| 33 | function send(sock, data, start, stop) |
| 34 | local s, err |
| 35 | local from = start or 1 |
| 36 | local sent = 0 |
| 37 | repeat |
| 38 | from = from + sent |
| 39 | s, err, sent = sock:send(data, from, stop) |
| 40 | if s or err ~= "timeout" then return s, err, sent end |
| 41 | socketWait(sock, EV_WRITE) |
| 42 | until false |
| 43 | end |
| 44 | function receive(sock, pattern, part) |
| 45 | local s, err |
| 46 | pattern = pattern or '*l' |
| 47 | repeat |
| 48 | s, err, part = sock:receive(pattern, part) |
| 49 | if s or err ~= "timeout" then return s, err, part end |
| 50 | socketWait(sock, EV_READ) |
| 51 | until false |
| 52 | end |
| 53 | -- same as above but with special treatment when reading chunks, |
| 54 | -- unblocks on any data received. |
| 55 | function receivePartial(client, pattern) |
| 56 | local s, err, part |
| 57 | pattern = pattern or "*l" |
| 58 | repeat |
| 59 | s, err, part = client:receive(pattern) |
| 60 | if s or ( (type(pattern)=="number") and part~="" and part ~=nil ) or |
| 61 | err ~= "timeout" then return s, err, part end |
| 62 | socketWait(sock, EV_READ) |
| 63 | until false |
| 64 | end |
| 65 | function connect(sock, ...) |
| 66 | sock:settimeout(0) |
| 67 | local ret, err = sock:connect(...) |
| 68 | if ret or err ~= "timeout" then return ret, err end |
| 69 | socketWait(sock, EV_WRITE) |
| 70 | ret, err = sock:connect(...) |
| 71 | if err == "already connected" then |
| 72 | return 1 |
| 73 | end |
| 74 | return ret, err |
| 75 | end |
| 76 | -- Deprecated.. |
| 77 | function flush(sock) |
| 78 | end |
| 79 | local function clientCoroutine(sock, handler) |
| 80 | -- Figure out what to do ...... |
| 81 | return handler(sock) |
| 82 | end |
| 83 | local function handleClient(co, client, handler) |
| 84 | local ok, res, event = coroutine.resume(co, client, handler) |
| 85 | end |
| 86 | local function serverCoroutine(sock, callback) |
| 87 | local listenItem = addevent(sock, EV_READ, getWrapper()) |
| 88 | repeat |
| 89 | local event = coroutine.yield(EV_READ) |
| 90 | -- Get new socket |
| 91 | local client = sock:accept() |
| 92 | if client then |
| 93 | client:settimeout(0) |
| 94 | local co = coroutine.create(clientCoroutine) |
| 95 | handleClient(co, client, callback) |
| 96 | end |
| 97 | until false |
| 98 | end |
| 99 | function addserver(sock, callback) |
| 100 | local coro = coroutine.create(serverCoroutine) |
| 101 | assert(coroutine.resume(coro, sock, callback)) |
| 102 | end |
| 103 | function addthread(func, ...) |
| 104 | return coroutine.resume(coroutine.create(func), ...) |
| 105 | end |
| 106 | local _skt_mt = {__index = { |
| 107 | connect = function(self, ...) |
| 108 | return connect(self.socket, ...) |
| 109 | end, |
| 110 | send = function (self, data) |
| 111 | return send(self.socket, data) |
| 112 | end, |
| 113 | |
| 114 | receive = function (self, pattern) |
| 115 | if (self.timeout==0) then |
| 116 | return receivePartial(self.socket, pattern) |
| 117 | end |
| 118 | return receive(self.socket, pattern) |
| 119 | end, |
| 120 | |
| 121 | flush = function (self) |
| 122 | return flush(self.socket) |
| 123 | end, |
| 124 | |
| 125 | settimeout = function (self,time) |
| 126 | self.timeout=time |
| 127 | return |
| 128 | end, |
| 129 | |
| 130 | close = function(self) |
| 131 | clientTable[self.socket]:close() |
| 132 | self.socket:close() |
| 133 | end |
| 134 | }} |
| 135 | function wrap(sock) |
| 136 | return setmetatable({socket = sock}, _skt_mt) |
| 137 | end |
| 138 | loop = function(...) base:loop(...) end |