server.lua

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

mercurial