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