Mercurial > prosody-hg
comparison server.lua @ 1:b8787e859fd2
Switched to new connection framework, courtesy of the luadch project
Now supports SSL on 5223
Beginning support for presence (aka. the proper routing of stanzas)
| author | matthew |
|---|---|
| date | Sun, 24 Aug 2008 01:51:02 +0000 |
| parents | |
| children | cb210ac67af8 |
comparison
equal
deleted
inserted
replaced
| 0:3e3171b59028 | 1:b8787e859fd2 |
|---|---|
| 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 _, handler in pairs( socketlist ) do | |
| 557 handler.shutdown( ) | |
| 558 handler.close( ) | |
| 559 socketlist[ _ ] = 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 } |
