net/server.lua

changeset 12
90f22275f7ae
parent 9
cb210ac67af8
child 64
bcd0a3975580
equal deleted inserted replaced
11:67460500abdd 12:90f22275f7ae
1 --[[
2
3 server.lua by blastbeat of the luadch project
4
5 re-used here under the MIT/X Consortium License
6
7 - this script contains the server loop of the program
8 - other scripts can reg a server here
9
10 ]]--
11
12 ----------------------------------// DECLARATION //--
13
14 --// constants //--
15
16 local STAT_UNIT = 1 / ( 1024 * 1024 ) -- mb
17
18 --// lua functions //--
19
20 local function use( what ) return _G[ what ] end
21
22 local type = use "type"
23 local pairs = use "pairs"
24 local ipairs = use "ipairs"
25 local tostring = use "tostring"
26 local collectgarbage = use "collectgarbage"
27
28 --// lua libs //--
29
30 local table = use "table"
31 local coroutine = use "coroutine"
32
33 --// lua lib methods //--
34
35 local table_concat = table.concat
36 local table_remove = table.remove
37 local string_sub = use'string'.sub
38 local coroutine_wrap = coroutine.wrap
39 local coroutine_yield = coroutine.yield
40 local print = print;
41 local out_put = function () end --print;
42 local out_error = print;
43
44 --// extern libs //--
45
46 local luasec = require "ssl"
47 local luasocket = require "socket"
48
49 --// extern lib methods //--
50
51 local ssl_wrap = ( luasec and luasec.wrap )
52 local socket_bind = luasocket.bind
53 local socket_select = luasocket.select
54 local ssl_newcontext = ( luasec and luasec.newcontext )
55
56 --// functions //--
57
58 local loop
59 local stats
60 local addtimer
61 local closeall
62 local addserver
63 local firetimer
64 local closesocket
65 local removesocket
66 local wrapserver
67 local wraptcpclient
68 local wrapsslclient
69
70 --// tables //--
71
72 local listener
73 local readlist
74 local writelist
75 local socketlist
76 local timelistener
77
78 --// simple data types //--
79
80 local _
81 local readlen = 0 -- length of readlist
82 local writelen = 0 -- lenght of writelist
83
84 local sendstat= 0
85 local receivestat = 0
86
87 ----------------------------------// DEFINITION //--
88
89 listener = { } -- key = port, value = table
90 readlist = { } -- array with sockets to read from
91 writelist = { } -- arrary with sockets to write to
92 socketlist = { } -- key = socket, value = wrapped socket
93 timelistener = { }
94
95 stats = function( )
96 return receivestat, sendstat
97 end
98
99 wrapserver = function( listener, socket, ip, serverport, mode, sslctx ) -- this function wraps a server
100
101 local dispatch, disconnect = listener.listener, listener.disconnect -- dangerous
102
103 local wrapclient, err
104
105 if sslctx then
106 if not ssl_newcontext then
107 return nil, "luasec not found"
108 -- elseif not cfg_get "use_ssl" then
109 -- return nil, "ssl is deactivated"
110 end
111 if type( sslctx ) ~= "table" then
112 out_error "server.lua: wrong server sslctx"
113 return nil, "wrong server sslctx"
114 end
115 sslctx, err = ssl_newcontext( sslctx )
116 if not sslctx then
117 err = err or "wrong sslctx parameters"
118 out_error( "server.lua: ", err )
119 return nil, err
120 end
121 wrapclient = wrapsslclient
122 else
123 wrapclient = wraptcpclient
124 end
125
126 local accept = socket.accept
127 local close = socket.close
128
129 --// public methods of the object //--
130
131 local handler = { }
132
133 handler.shutdown = function( ) end
134
135 --[[handler.listener = function( data, err )
136 return ondata( handler, data, err )
137 end]]
138 handler.ssl = function( )
139 return sslctx and true or false
140 end
141 handler.close = function( closed )
142 _ = not closed and close( socket )
143 writelen = removesocket( writelist, socket, writelen )
144 readlen = removesocket( readlist, socket, readlen )
145 socketlist[ socket ] = nil
146 handler = nil
147 end
148 handler.ip = function( )
149 return ip
150 end
151 handler.serverport = function( )
152 return serverport
153 end
154 handler.socket = function( )
155 return socket
156 end
157 handler.receivedata = function( )
158 local client, err = accept( socket ) -- try to accept
159 if client then
160 local ip, clientport = client:getpeername( )
161 client:settimeout( 0 )
162 local handler, client, err = wrapclient( listener, client, ip, serverport, clientport, mode, sslctx ) -- wrap new client socket
163 if err then -- error while wrapping ssl socket
164 return false
165 end
166 out_put( "server.lua: accepted new client connection from ", ip, ":", clientport )
167 return dispatch( handler )
168 elseif err then -- maybe timeout or something else
169 out_put( "server.lua: error with new client connection: ", err )
170 return false
171 end
172 end
173 return handler
174 end
175
176 wrapsslclient = function( listener, socket, ip, serverport, clientport, mode, sslctx ) -- this function wraps a ssl cleint
177
178 local dispatch, disconnect = listener.listener, listener.disconnect
179
180 --// transform socket to ssl object //--
181
182 local err
183 socket, err = ssl_wrap( socket, sslctx ) -- wrap socket
184 if err then
185 out_put( "server.lua: ssl error: ", err )
186 return nil, nil, err -- fatal error
187 end
188 socket:settimeout( 0 )
189
190 --// private closures of the object //--
191
192 local writequeue = { } -- buffer for messages to send
193
194 local eol -- end of buffer
195
196 local sstat, rstat = 0, 0
197
198 --// local import of socket methods //--
199
200 local send = socket.send
201 local receive = socket.receive
202 local close = socket.close
203 --local shutdown = socket.shutdown
204
205 --// public methods of the object //--
206
207 local handler = { }
208
209 handler.getstats = function( )
210 return rstat, sstat
211 end
212
213 handler.listener = function( data, err )
214 return listener( handler, data, err )
215 end
216 handler.ssl = function( )
217 return true
218 end
219 handler.send = function( _, data, i, j )
220 return send( socket, data, i, j )
221 end
222 handler.receive = function( pattern, prefix )
223 return receive( socket, pattern, prefix )
224 end
225 handler.shutdown = function( pattern )
226 --return shutdown( socket, pattern )
227 end
228 handler.close = function( closed )
229 close( socket )
230 writelen = ( eol and removesocket( writelist, socket, writelen ) ) or writelen
231 readlen = removesocket( readlist, socket, readlen )
232 socketlist[ socket ] = nil
233 out_put "server.lua: closed handler and removed socket from list"
234 end
235 handler.ip = function( )
236 return ip
237 end
238 handler.serverport = function( )
239 return serverport
240 end
241 handler.clientport = function( )
242 return clientport
243 end
244
245 handler.write = function( data )
246 if not eol then
247 writelen = writelen + 1
248 writelist[ writelen ] = socket
249 eol = 0
250 end
251 eol = eol + 1
252 writequeue[ eol ] = data
253 end
254 handler.writequeue = function( )
255 return writequeue
256 end
257 handler.socket = function( )
258 return socket
259 end
260 handler.mode = function( )
261 return mode
262 end
263 handler._receivedata = function( )
264 local data, err, part = receive( socket, mode ) -- receive data in "mode"
265 if not err or ( err == "timeout" or err == "wantread" ) then -- received something
266 local data = data or part or ""
267 local count = #data * STAT_UNIT
268 rstat = rstat + count
269 receivestat = receivestat + count
270 out_put( "server.lua: read data '", data, "', error: ", err )
271 return dispatch( handler, data, err )
272 else -- connections was closed or fatal error
273 out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )
274 handler.close( )
275 disconnect( handler, err )
276 writequeue = nil
277 handler = nil
278 return false
279 end
280 end
281 handler._dispatchdata = function( ) -- this function writes data to handlers
282 local buffer = table_concat( writequeue, "", 1, eol )
283 local succ, err, byte = send( socket, buffer )
284 local count = ( succ or 0 ) * STAT_UNIT
285 sstat = sstat + count
286 sendstat = sendstat + count
287 out_put( "server.lua: sended '", buffer, "', bytes: ", succ, ", error: ", err, ", part: ", byte, ", to: ", ip, ":", clientport )
288 if succ then -- sending succesful
289 --writequeue = { }
290 eol = nil
291 writelen = removesocket( writelist, socket, writelen ) -- delete socket from writelist
292 return true
293 elseif byte and ( err == "timeout" or err == "wantwrite" ) then -- want write
294 buffer = string_sub( buffer, byte + 1, -1 ) -- new buffer
295 writequeue[ 1 ] = buffer -- insert new buffer in queue
296 eol = 1
297 return true
298 else -- connection was closed during sending or fatal error
299 out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )
300 handler.close( )
301 disconnect( handler, err )
302 writequeue = nil
303 handler = nil
304 return false
305 end
306 end
307
308 -- // COMPAT // --
309
310 handler.getIp = handler.ip
311 handler.getPort = handler.clientport
312
313 --// handshake //--
314
315 local wrote
316
317 handler.handshake = coroutine_wrap( function( client )
318 local err
319 for i = 1, 10 do -- 10 handshake attemps
320 _, err = client:dohandshake( )
321 if not err then
322 out_put( "server.lua: ssl handshake done" )
323 writelen = ( wrote and removesocket( writelist, socket, writelen ) ) or writelen
324 handler.receivedata = handler._receivedata -- when handshake is done, replace the handshake function with regular functions
325 handler.dispatchdata = handler._dispatchdata
326 return dispatch( handler )
327 else
328 out_put( "server.lua: error during ssl handshake: ", err )
329 if err == "wantwrite" then
330 if wrote == nil then
331 writelen = writelen + 1
332 writelist[ writelen ] = client
333 wrote = true
334 end
335 end
336 coroutine_yield( handler, nil, err ) -- handshake not finished
337 end
338 end
339 _ = err ~= "closed" and close( socket )
340 handler.close( )
341 disconnect( handler, err )
342 writequeue = nil
343 handler = nil
344 return false -- handshake failed
345 end
346 )
347 handler.receivedata = handler.handshake
348 handler.dispatchdata = handler.handshake
349
350 handler.handshake( socket ) -- do handshake
351
352 socketlist[ socket ] = handler
353 readlen = readlen + 1
354 readlist[ readlen ] = socket
355
356 return handler, socket
357 end
358
359 wraptcpclient = function( listener, socket, ip, serverport, clientport, mode ) -- this function wraps a socket
360
361 local dispatch, disconnect = listener.listener, listener.disconnect
362
363 --// private closures of the object //--
364
365 local writequeue = { } -- list for messages to send
366
367 local eol
368
369 local rstat, sstat = 0, 0
370
371 --// local import of socket methods //--
372
373 local send = socket.send
374 local receive = socket.receive
375 local close = socket.close
376 local shutdown = socket.shutdown
377
378 --// public methods of the object //--
379
380 local handler = { }
381
382 handler.getstats = function( )
383 return rstat, sstat
384 end
385
386 handler.listener = function( data, err )
387 return listener( handler, data, err )
388 end
389 handler.ssl = function( )
390 return false
391 end
392 handler.send = function( _, data, i, j )
393 return send( socket, data, i, j )
394 end
395 handler.receive = function( pattern, prefix )
396 return receive( socket, pattern, prefix )
397 end
398 handler.shutdown = function( pattern )
399 return shutdown( socket, pattern )
400 end
401 handler.close = function( closed )
402 _ = not closed and shutdown( socket )
403 _ = not closed and close( socket )
404 writelen = ( eol and removesocket( writelist, socket, writelen ) ) or writelen
405 readlen = removesocket( readlist, socket, readlen )
406 socketlist[ socket ] = nil
407 out_put "server.lua: closed handler and removed socket from list"
408 end
409 handler.ip = function( )
410 return ip
411 end
412 handler.serverport = function( )
413 return serverport
414 end
415 handler.clientport = function( )
416 return clientport
417 end
418 handler.write = function( data )
419 if not eol then
420 writelen = writelen + 1
421 writelist[ writelen ] = socket
422 eol = 0
423 end
424 eol = eol + 1
425 writequeue[ eol ] = data
426 end
427 handler.writequeue = function( )
428 return writequeue
429 end
430 handler.socket = function( )
431 return socket
432 end
433 handler.mode = function( )
434 return mode
435 end
436 handler.receivedata = function( )
437 local data, err, part = receive( socket, mode ) -- receive data in "mode"
438 if not err or ( err == "timeout" or err == "wantread" ) then -- received something
439 local data = data or part or ""
440 local count = #data * STAT_UNIT
441 rstat = rstat + count
442 receivestat = receivestat + count
443 out_put( "server.lua: read data '", data, "', error: ", err )
444 return dispatch( handler, data, err )
445 else -- connections was closed or fatal error
446 out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )
447 handler.close( )
448 disconnect( handler, err )
449 writequeue = nil
450 handler = nil
451 return false
452 end
453 end
454 handler.dispatchdata = function( ) -- this function writes data to handlers
455 local buffer = table_concat( writequeue, "", 1, eol )
456 local succ, err, byte = send( socket, buffer )
457 local count = ( succ or 0 ) * STAT_UNIT
458 sstat = sstat + count
459 sendstat = sendstat + count
460 out_put( "server.lua: sended '", buffer, "', bytes: ", succ, ", error: ", err, ", part: ", byte, ", to: ", ip, ":", clientport )
461 if succ then -- sending succesful
462 --writequeue = { }
463 eol = nil
464 writelen = removesocket( writelist, socket, writelen ) -- delete socket from writelist
465 return true
466 elseif byte and ( err == "timeout" or err == "wantwrite" ) then -- want write
467 buffer = string_sub( buffer, byte + 1, -1 ) -- new buffer
468 writequeue[ 1 ] = buffer -- insert new buffer in queue
469 eol = 1
470 return true
471 else -- connection was closed during sending or fatal error
472 out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )
473 handler.close( )
474 disconnect( handler, err )
475 writequeue = nil
476 handler = nil
477 return false
478 end
479 end
480
481 -- // COMPAT // --
482
483 handler.getIp = handler.ip
484 handler.getPort = handler.clientport
485
486 socketlist[ socket ] = handler
487 readlen = readlen + 1
488 readlist[ readlen ] = socket
489
490 return handler, socket
491 end
492
493 addtimer = function( listener )
494 timelistener[ #timelistener + 1 ] = listener
495 end
496
497 firetimer = function( listener )
498 for i, listener in ipairs( timelistener ) do
499 listener( )
500 end
501 end
502
503 addserver = function( listeners, port, addr, mode, sslctx ) -- this function provides a way for other scripts to reg a server
504 local err
505 if type( listeners ) ~= "table" then
506 err = "invalid listener table"
507 else
508 for name, func in pairs( listeners ) do
509 if type( func ) ~= "function" then
510 err = "invalid listener function"
511 break
512 end
513 end
514 end
515 if not type( port ) == "number" or not ( port >= 0 and port <= 65535 ) then
516 err = "invalid port"
517 elseif listener[ port ] then
518 err= "listeners on port '" .. port .. "' already exist"
519 elseif sslctx and not luasec then
520 err = "luasec not found"
521 end
522 if err then
523 out_error( "server.lua: ", err )
524 return nil, err
525 end
526 addr = addr or "*"
527 local server, err = socket_bind( addr, port )
528 if err then
529 out_error( "server.lua: ", err )
530 return nil, err
531 end
532 local handler, err = wrapserver( listeners, server, addr, port, mode, sslctx ) -- wrap new server socket
533 if not handler then
534 server:close( )
535 return nil, err
536 end
537 server:settimeout( 0 )
538 readlen = readlen + 1
539 readlist[ readlen ] = server
540 listener[ port ] = listeners
541 socketlist[ server ] = handler
542 out_put( "server.lua: new server listener on ", addr, ":", port )
543 return true
544 end
545
546 removesocket = function( tbl, socket, len ) -- this function removes sockets from a list
547 for i, target in ipairs( tbl ) do
548 if target == socket then
549 len = len - 1
550 table_remove( tbl, i )
551 return len
552 end
553 end
554 return len
555 end
556
557 closeall = function( )
558 for sock, handler in pairs( socketlist ) do
559 handler.shutdown( )
560 handler.close( )
561 socketlist[ sock ] = nil
562 end
563 writelist, readlist, socketlist = { }, { }, { }
564 end
565
566 closesocket = function( socket )
567 writelen = removesocket( writelist, socket, writelen )
568 readlen = removesocket( readlist, socket, readlen )
569 socketlist[ socket ] = nil
570 socket:close( )
571 end
572
573 loop = function( ) -- this is the main loop of the program
574 --signal_set( "hub", "run" )
575 repeat
576 local read, write, err = socket_select( readlist, writelist, 1 ) -- 1 sec timeout, nice for timers
577 for i, socket in ipairs( write ) do -- send data waiting in writequeues
578 local handler = socketlist[ socket ]
579 if handler then
580 handler.dispatchdata( )
581 else
582 closesocket( socket )
583 out_put "server.lua: found no handler and closed socket (writelist)" -- this should not happen
584 end
585 end
586 for i, socket in ipairs( read ) do -- receive data
587 local handler = socketlist[ socket ]
588 if handler then
589 handler.receivedata( )
590 else
591 closesocket( socket )
592 out_put "server.lua: found no handler and closed socket (readlist)" -- this can happen
593 end
594 end
595 firetimer( )
596 --collectgarbage "collect"
597 until false --signal_get "hub" ~= "run"
598 return --signal_get "hub"
599 end
600
601 ----------------------------------// BEGIN //--
602
603 ----------------------------------// PUBLIC INTERFACE //--
604
605 return {
606
607 add = addserver,
608 loop = loop,
609 stats = stats,
610 closeall = closeall,
611 addtimer = addtimer,
612
613 }

mercurial