Comparison

net/server_select.lua @ 2093:abfa1cc3a42f

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