12b0aa903e0ab283238897f6449a2b4d914bc61b
[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.remove = function( )\r
234         connections = connections - 1\r
235     end\r
236     handler.close = function( )\r
237         for _, handler in pairs( _socketlist ) do\r
238             if handler.serverport == serverport then\r
239                 handler.disconnect( handler, "server closed" )\r
240                 handler.close( true )\r
241             end\r
242         end\r
243         socket:close( )\r
244         _sendlistlen = removesocket( _sendlist, socket, _sendlistlen )\r
245         _readlistlen = removesocket( _readlist, socket, _readlistlen )\r
246         _socketlist[ socket ] = nil\r
247         handler = nil\r
248         socket = nil\r
249         mem_free( )\r
250         out_put "server.lua: closed server handler and removed sockets from list"\r
251     end\r
252     handler.ip = function( )\r
253         return ip\r
254     end\r
255     handler.serverport = function( )\r
256         return serverport\r
257     end\r
258     handler.socket = function( )\r
259         return socket\r
260     end\r
261     handler.readbuffer = function( )\r
262         if connections > maxconnections then\r
263             out_put( "server.lua: refused new client connection: server full" )\r
264             return false\r
265         end\r
266         local client, err = accept( socket )    -- try to accept\r
267         if client then\r
268             local ip, clientport = client:getpeername( )\r
269             client:settimeout( 0 )\r
270             local handler, client, err = wrapconnection( handler, listeners, client, ip, serverport, clientport, pattern, sslctx, startssl )    -- wrap new client socket\r
271             if err then    -- error while wrapping ssl socket\r
272                 return false\r
273             end\r
274             connections = connections + 1\r
275             out_put( "server.lua: accepted new client connection from ", tostring(ip), ":", tostring(clientport), " to ", tostring(serverport))\r
276             return dispatch( handler )\r
277         elseif err then    -- maybe timeout or something else\r
278             out_put( "server.lua: error with new client connection: ", tostring(err) )\r
279             return false\r
280         end\r
281     end\r
282     return handler\r
283 end\r
284 \r
285 wrapconnection = function( server, listeners, socket, ip, serverport, clientport, pattern, sslctx, startssl )    -- this function wraps a client to a handler object\r
286 \r
287     socket:settimeout( 0 )\r
288 \r
289     --// local import of socket methods //--\r
290 \r
291     local send\r
292     local receive\r
293     local shutdown\r
294 \r
295     --// private closures of the object //--\r
296 \r
297     local ssl\r
298 \r
299     local dispatch = listeners.incoming or listeners.listener\r
300     local disconnect = listeners.disconnect\r
301 \r
302     local bufferqueue = { }    -- buffer array\r
303     local bufferqueuelen = 0    -- end of buffer array\r
304 \r
305     local toclose\r
306     local fatalerror\r
307     local needtls\r
308 \r
309     local bufferlen = 0\r
310 \r
311     local noread = false\r
312     local nosend = false\r
313 \r
314     local sendtraffic, readtraffic = 0, 0\r
315 \r
316     local maxsendlen = _maxsendlen\r
317     local maxreadlen = _maxreadlen\r
318 \r
319     --// public methods of the object //--\r
320 \r
321     local handler = bufferqueue    -- saves a table ^_^\r
322 \r
323     handler.dispatch = function( )\r
324         return dispatch\r
325     end\r
326     handler.disconnect = function( )\r
327         return disconnect\r
328     end\r
329     handler.setlistener = function( listeners )\r
330         dispatch = listeners.incoming\r
331         disconnect = listeners.disconnect\r
332     end\r
333     handler.getstats = function( )\r
334         return readtraffic, sendtraffic\r
335     end\r
336     handler.ssl = function( )\r
337         return ssl\r
338     end\r
339     handler.send = function( _, data, i, j )\r
340         return send( socket, data, i, j )\r
341     end\r
342     handler.receive = function( pattern, prefix )\r
343         return receive( socket, pattern, prefix )\r
344     end\r
345     handler.shutdown = function( pattern )\r
346         return shutdown( socket, pattern )\r
347     end\r
348     handler.close = function( forced )\r
349         if not handler then return true; end\r
350         _readlistlen = removesocket( _readlist, socket, _readlistlen )\r
351         _readtimes[ handler ] = nil\r
352         if bufferqueuelen ~= 0 then\r
353             if not ( forced or fatalerror ) then\r
354                 handler.sendbuffer( )\r
355                 if bufferqueuelen ~= 0 then   -- try again...\r
356                     if handler then\r
357                         handler.write = nil    -- ... but no further writing allowed\r
358                     end\r
359                     toclose = true\r
360                     return false\r
361                 end\r
362             else\r
363                 send( socket, table_concat( bufferqueue, "", 1, bufferqueuelen ), 1, bufferlen )    -- forced send\r
364             end\r
365         end\r
366         _ = shutdown and shutdown( socket )\r
367         socket:close( )\r
368         _sendlistlen = removesocket( _sendlist, socket, _sendlistlen )\r
369         _socketlist[ socket ] = nil\r
370         if handler then\r
371             _writetimes[ handler ] = nil\r
372             _closelist[ handler ] = nil\r
373             handler = nil\r
374         end\r
375         socket = nil\r
376         mem_free( )\r
377         if server then\r
378                 server.remove( )\r
379         end\r
380         out_put "server.lua: closed client handler and removed socket from list"\r
381         return true\r
382     end\r
383     handler.ip = function( )\r
384         return ip\r
385     end\r
386     handler.serverport = function( )\r
387         return serverport\r
388     end\r
389     handler.clientport = function( )\r
390         return clientport\r
391     end\r
392     local write = function( data )\r
393         bufferlen = bufferlen + string_len( data )\r
394         if bufferlen > maxsendlen then\r
395             _closelist[ handler ] = "send buffer exceeded"   -- cannot close the client at the moment, have to wait to the end of the cycle\r
396             handler.write = idfalse    -- dont write anymore\r
397             return false\r
398         elseif socket and not _sendlist[ socket ] then\r
399             _sendlistlen = _sendlistlen + 1\r
400             _sendlist[ _sendlistlen ] = socket\r
401             _sendlist[ socket ] = _sendlistlen\r
402         end\r
403         bufferqueuelen = bufferqueuelen + 1\r
404         bufferqueue[ bufferqueuelen ] = data\r
405         if handler then\r
406                 _writetimes[ handler ] = _writetimes[ handler ] or _currenttime\r
407         end\r
408         return true\r
409     end\r
410     handler.write = write\r
411     handler.bufferqueue = function( )\r
412         return bufferqueue\r
413     end\r
414     handler.socket = function( )\r
415         return socket\r
416     end\r
417     handler.pattern = function( new )\r
418         pattern = new or pattern\r
419         return pattern\r
420     end\r
421     handler.setsend = function ( newsend )\r
422         send = newsend or send\r
423         return send\r
424     end\r
425     handler.bufferlen = function( readlen, sendlen )\r
426         maxsendlen = sendlen or maxsendlen\r
427         maxreadlen = readlen or maxreadlen\r
428         return maxreadlen, maxsendlen\r
429     end\r
430     handler.lock = function( switch )\r
431         if switch == true then\r
432             handler.write = idfalse\r
433             local tmp = _sendlistlen\r
434             _sendlistlen = removesocket( _sendlist, socket, _sendlistlen )\r
435             _writetimes[ handler ] = nil\r
436             if _sendlistlen ~= tmp then\r
437                 nosend = true\r
438             end\r
439             tmp = _readlistlen\r
440             _readlistlen = removesocket( _readlist, socket, _readlistlen )\r
441             _readtimes[ handler ] = nil\r
442             if _readlistlen ~= tmp then\r
443                 noread = true\r
444             end\r
445         elseif switch == false then\r
446             handler.write = write\r
447             if noread then\r
448                 noread = false\r
449                 _readlistlen = _readlistlen + 1\r
450                 _readlist[ socket ] = _readlistlen\r
451                 _readlist[ _readlistlen ] = socket\r
452                 _readtimes[ handler ] = _currenttime\r
453             end\r
454             if nosend then\r
455                 nosend = false\r
456                 write( "" )\r
457             end\r
458         end\r
459         return noread, nosend\r
460     end\r
461     local _readbuffer = function( )    -- this function reads data\r
462         local buffer, err, part = receive( socket, pattern )    -- receive buffer with "pattern"\r
463         if not err or ( err == "timeout" or err == "wantread" ) then    -- received something\r
464             local buffer = buffer or part or ""\r
465             local len = string_len( buffer )\r
466             if len > maxreadlen then\r
467                 disconnect( handler, "receive buffer exceeded" )\r
468                 handler.close( true )\r
469                 return false\r
470             end\r
471             local count = len * STAT_UNIT\r
472             readtraffic = readtraffic + count\r
473             _readtraffic = _readtraffic + count\r
474             _readtimes[ handler ] = _currenttime\r
475             --out_put( "server.lua: read data '", buffer, "', error: ", err )\r
476             return dispatch( handler, buffer, err )\r
477         else    -- connections was closed or fatal error\r
478             out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " error: ", tostring(err) )\r
479             fatalerror = true\r
480             disconnect( handler, err )\r
481             _ = handler and handler.close( )\r
482             return false\r
483         end\r
484     end\r
485     local _sendbuffer = function( )    -- this function sends data\r
486         local succ, err, byte, buffer, count;\r
487         local count;\r
488         if socket then\r
489             buffer = table_concat( bufferqueue, "", 1, bufferqueuelen )\r
490             succ, err, byte = send( socket, buffer, 1, bufferlen )\r
491             count = ( succ or byte or 0 ) * STAT_UNIT\r
492             sendtraffic = sendtraffic + count\r
493             _sendtraffic = _sendtraffic + count\r
494             _ = _cleanqueue and clean( bufferqueue )\r
495             --out_put( "server.lua: sended '", buffer, "', bytes: ", tostring(succ), ", error: ", tostring(err), ", part: ", tostring(byte), ", to: ", tostring(ip), ":", tostring(clientport) )\r
496         else\r
497             succ, err, count = false, "closed", 0;\r
498         end\r
499         if succ then    -- sending succesful\r
500             bufferqueuelen = 0\r
501             bufferlen = 0\r
502             _sendlistlen = removesocket( _sendlist, socket, _sendlistlen )    -- delete socket from writelist\r
503             _ = needtls and handler.starttls(true)\r
504             _writetimes[ handler ] = nil\r
505             _ = toclose and handler.close( )\r
506             return true\r
507         elseif byte and ( err == "timeout" or err == "wantwrite" ) then    -- want write\r
508             buffer = string_sub( buffer, byte + 1, bufferlen )    -- new buffer\r
509             bufferqueue[ 1 ] = buffer    -- insert new buffer in queue\r
510             bufferqueuelen = 1\r
511             bufferlen = bufferlen - byte\r
512             _writetimes[ handler ] = _currenttime\r
513             return true\r
514         else    -- connection was closed during sending or fatal error\r
515             out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " error: ", tostring(err) )\r
516             fatalerror = true\r
517             disconnect( handler, err )\r
518             _ = handler and handler.close( )\r
519             return false\r
520         end\r
521     end\r
522 \r
523     if sslctx then    -- ssl?\r
524         ssl = true\r
525         local wrote\r
526         local read\r
527         local handshake = coroutine_wrap( function( client )    -- create handshake coroutine\r
528                 local err\r
529                 for i = 1, 10 do    -- 10 handshake attemps\r
530                     _sendlistlen = ( wrote and removesocket( _sendlist, socket, _sendlistlen ) ) or _sendlistlen\r
531                     _readlistlen = ( read and removesocket( _readlist, socket, _readlistlen ) ) or _readlistlen\r
532                     read, wrote = nil, nil\r
533                     _, err = client:dohandshake( )\r
534                     if not err then\r
535                         out_put( "server.lua: ssl handshake done" )\r
536                         handler.readbuffer = _readbuffer    -- when handshake is done, replace the handshake function with regular functions\r
537                         handler.sendbuffer = _sendbuffer\r
538                         -- return dispatch( handler )\r
539                         return true\r
540                     else\r
541                         out_put( "server.lua: error during ssl handshake: ", tostring(err) )\r
542                         if err == "wantwrite" and not wrote then\r
543                             _sendlistlen = _sendlistlen + 1\r
544                             _sendlist[ _sendlistlen ] = client\r
545                             wrote = true\r
546                         elseif err == "wantread" and not read then\r
547                                 _readlistlen = _readlistlen + 1\r
548                                 _readlist [ _readlistlen ] = client\r
549                                 read = true\r
550                         else\r
551                                 break;\r
552                         end\r
553                         --coroutine_yield( handler, nil, err )    -- handshake not finished\r
554                         coroutine_yield( )\r
555                     end\r
556                 end\r
557                 disconnect( handler, "ssl handshake failed" )\r
558                 _ = handler and handler.close( true )    -- forced disconnect\r
559                 return false    -- handshake failed\r
560             end\r
561         )\r
562         if startssl then    -- ssl now?\r
563             --out_put("server.lua: ", "starting ssl handshake")\r
564             local err\r
565             socket, err = ssl_wrap( socket, sslctx )    -- wrap socket\r
566             if err then\r
567                 out_put( "server.lua: ssl error: ", tostring(err) )\r
568                 mem_free( )\r
569                 return nil, nil, err    -- fatal error\r
570             end\r
571             socket:settimeout( 0 )\r
572             handler.readbuffer = handshake\r
573             handler.sendbuffer = handshake\r
574             handshake( socket ) -- do handshake\r
575             if not socket then\r
576                 return nil, nil, "ssl handshake failed";\r
577             end\r
578         else\r
579             -- We're not automatically doing SSL, so we're not secure (yet)\r
580             ssl = false\r
581             handler.starttls = function( now )\r
582                 if not now then\r
583                     --out_put "server.lua: we need to do tls, but delaying until later"\r
584                     needtls = true\r
585                     return\r
586                 end\r
587                 --out_put( "server.lua: attempting to start tls on " .. tostring( socket ) )\r
588                 local oldsocket, err = socket\r
589                 socket, err = ssl_wrap( socket, sslctx )    -- wrap socket\r
590                 --out_put( "server.lua: sslwrapped socket is " .. tostring( socket ) )\r
591                 if err then\r
592                     out_put( "server.lua: error while starting tls on client: ", tostring(err) )\r
593                     return nil, err    -- fatal error\r
594                 end\r
595 \r
596                 socket:settimeout( 0 )\r
597 \r
598                 -- add the new socket to our system\r
599 \r
600                 send = socket.send\r
601                 receive = socket.receive\r
602                 shutdown = id\r
603 \r
604                 _socketlist[ socket ] = handler\r
605                 _readlistlen = _readlistlen + 1\r
606                 _readlist[ _readlistlen ] = socket\r
607                 _readlist[ socket ] = _readlistlen\r
608 \r
609                 -- remove traces of the old socket\r
610 \r
611                 _readlistlen = removesocket( _readlist, oldsocket, _readlistlen )\r
612                 _sendlistlen = removesocket( _sendlist, oldsocket, _sendlistlen )\r
613                 _socketlist[ oldsocket ] = nil\r
614 \r
615                 handler.starttls = nil\r
616                 needtls = nil\r
617                 \r
618                 -- Secure now\r
619                 ssl = true\r
620 \r
621                 handler.readbuffer = handshake\r
622                 handler.sendbuffer = handshake\r
623                 handshake( socket )    -- do handshake\r
624             end\r
625             handler.readbuffer = _readbuffer\r
626             handler.sendbuffer = _sendbuffer\r
627         end\r
628     else    -- normal connection\r
629         ssl = false\r
630         handler.readbuffer = _readbuffer\r
631         handler.sendbuffer = _sendbuffer\r
632     end\r
633 \r
634     send = socket.send\r
635     receive = socket.receive\r
636     shutdown = ( ssl and id ) or socket.shutdown\r
637 \r
638     _socketlist[ socket ] = handler\r
639     _readlistlen = _readlistlen + 1\r
640     _readlist[ _readlistlen ] = socket\r
641     _readlist[ socket ] = _readlistlen\r
642 \r
643     return handler, socket\r
644 end\r
645 \r
646 id = function( )\r
647 end\r
648 \r
649 idfalse = function( )\r
650     return false\r
651 end\r
652 \r
653 removesocket = function( list, socket, len )    -- this function removes sockets from a list ( copied from copas )\r
654     local pos = list[ socket ]\r
655     if pos then\r
656         list[ socket ] = nil\r
657         local last = list[ len ]\r
658         list[ len ] = nil\r
659         if last ~= socket then\r
660             list[ last ] = pos\r
661             list[ pos ] = last\r
662         end\r
663         return len - 1\r
664     end\r
665     return len\r
666 end\r
667 \r
668 closesocket = function( socket )\r
669     _sendlistlen = removesocket( _sendlist, socket, _sendlistlen )\r
670     _readlistlen = removesocket( _readlist, socket, _readlistlen )\r
671     _socketlist[ socket ] = nil\r
672     socket:close( )\r
673     mem_free( )\r
674 end\r
675 \r
676 ----------------------------------// PUBLIC //--\r
677 \r
678 addserver = function( listeners, port, addr, pattern, sslctx, maxconnections, startssl )    -- this function provides a way for other scripts to reg a server\r
679     local err\r
680     --out_put("server.lua: autossl on ", port, " is ", startssl)\r
681     if type( listeners ) ~= "table" then\r
682         err = "invalid listener table"\r
683     end\r
684     if not type( port ) == "number" or not ( port >= 0 and port <= 65535 ) then\r
685         err = "invalid port"\r
686     elseif _server[ port ] then\r
687         err =  "listeners on port '" .. port .. "' already exist"\r
688     elseif sslctx and not luasec then\r
689         err = "luasec not found"\r
690     end\r
691     if err then\r
692         out_error( "server.lua, port ", port, ": ", err )\r
693         return nil, err\r
694     end\r
695     addr = addr or "*"\r
696     local server, err = socket_bind( addr, port )\r
697     if err then\r
698         out_error( "server.lua, port ", port, ": ", err )\r
699         return nil, err\r
700     end\r
701     local handler, err = wrapserver( listeners, server, addr, port, pattern, sslctx, maxconnections, startssl )    -- wrap new server socket\r
702     if not handler then\r
703         server:close( )\r
704         return nil, err\r
705     end\r
706     server:settimeout( 0 )\r
707     _readlistlen = _readlistlen + 1\r
708     _readlist[ _readlistlen ] = server\r
709     _server[ port ] = handler\r
710     _socketlist[ server ] = handler\r
711     out_put( "server.lua: new server listener on '", addr, ":", port, "'" )\r
712     return handler\r
713 end\r
714 \r
715 getserver = function ( port )\r
716         return _server[ port ];\r
717 end\r
718 \r
719 removeserver = function( port )\r
720     local handler = _server[ port ]\r
721     if not handler then\r
722         return nil, "no server found on port '" .. tostring( port ) "'"\r
723     end\r
724     handler.close( )\r
725     _server[ port ] = nil\r
726     return true\r
727 end\r
728 \r
729 closeall = function( )\r
730     for _, handler in pairs( _socketlist ) do\r
731         handler.close( )\r
732         _socketlist[ _ ] = nil\r
733     end\r
734     _readlistlen = 0\r
735     _sendlistlen = 0\r
736     _timerlistlen = 0\r
737     _server = { }\r
738     _readlist = { }\r
739     _sendlist = { }\r
740     _timerlist = { }\r
741     _socketlist = { }\r
742     mem_free( )\r
743 end\r
744 \r
745 getsettings = function( )\r
746     return  _selecttimeout, _sleeptime, _maxsendlen, _maxreadlen, _checkinterval, _sendtimeout, _readtimeout, _cleanqueue, _maxclientsperserver\r
747 end\r
748 \r
749 changesettings = function( new )\r
750     if type( new ) ~= "table" then\r
751         return nil, "invalid settings table"\r
752     end\r
753     _selecttimeout = tonumber( new.timeout ) or _selecttimeout\r
754     _sleeptime = tonumber( new.sleeptime ) or _sleeptime\r
755     _maxsendlen = tonumber( new.maxsendlen ) or _maxsendlen\r
756     _maxreadlen = tonumber( new.maxreadlen ) or _maxreadlen\r
757     _checkinterval = tonumber( new.checkinterval ) or _checkinterval\r
758     _sendtimeout = tonumber( new.sendtimeout ) or _sendtimeout\r
759     _readtimeout = tonumber( new.readtimeout ) or _readtimeout\r
760     _cleanqueue = new.cleanqueue\r
761     _maxclientsperserver = new._maxclientsperserver or _maxclientsperserver\r
762     return true\r
763 end\r
764 \r
765 addtimer = function( listener )\r
766     if type( listener ) ~= "function" then\r
767         return nil, "invalid listener function"\r
768     end\r
769     _timerlistlen = _timerlistlen + 1\r
770     _timerlist[ _timerlistlen ] = listener\r
771     return true\r
772 end\r
773 \r
774 stats = function( )\r
775     return _readtraffic, _sendtraffic, _readlistlen, _sendlistlen, _timerlistlen\r
776 end\r
777 \r
778 local dontstop = true; -- thinking about tomorrow, ...\r
779 \r
780 setquitting = function (quit)\r
781         dontstop = not quit;\r
782         return;\r
783 end\r
784 \r
785 loop = function( )    -- this is the main loop of the program\r
786     while dontstop do\r
787         local read, write, err = socket_select( _readlist, _sendlist, _selecttimeout )\r
788         for i, socket in ipairs( write ) do    -- send data waiting in writequeues\r
789             local handler = _socketlist[ socket ]\r
790             if handler then\r
791                 handler.sendbuffer( )\r
792             else\r
793                 closesocket( socket )\r
794                 out_put "server.lua: found no handler and closed socket (writelist)"    -- this should not happen\r
795             end\r
796         end\r
797         for i, socket in ipairs( read ) do    -- receive data\r
798             local handler = _socketlist[ socket ]\r
799             if handler then\r
800                 handler.readbuffer( )\r
801             else\r
802                 closesocket( socket )\r
803                 out_put "server.lua: found no handler and closed socket (readlist)"    -- this can happen\r
804             end\r
805         end\r
806         for handler, err in pairs( _closelist ) do\r
807             handler.disconnect( )( handler, err )\r
808             handler.close( true )    -- forced disconnect\r
809         end\r
810         clean( _closelist )\r
811         _currenttime = os_time( )\r
812         if os_difftime( _currenttime - _timer ) >= 1 then\r
813             for i = 1, _timerlistlen do\r
814                 _timerlist[ i ]( )    -- fire timers\r
815             end\r
816             _timer = _currenttime\r
817         end\r
818         socket_sleep( _sleeptime )    -- wait some time\r
819         --collectgarbage( )\r
820     end\r
821     return "quitting"\r
822 end\r
823 \r
824 --// EXPERIMENTAL //--\r
825 \r
826 local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx, startssl )\r
827     local handler = wrapconnection( nil, listeners, socket, ip, serverport, "clientport", pattern, sslctx, startssl )\r
828     _socketlist[ socket ] = handler\r
829     _sendlistlen = _sendlistlen + 1\r
830     _sendlist[ _sendlistlen ] = socket\r
831     _sendlist[ socket ] = _sendlistlen\r
832     return handler, socket\r
833 end\r
834 \r
835 local addclient = function( address, port, listeners, pattern, sslctx, startssl )\r
836     local client, err = luasocket.tcp( )\r
837     if err then\r
838         return nil, err\r
839     end\r
840     client:settimeout( 0 )\r
841     _, err = client:connect( address, port )\r
842     if err then    -- try again\r
843         local handler = wrapclient( client, address, port, listeners )\r
844     else\r
845         wrapconnection( nil, listeners, client, address, port, "clientport", pattern, sslctx, startssl )\r
846     end\r
847 end\r
848 \r
849 --// EXPERIMENTAL //--\r
850 \r
851 ----------------------------------// BEGIN //--\r
852 \r
853 use "setmetatable" ( _socketlist, { __mode = "k" } )\r
854 use "setmetatable" ( _readtimes, { __mode = "k" } )\r
855 use "setmetatable" ( _writetimes, { __mode = "k" } )\r
856 \r
857 _timer = os_time( )\r
858 _starttime = os_time( )\r
859 \r
860 addtimer( function( )\r
861         local difftime = os_difftime( _currenttime - _starttime )\r
862         if difftime > _checkinterval then\r
863             _starttime = _currenttime\r
864             for handler, timestamp in pairs( _writetimes ) do\r
865                 if os_difftime( _currenttime - timestamp ) > _sendtimeout then\r
866                     --_writetimes[ handler ] = nil\r
867                     handler.disconnect( )( handler, "send timeout" )\r
868                     handler.close( true )    -- forced disconnect\r
869                 end\r
870             end\r
871             for handler, timestamp in pairs( _readtimes ) do\r
872                 if os_difftime( _currenttime - timestamp ) > _readtimeout then\r
873                     --_readtimes[ handler ] = nil\r
874                     handler.disconnect( )( handler, "read timeout" )\r
875                     handler.close( )    -- forced disconnect?\r
876                 end\r
877             end\r
878         end\r
879     end\r
880 )\r
881 \r
882 ----------------------------------// PUBLIC INTERFACE //--\r
883 \r
884 return {\r
885 \r
886     addclient = addclient,\r
887     wrapclient = wrapclient,\r
888     \r
889     loop = loop,\r
890     stats = stats,\r
891     closeall = closeall,\r
892     addtimer = addtimer,\r
893     addserver = addserver,\r
894     getserver = getserver,\r
895     getsettings = getsettings,\r
896     setquitting = setquitting,\r
897     removeserver = removeserver,\r
898     changesettings = changesettings,\r
899 }\r