fb05e8177cfd88af2fd62fb86fa163cab7cdcfe0
[prosody.git] / net / server.lua
1 -- \r
2 -- server.lua by blastbeat of the luadch project\r
3 -- Re-used here under the MIT/X Consortium License\r
4 -- \r
5 -- Modifications (C) 2008-2009 Matthew Wild, Waqas Hussain\r
6 --\r
7 \r
8 -- // wrapping luadch stuff // --\r
9 \r
10 local use = function( what )\r
11     return _G[ what ]\r
12 end\r
13 local clean = function( tbl )\r
14     for i, k in pairs( tbl ) do\r
15         tbl[ i ] = nil\r
16     end\r
17 end\r
18 \r
19 local log, table_concat = require ("util.logger").init("socket"), table.concat;\r
20 local out_put = function (...) return log("debug", table_concat{...}); end\r
21 local out_error = function (...) return log("warn", table_concat{...}); end\r
22 local mem_free = collectgarbage\r
23 \r
24 ----------------------------------// DECLARATION //--\r
25 \r
26 --// constants //--\r
27 \r
28 local STAT_UNIT = 1    -- byte\r
29 \r
30 --// lua functions //--\r
31 \r
32 local type = use "type"\r
33 local pairs = use "pairs"\r
34 local ipairs = use "ipairs"\r
35 local tostring = use "tostring"\r
36 local collectgarbage = use "collectgarbage"\r
37 \r
38 --// lua libs //--\r
39 \r
40 local os = use "os"\r
41 local table = use "table"\r
42 local string = use "string"\r
43 local coroutine = use "coroutine"\r
44 \r
45 --// lua lib methods //--\r
46 \r
47 local os_time = os.time\r
48 local os_difftime = os.difftime\r
49 local table_concat = table.concat\r
50 local table_remove = table.remove\r
51 local string_len = string.len\r
52 local string_sub = string.sub\r
53 local coroutine_wrap = coroutine.wrap\r
54 local coroutine_yield = coroutine.yield\r
55 \r
56 --// extern libs //--\r
57 \r
58 local luasec = select( 2, pcall( require, "ssl" ) )\r
59 local luasocket = require "socket"\r
60 \r
61 --// extern lib methods //--\r
62 \r
63 local ssl_wrap = ( luasec and luasec.wrap )\r
64 local socket_bind = luasocket.bind\r
65 local socket_sleep = luasocket.sleep\r
66 local socket_select = luasocket.select\r
67 local ssl_newcontext = ( luasec and luasec.newcontext )\r
68 \r
69 --// functions //--\r
70 \r
71 local id\r
72 local loop\r
73 local stats\r
74 local idfalse\r
75 local addtimer\r
76 local closeall\r
77 local addserver\r
78 local getserver\r
79 local wrapserver\r
80 local getsettings\r
81 local closesocket\r
82 local removesocket\r
83 local removeserver\r
84 local changetimeout\r
85 local wrapconnection\r
86 local changesettings\r
87 \r
88 --// tables //--\r
89 \r
90 local _server\r
91 local _readlist\r
92 local _timerlist\r
93 local _sendlist\r
94 local _socketlist\r
95 local _closelist\r
96 local _readtimes\r
97 local _writetimes\r
98 \r
99 --// simple data types //--\r
100 \r
101 local _\r
102 local _readlistlen\r
103 local _sendlistlen\r
104 local _timerlistlen\r
105 \r
106 local _sendtraffic\r
107 local _readtraffic\r
108 \r
109 local _selecttimeout\r
110 local _sleeptime\r
111 \r
112 local _starttime\r
113 local _currenttime\r
114 \r
115 local _maxsendlen\r
116 local _maxreadlen\r
117 \r
118 local _checkinterval\r
119 local _sendtimeout\r
120 local _readtimeout\r
121 \r
122 local _cleanqueue\r
123 \r
124 local _timer\r
125 \r
126 local _maxclientsperserver\r
127 \r
128 ----------------------------------// DEFINITION //--\r
129 \r
130 _server = { }    -- key = port, value = table; list of listening servers\r
131 _readlist = { }    -- array with sockets to read from\r
132 _sendlist = { }    -- arrary with sockets to write to\r
133 _timerlist = { }    -- array of timer functions\r
134 _socketlist = { }    -- key = socket, value = wrapped socket (handlers)\r
135 _readtimes = { }   -- key = handler, value = timestamp of last data reading\r
136 _writetimes = { }   -- key = handler, value = timestamp of last data writing/sending\r
137 _closelist = { }    -- handlers to close\r
138 \r
139 _readlistlen = 0    -- length of readlist\r
140 _sendlistlen = 0    -- length of sendlist\r
141 _timerlistlen = 0    -- lenght of timerlist\r
142 \r
143 _sendtraffic = 0    -- some stats\r
144 _readtraffic = 0\r
145 \r
146 _selecttimeout = 1    -- timeout of socket.select\r
147 _sleeptime = 0    -- time to wait at the end of every loop\r
148 \r
149 _maxsendlen = 51000 * 1024    -- max len of send buffer\r
150 _maxreadlen = 25000 * 1024    -- max len of read buffer\r
151 \r
152 _checkinterval = 1200000    -- interval in secs to check idle clients\r
153 _sendtimeout = 60000    -- allowed send idle time in secs\r
154 _readtimeout = 6 * 60 * 60    -- allowed read idle time in secs\r
155 \r
156 _cleanqueue = false    -- clean bufferqueue after using\r
157 \r
158 _maxclientsperserver = 1000\r
159 \r
160 ----------------------------------// PRIVATE //--\r
161 \r
162 wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx, maxconnections, startssl )    -- this function wraps a server\r
163 \r
164     maxconnections = maxconnections or _maxclientsperserver\r
165 \r
166     local connections = 0\r
167 \r
168     local dispatch, disconnect = listeners.incoming or listeners.listener, listeners.disconnect\r
169 \r
170     local err\r
171 \r
172     local ssl = false\r
173 \r
174     if sslctx then\r
175         ssl = true\r
176         if not ssl_newcontext then\r
177             out_error "luasec not found"\r
178             ssl = false\r
179         end\r
180         if type( sslctx ) ~= "table" then\r
181             out_error "server.lua: wrong server sslctx"\r
182             ssl = false\r
183         end\r
184         local ctx;\r
185         ctx, err = ssl_newcontext( sslctx )\r
186         if not ctx then\r
187             err = err or "wrong sslctx parameters"\r
188             local file;\r
189             file = err:match("^error loading (.-) %(");\r
190             if file then\r
191                 if file == "private key" then\r
192                         file = sslctx.key or "your private key";\r
193                 elseif file == "certificate" then\r
194                         file = sslctx.certificate or "your certificate file";\r
195                 end\r
196                 local reason = err:match("%((.+)%)$") or "some reason";\r
197                 if reason == "Permission denied" then\r
198                         reason = "Check that the permissions allow Prosody to read this file.";\r
199                 elseif reason == "No such file or directory" then\r
200                         reason = "Check that the path is correct, and the file exists.";\r
201                 elseif reason == "system lib" then\r
202                         reason = "Previous error (see logs), or other system error.";\r
203                 else\r
204                         reason = "Reason: "..tostring(reason or "unknown"):lower();\r
205                 end\r
206                 log("error", "SSL/TLS: Failed to load %s: %s", file, reason);\r
207             else\r
208                 log("error", "SSL/TLS: Error initialising for port %d: %s", serverport, err );\r
209             end\r
210             ssl = false\r
211         end\r
212         sslctx = ctx;\r
213     end\r
214     if not ssl then\r
215       sslctx = false;\r
216       if startssl then\r
217          log("error", "Failed to listen on port %d due to SSL/TLS to SSL/TLS initialisation errors (see logs)", serverport )\r
218          return nil, "Cannot start ssl,  see log for details"\r
219        end\r
220     end\r
221 \r
222     local accept = socket.accept\r
223 \r
224     --// public methods of the object //--\r
225 \r
226     local handler = { }\r
227 \r
228     handler.shutdown = function( ) end\r
229 \r
230     handler.ssl = function( )\r
231         return ssl\r
232     end\r
233     handler.sslctx = function( )\r
234         return sslctx\r
235     end\r
236     handler.remove = function( )\r
237         connections = connections - 1\r
238     end\r
239     handler.close = function( )\r
240         for _, handler in pairs( _socketlist ) do\r
241             if handler.serverport == serverport then\r
242                 handler.disconnect( handler, "server closed" )\r
243                 handler.close( true )\r
244             end\r
245         end\r
246         socket:close( )\r
247         _sendlistlen = removesocket( _sendlist, socket, _sendlistlen )\r
248         _readlistlen = removesocket( _readlist, socket, _readlistlen )\r
249         _socketlist[ socket ] = nil\r
250         handler = nil\r
251         socket = nil\r
252         --mem_free( )\r
253         out_put "server.lua: closed server handler and removed sockets from list"\r
254     end\r
255     handler.ip = function( )\r
256         return ip\r
257     end\r
258     handler.serverport = function( )\r
259         return serverport\r
260     end\r
261     handler.socket = function( )\r
262         return socket\r
263     end\r
264     handler.readbuffer = function( )\r
265         if connections > maxconnections then\r
266             out_put( "server.lua: refused new client connection: server full" )\r
267             return false\r
268         end\r
269         local client, err = accept( socket )    -- try to accept\r
270         if client then\r
271             local ip, clientport = client:getpeername( )\r
272             client:settimeout( 0 )\r
273             local handler, client, err = wrapconnection( handler, listeners, client, ip, serverport, clientport, pattern, sslctx, startssl )    -- wrap new client socket\r
274             if err then    -- error while wrapping ssl socket\r
275                 return false\r
276             end\r
277             connections = connections + 1\r
278             out_put( "server.lua: accepted new client connection from ", tostring(ip), ":", tostring(clientport), " to ", tostring(serverport))\r
279             return dispatch( handler )\r
280         elseif err then    -- maybe timeout or something else\r
281             out_put( "server.lua: error with new client connection: ", tostring(err) )\r
282             return false\r
283         end\r
284     end\r
285     return handler\r
286 end\r
287 \r
288 wrapconnection = function( server, listeners, socket, ip, serverport, clientport, pattern, sslctx, startssl )    -- this function wraps a client to a handler object\r
289 \r
290     socket:settimeout( 0 )\r
291 \r
292     --// local import of socket methods //--\r
293 \r
294     local send\r
295     local receive\r
296     local shutdown\r
297 \r
298     --// private closures of the object //--\r
299 \r
300     local ssl\r
301 \r
302     local dispatch = listeners.incoming or listeners.listener\r
303     local status = listeners.status\r
304     local disconnect = listeners.disconnect\r
305 \r
306     local bufferqueue = { }    -- buffer array\r
307     local bufferqueuelen = 0    -- end of buffer array\r
308 \r
309     local toclose\r
310     local fatalerror\r
311     local needtls\r
312 \r
313     local bufferlen = 0\r
314 \r
315     local noread = false\r
316     local nosend = false\r
317 \r
318     local sendtraffic, readtraffic = 0, 0\r
319 \r
320     local maxsendlen = _maxsendlen\r
321     local maxreadlen = _maxreadlen\r
322 \r
323     --// public methods of the object //--\r
324 \r
325     local handler = bufferqueue    -- saves a table ^_^\r
326 \r
327     handler.dispatch = function( )\r
328         return dispatch\r
329     end\r
330     handler.disconnect = function( )\r
331         return disconnect\r
332     end\r
333     handler.setlistener = function( listeners )\r
334         dispatch = listeners.incoming\r
335         disconnect = listeners.disconnect\r
336     end\r
337     handler.getstats = function( )\r
338         return readtraffic, sendtraffic\r
339     end\r
340     handler.ssl = function( )\r
341         return ssl\r
342     end\r
343     handler.sslctx = function ( )\r
344         return sslctx\r
345     end\r
346     handler.send = function( _, data, i, j )\r
347         return send( socket, data, i, j )\r
348     end\r
349     handler.receive = function( pattern, prefix )\r
350         return receive( socket, pattern, prefix )\r
351     end\r
352     handler.shutdown = function( pattern )\r
353         return shutdown( socket, pattern )\r
354     end\r
355     handler.close = function( forced )\r
356         if not handler then return true; end\r
357         _readlistlen = removesocket( _readlist, socket, _readlistlen )\r
358         _readtimes[ handler ] = nil\r
359         if bufferqueuelen ~= 0 then\r
360             if not ( forced or fatalerror ) then\r
361                 handler.sendbuffer( )\r
362                 if bufferqueuelen ~= 0 then   -- try again...\r
363                     if handler then\r
364                         handler.write = nil    -- ... but no further writing allowed\r
365                     end\r
366                     toclose = true\r
367                     return false\r
368                 end\r
369             else\r
370                 send( socket, table_concat( bufferqueue, "", 1, bufferqueuelen ), 1, bufferlen )    -- forced send\r
371             end\r
372         end\r
373         if not handler then return true; end\r
374         _ = shutdown and shutdown( socket )\r
375         socket:close( )\r
376         _sendlistlen = removesocket( _sendlist, socket, _sendlistlen )\r
377         _socketlist[ socket ] = nil\r
378         if handler then\r
379             _writetimes[ handler ] = nil\r
380             _closelist[ handler ] = nil\r
381             handler = nil\r
382         end\r
383         socket = nil\r
384         --mem_free( )\r
385         if server then\r
386                 server.remove( )\r
387         end\r
388         out_put "server.lua: closed client handler and removed socket from list"\r
389         return true\r
390     end\r
391     handler.ip = function( )\r
392         return ip\r
393     end\r
394     handler.serverport = function( )\r
395         return serverport\r
396     end\r
397     handler.clientport = function( )\r
398         return clientport\r
399     end\r
400     local write = function( data )\r
401         bufferlen = bufferlen + string_len( data )\r
402         if bufferlen > maxsendlen then\r
403             _closelist[ handler ] = "send buffer exceeded"   -- cannot close the client at the moment, have to wait to the end of the cycle\r
404             handler.write = idfalse    -- dont write anymore\r
405             return false\r
406         elseif socket and not _sendlist[ socket ] then\r
407             _sendlistlen = _sendlistlen + 1\r
408             _sendlist[ _sendlistlen ] = socket\r
409             _sendlist[ socket ] = _sendlistlen\r
410         end\r
411         bufferqueuelen = bufferqueuelen + 1\r
412         bufferqueue[ bufferqueuelen ] = data\r
413         if handler then\r
414                 _writetimes[ handler ] = _writetimes[ handler ] or _currenttime\r
415         end\r
416         return true\r
417     end\r
418     handler.write = write\r
419     handler.bufferqueue = function( )\r
420         return bufferqueue\r
421     end\r
422     handler.socket = function( )\r
423         return socket\r
424     end\r
425     handler.pattern = function( new )\r
426         pattern = new or pattern\r
427         return pattern\r
428     end\r
429     handler.setsend = function ( newsend )\r
430         send = newsend or send\r
431         return send\r
432     end\r
433     handler.bufferlen = function( readlen, sendlen )\r
434         maxsendlen = sendlen or maxsendlen\r
435         maxreadlen = readlen or maxreadlen\r
436         return maxreadlen, maxsendlen\r
437     end\r
438     handler.lock = function( switch )\r
439         if switch == true then\r
440             handler.write = idfalse\r
441             local tmp = _sendlistlen\r
442             _sendlistlen = removesocket( _sendlist, socket, _sendlistlen )\r
443             _writetimes[ handler ] = nil\r
444             if _sendlistlen ~= tmp then\r
445                 nosend = true\r
446             end\r
447             tmp = _readlistlen\r
448             _readlistlen = removesocket( _readlist, socket, _readlistlen )\r
449             _readtimes[ handler ] = nil\r
450             if _readlistlen ~= tmp then\r
451                 noread = true\r
452             end\r
453         elseif switch == false then\r
454             handler.write = write\r
455             if noread then\r
456                 noread = false\r
457                 _readlistlen = _readlistlen + 1\r
458                 _readlist[ socket ] = _readlistlen\r
459                 _readlist[ _readlistlen ] = socket\r
460                 _readtimes[ handler ] = _currenttime\r
461             end\r
462             if nosend then\r
463                 nosend = false\r
464                 write( "" )\r
465             end\r
466         end\r
467         return noread, nosend\r
468     end\r
469     local _readbuffer = function( )    -- this function reads data\r
470         local buffer, err, part = receive( socket, pattern )    -- receive buffer with "pattern"\r
471         if not err or ( err == "timeout" or err == "wantread" ) then    -- received something\r
472             local buffer = buffer or part or ""\r
473             local len = string_len( buffer )\r
474             if len > maxreadlen then\r
475                 disconnect( handler, "receive buffer exceeded" )\r
476                 handler.close( true )\r
477                 return false\r
478             end\r
479             local count = len * STAT_UNIT\r
480             readtraffic = readtraffic + count\r
481             _readtraffic = _readtraffic + count\r
482             _readtimes[ handler ] = _currenttime\r
483             --out_put( "server.lua: read data '", buffer, "', error: ", err )\r
484             return dispatch( handler, buffer, err )\r
485         else    -- connections was closed or fatal error\r
486             out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " error: ", tostring(err) )\r
487             fatalerror = true\r
488             disconnect( handler, err )\r
489             _ = handler and handler.close( )\r
490             return false\r
491         end\r
492     end\r
493     local _sendbuffer = function( )    -- this function sends data\r
494         local succ, err, byte, buffer, count;\r
495         local count;\r
496         if socket then\r
497             buffer = table_concat( bufferqueue, "", 1, bufferqueuelen )\r
498             succ, err, byte = send( socket, buffer, 1, bufferlen )\r
499             count = ( succ or byte or 0 ) * STAT_UNIT\r
500             sendtraffic = sendtraffic + count\r
501             _sendtraffic = _sendtraffic + count\r
502             _ = _cleanqueue and clean( bufferqueue )\r
503             --out_put( "server.lua: sended '", buffer, "', bytes: ", tostring(succ), ", error: ", tostring(err), ", part: ", tostring(byte), ", to: ", tostring(ip), ":", tostring(clientport) )\r
504         else\r
505             succ, err, count = false, "closed", 0;\r
506         end\r
507         if succ then    -- sending succesful\r
508             bufferqueuelen = 0\r
509             bufferlen = 0\r
510             _sendlistlen = removesocket( _sendlist, socket, _sendlistlen )    -- delete socket from writelist\r
511             _ = needtls and handler.starttls(true)\r
512             _writetimes[ handler ] = nil\r
513             _ = toclose and handler.close( )\r
514             return true\r
515         elseif byte and ( err == "timeout" or err == "wantwrite" ) then    -- want write\r
516             buffer = string_sub( buffer, byte + 1, bufferlen )    -- new buffer\r
517             bufferqueue[ 1 ] = buffer    -- insert new buffer in queue\r
518             bufferqueuelen = 1\r
519             bufferlen = bufferlen - byte\r
520             _writetimes[ handler ] = _currenttime\r
521             return true\r
522         else    -- connection was closed during sending or fatal error\r
523             out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " error: ", tostring(err) )\r
524             fatalerror = true\r
525             disconnect( handler, err )\r
526             _ = handler and handler.close( )\r
527             return false\r
528         end\r
529     end\r
530 \r
531     if sslctx then    -- ssl?\r
532         ssl = true\r
533         local wrote\r
534         local read\r
535         local handshake = coroutine_wrap( function( client )    -- create handshake coroutine\r
536                 local err\r
537                 for i = 1, 10 do    -- 10 handshake attemps\r
538                     _sendlistlen = ( wrote and removesocket( _sendlist, socket, _sendlistlen ) ) or _sendlistlen\r
539                     _readlistlen = ( read and removesocket( _readlist, socket, _readlistlen ) ) or _readlistlen\r
540                     read, wrote = nil, nil\r
541                     _, err = client:dohandshake( )\r
542                     if not err then\r
543                         out_put( "server.lua: ssl handshake done" )\r
544                         handler.readbuffer = _readbuffer    -- when handshake is done, replace the handshake function with regular functions\r
545                         handler.sendbuffer = _sendbuffer\r
546                         _ = status and status( handler, "ssl-handshake-complete" )\r
547                         return true\r
548                     else\r
549                         out_put( "server.lua: error during ssl handshake: ", tostring(err) )\r
550                         if err == "wantwrite" and not wrote then\r
551                             _sendlistlen = _sendlistlen + 1\r
552                             _sendlist[ _sendlistlen ] = client\r
553                             wrote = true\r
554                         elseif err == "wantread" and not read then\r
555                                 _readlistlen = _readlistlen + 1\r
556                                 _readlist [ _readlistlen ] = client\r
557                                 read = true\r
558                         else\r
559                                 break;\r
560                         end\r
561                         --coroutine_yield( handler, nil, err )    -- handshake not finished\r
562                         coroutine_yield( )\r
563                     end\r
564                 end\r
565                 disconnect( handler, "ssl handshake failed" )\r
566                 _ = handler and handler.close( true )    -- forced disconnect\r
567                 return false    -- handshake failed\r
568             end\r
569         )\r
570         if startssl then    -- ssl now?\r
571             --out_put("server.lua: ", "starting ssl handshake")\r
572             local err\r
573             socket, err = ssl_wrap( socket, sslctx )    -- wrap socket\r
574             if err then\r
575                 out_put( "server.lua: ssl error: ", tostring(err) )\r
576                 --mem_free( )\r
577                 return nil, nil, err    -- fatal error\r
578             end\r
579             socket:settimeout( 0 )\r
580             handler.readbuffer = handshake\r
581             handler.sendbuffer = handshake\r
582             handshake( socket ) -- do handshake\r
583             if not socket then\r
584                 return nil, nil, "ssl handshake failed";\r
585             end\r
586         else\r
587             -- We're not automatically doing SSL, so we're not secure (yet)\r
588             ssl = false\r
589             handler.starttls = function( now )\r
590                 if not now then\r
591                     --out_put "server.lua: we need to do tls, but delaying until later"\r
592                     needtls = true\r
593                     return\r
594                 end\r
595                 --out_put( "server.lua: attempting to start tls on " .. tostring( socket ) )\r
596                 local oldsocket, err = socket\r
597                 socket, err = ssl_wrap( socket, sslctx )    -- wrap socket\r
598                 --out_put( "server.lua: sslwrapped socket is " .. tostring( socket ) )\r
599                 if err then\r
600                     out_put( "server.lua: error while starting tls on client: ", tostring(err) )\r
601                     return nil, err    -- fatal error\r
602                 end\r
603 \r
604                 socket:settimeout( 0 )\r
605 \r
606                 -- add the new socket to our system\r
607 \r
608                 send = socket.send\r
609                 receive = socket.receive\r
610                 shutdown = id\r
611 \r
612                 _socketlist[ socket ] = handler\r
613                 _readlistlen = _readlistlen + 1\r
614                 _readlist[ _readlistlen ] = socket\r
615                 _readlist[ socket ] = _readlistlen\r
616 \r
617                 -- remove traces of the old socket\r
618 \r
619                 _readlistlen = removesocket( _readlist, oldsocket, _readlistlen )\r
620                 _sendlistlen = removesocket( _sendlist, oldsocket, _sendlistlen )\r
621                 _socketlist[ oldsocket ] = nil\r
622 \r
623                 handler.starttls = nil\r
624                 needtls = nil\r
625                 \r
626                 -- Secure now\r
627                 ssl = true\r
628 \r
629                 handler.readbuffer = handshake\r
630                 handler.sendbuffer = handshake\r
631                 handshake( socket )    -- do handshake\r
632             end\r
633             handler.readbuffer = _readbuffer\r
634             handler.sendbuffer = _sendbuffer\r
635         end\r
636     else    -- normal connection\r
637         ssl = false\r
638         handler.readbuffer = _readbuffer\r
639         handler.sendbuffer = _sendbuffer\r
640     end\r
641 \r
642     send = socket.send\r
643     receive = socket.receive\r
644     shutdown = ( ssl and id ) or socket.shutdown\r
645 \r
646     _socketlist[ socket ] = handler\r
647     _readlistlen = _readlistlen + 1\r
648     _readlist[ _readlistlen ] = socket\r
649     _readlist[ socket ] = _readlistlen\r
650 \r
651     return handler, socket\r
652 end\r
653 \r
654 id = function( )\r
655 end\r
656 \r
657 idfalse = function( )\r
658     return false\r
659 end\r
660 \r
661 removesocket = function( list, socket, len )    -- this function removes sockets from a list ( copied from copas )\r
662     local pos = list[ socket ]\r
663     if pos then\r
664         list[ socket ] = nil\r
665         local last = list[ len ]\r
666         list[ len ] = nil\r
667         if last ~= socket then\r
668             list[ last ] = pos\r
669             list[ pos ] = last\r
670         end\r
671         return len - 1\r
672     end\r
673     return len\r
674 end\r
675 \r
676 closesocket = function( socket )\r
677     _sendlistlen = removesocket( _sendlist, socket, _sendlistlen )\r
678     _readlistlen = removesocket( _readlist, socket, _readlistlen )\r
679     _socketlist[ socket ] = nil\r
680     socket:close( )\r
681     --mem_free( )\r
682 end\r
683 \r
684 ----------------------------------// PUBLIC //--\r
685 \r
686 addserver = function( listeners, port, addr, pattern, sslctx, maxconnections, startssl )    -- this function provides a way for other scripts to reg a server\r
687     local err\r
688     --out_put("server.lua: autossl on ", port, " is ", startssl)\r
689     if type( listeners ) ~= "table" then\r
690         err = "invalid listener table"\r
691     end\r
692     if not type( port ) == "number" or not ( port >= 0 and port <= 65535 ) then\r
693         err = "invalid port"\r
694     elseif _server[ port ] then\r
695         err =  "listeners on port '" .. port .. "' already exist"\r
696     elseif sslctx and not luasec then\r
697         err = "luasec not found"\r
698     end\r
699     if err then\r
700         out_error( "server.lua, port ", port, ": ", err )\r
701         return nil, err\r
702     end\r
703     addr = addr or "*"\r
704     local server, err = socket_bind( addr, port )\r
705     if err then\r
706         out_error( "server.lua, port ", port, ": ", err )\r
707         return nil, err\r
708     end\r
709     local handler, err = wrapserver( listeners, server, addr, port, pattern, sslctx, maxconnections, startssl )    -- wrap new server socket\r
710     if not handler then\r
711         server:close( )\r
712         return nil, err\r
713     end\r
714     server:settimeout( 0 )\r
715     _readlistlen = _readlistlen + 1\r
716     _readlist[ _readlistlen ] = server\r
717     _server[ port ] = handler\r
718     _socketlist[ server ] = handler\r
719     out_put( "server.lua: new server listener on '", addr, ":", port, "'" )\r
720     return handler\r
721 end\r
722 \r
723 getserver = function ( port )\r
724         return _server[ port ];\r
725 end\r
726 \r
727 removeserver = function( port )\r
728     local handler = _server[ port ]\r
729     if not handler then\r
730         return nil, "no server found on port '" .. tostring( port ) .. "'"\r
731     end\r
732     handler.close( )\r
733     _server[ port ] = nil\r
734     return true\r
735 end\r
736 \r
737 closeall = function( )\r
738     for _, handler in pairs( _socketlist ) do\r
739         handler.close( )\r
740         _socketlist[ _ ] = nil\r
741     end\r
742     _readlistlen = 0\r
743     _sendlistlen = 0\r
744     _timerlistlen = 0\r
745     _server = { }\r
746     _readlist = { }\r
747     _sendlist = { }\r
748     _timerlist = { }\r
749     _socketlist = { }\r
750     --mem_free( )\r
751 end\r
752 \r
753 getsettings = function( )\r
754     return  _selecttimeout, _sleeptime, _maxsendlen, _maxreadlen, _checkinterval, _sendtimeout, _readtimeout, _cleanqueue, _maxclientsperserver\r
755 end\r
756 \r
757 changesettings = function( new )\r
758     if type( new ) ~= "table" then\r
759         return nil, "invalid settings table"\r
760     end\r
761     _selecttimeout = tonumber( new.timeout ) or _selecttimeout\r
762     _sleeptime = tonumber( new.sleeptime ) or _sleeptime\r
763     _maxsendlen = tonumber( new.maxsendlen ) or _maxsendlen\r
764     _maxreadlen = tonumber( new.maxreadlen ) or _maxreadlen\r
765     _checkinterval = tonumber( new.checkinterval ) or _checkinterval\r
766     _sendtimeout = tonumber( new.sendtimeout ) or _sendtimeout\r
767     _readtimeout = tonumber( new.readtimeout ) or _readtimeout\r
768     _cleanqueue = new.cleanqueue\r
769     _maxclientsperserver = new._maxclientsperserver or _maxclientsperserver\r
770     return true\r
771 end\r
772 \r
773 addtimer = function( listener )\r
774     if type( listener ) ~= "function" then\r
775         return nil, "invalid listener function"\r
776     end\r
777     _timerlistlen = _timerlistlen + 1\r
778     _timerlist[ _timerlistlen ] = listener\r
779     return true\r
780 end\r
781 \r
782 stats = function( )\r
783     return _readtraffic, _sendtraffic, _readlistlen, _sendlistlen, _timerlistlen\r
784 end\r
785 \r
786 local dontstop = true; -- thinking about tomorrow, ...\r
787 \r
788 setquitting = function (quit)\r
789         dontstop = not quit;\r
790         return;\r
791 end\r
792 \r
793 loop = function( )    -- this is the main loop of the program\r
794     while dontstop do\r
795         local read, write, err = socket_select( _readlist, _sendlist, _selecttimeout )\r
796         for i, socket in ipairs( write ) do    -- send data waiting in writequeues\r
797             local handler = _socketlist[ socket ]\r
798             if handler then\r
799                 handler.sendbuffer( )\r
800             else\r
801                 closesocket( socket )\r
802                 out_put "server.lua: found no handler and closed socket (writelist)"    -- this should not happen\r
803             end\r
804         end\r
805         for i, socket in ipairs( read ) do    -- receive data\r
806             local handler = _socketlist[ socket ]\r
807             if handler then\r
808                 handler.readbuffer( )\r
809             else\r
810                 closesocket( socket )\r
811                 out_put "server.lua: found no handler and closed socket (readlist)"    -- this can happen\r
812             end\r
813         end\r
814         for handler, err in pairs( _closelist ) do\r
815             handler.disconnect( )( handler, err )\r
816             handler.close( true )    -- forced disconnect\r
817         end\r
818         clean( _closelist )\r
819         _currenttime = os_time( )\r
820         if os_difftime( _currenttime - _timer ) >= 1 then\r
821             for i = 1, _timerlistlen do\r
822                 _timerlist[ i ]( )    -- fire timers\r
823             end\r
824             _timer = _currenttime\r
825         end\r
826         socket_sleep( _sleeptime )    -- wait some time\r
827         --collectgarbage( )\r
828     end\r
829     return "quitting"\r
830 end\r
831 \r
832 --// EXPERIMENTAL //--\r
833 \r
834 local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx, startssl )\r
835     local handler = wrapconnection( nil, listeners, socket, ip, serverport, "clientport", pattern, sslctx, startssl )\r
836     _socketlist[ socket ] = handler\r
837     _sendlistlen = _sendlistlen + 1\r
838     _sendlist[ _sendlistlen ] = socket\r
839     _sendlist[ socket ] = _sendlistlen\r
840     return handler, socket\r
841 end\r
842 \r
843 local addclient = function( address, port, listeners, pattern, sslctx, startssl )\r
844     local client, err = luasocket.tcp( )\r
845     if err then\r
846         return nil, err\r
847     end\r
848     client:settimeout( 0 )\r
849     _, err = client:connect( address, port )\r
850     if err then    -- try again\r
851         local handler = wrapclient( client, address, port, listeners )\r
852     else\r
853         wrapconnection( nil, listeners, client, address, port, "clientport", pattern, sslctx, startssl )\r
854     end\r
855 end\r
856 \r
857 --// EXPERIMENTAL //--\r
858 \r
859 ----------------------------------// BEGIN //--\r
860 \r
861 use "setmetatable" ( _socketlist, { __mode = "k" } )\r
862 use "setmetatable" ( _readtimes, { __mode = "k" } )\r
863 use "setmetatable" ( _writetimes, { __mode = "k" } )\r
864 \r
865 _timer = os_time( )\r
866 _starttime = os_time( )\r
867 \r
868 addtimer( function( )\r
869         local difftime = os_difftime( _currenttime - _starttime )\r
870         if difftime > _checkinterval then\r
871             _starttime = _currenttime\r
872             for handler, timestamp in pairs( _writetimes ) do\r
873                 if os_difftime( _currenttime - timestamp ) > _sendtimeout then\r
874                     --_writetimes[ handler ] = nil\r
875                     handler.disconnect( )( handler, "send timeout" )\r
876                     handler.close( true )    -- forced disconnect\r
877                 end\r
878             end\r
879             for handler, timestamp in pairs( _readtimes ) do\r
880                 if os_difftime( _currenttime - timestamp ) > _readtimeout then\r
881                     --_readtimes[ handler ] = nil\r
882                     handler.disconnect( )( handler, "read timeout" )\r
883                     handler.close( )    -- forced disconnect?\r
884                 end\r
885             end\r
886         end\r
887     end\r
888 )\r
889 \r
890 ----------------------------------// PUBLIC INTERFACE //--\r
891 \r
892 return {\r
893 \r
894     addclient = addclient,\r
895     wrapclient = wrapclient,\r
896     \r
897     loop = loop,\r
898     stats = stats,\r
899     closeall = closeall,\r
900     addtimer = addtimer,\r
901     addserver = addserver,\r
902     getserver = getserver,\r
903     getsettings = getsettings,\r
904     setquitting = setquitting,\r
905     removeserver = removeserver,\r
906     changesettings = changesettings,\r
907 }\r