# HG changeset patch # User jbe # Date 1428618787 -7200 # Node ID 7014436d88ea9c27eddb73c479cb67a589a4fe5f # Parent 0eba766e9be2cf308cadfbb52967557c2bc60c8a Added helper function moonbridge_io.timeref(...); HTTP module sends 408 Request Timeout diff -r 0eba766e9be2 -r 7014436d88ea example_application.lua --- a/example_application.lua Thu Apr 09 20:02:43 2015 +0200 +++ b/example_application.lua Fri Apr 10 00:33:07 2015 +0200 @@ -60,7 +60,8 @@ { static_headers = {"Server: Moonbridge Example Server"}, request_body_size_limit = 16*1024*1024*1024, -- allow big file uploads - request_header_timeout = 360, -- request headers must be sent within 6 minutes (if nil, defaults to timeout below) + request_idle_timeout = 330, -- 5 minutes and 30 seconds after which an idle connection will be closed + request_header_timeout = 30, -- request headers must be sent within 30 seconds after first byte was received timeout = 1800 -- request body and response must be sent within 30 minutes }, function(request) diff -r 0eba766e9be2 -r 7014436d88ea moonbridge.c --- a/moonbridge.c Thu Apr 09 20:02:43 2015 +0200 +++ b/moonbridge.c Fri Apr 10 00:33:07 2015 +0200 @@ -2016,7 +2016,7 @@ return lua_gettop(L) - 1; } } - lua_pushnumber(L, oldval.it_value.tv_sec + 1e-6 * oldval.it_value.tv_usec); + lua_pushnumber(L, oldval.it_value.tv_sec + 1e-6 * oldval.it_value.tv_usec); return 1; } diff -r 0eba766e9be2 -r 7014436d88ea moonbridge_http.lua --- a/moonbridge_http.lua Thu Apr 09 20:02:43 2015 +0200 +++ b/moonbridge_http.lua Fri Apr 10 00:33:07 2015 +0200 @@ -180,13 +180,16 @@ end local input_chunk_size = options.maximum_input_chunk_size or options.chunk_size or 16384 local output_chunk_size = options.minimum_output_chunk_size or options.chunk_size or 1024 - local request_header_timeout, response_timeout + local request_idle_timeout, request_header_timeout, response_timeout + if options.request_idle_timeout ~= nil then + request_idle_timeout = options.request_idle_timeout or 0 + else + request_idle_timeout = 330 + end if options.request_header_timeout ~= nil then - request_header_timeout = options.request_header_timeout - elseif options.timeout ~= nil then - request_header_timeout = options.timeout or 0 + request_header_timeout = options.request_header_timeout or 0 else - request_header_timeout = 360 + request_header_timeout = 30 end if options.timeout ~= nil then response_timeout = options.timeout or 0 @@ -195,10 +198,9 @@ end -- return connect handler: return function(socket) + local socket_set = {[socket] = true} -- used for moonbridge_io.poll(...) local survive = true -- set to false if process shall be terminated later repeat - -- (re)set timeout: - timeout(request_header_timeout or 0) -- process named arguments "request_header_size_limit" and "request_body_size_limit": local remaining_header_size_limit = options.request_header_size_limit or 1024*1024 local remaining_body_size_limit = options.request_body_size_limit or 64*1024*1024 @@ -311,9 +313,13 @@ -- close output stream: assert_output(socket:finish()) -- wait for EOF of peer to avoid immediate TCP RST condition: - timeout(2, function() - socket:drain() - end) + do + local start_time = moonbridge_io.timeref() + repeat + socket:drain_nb() + local time_left = 2 - moonbridge_io.timeref(start_time) + until time_left <= 0 or not moonbridge_io.poll(socket_set, nil, time_left) + end -- fully close socket: socket_closed = true -- avoid double close on error assert_output(socket:close()) @@ -893,6 +899,12 @@ end end }) + -- wait for input: + if not moonbridge_io.poll({[socket] = true}, nil, request_idle_timeout) then + return request_error(false, "408 Request Timeout") + end + -- set timeout for request header processing: + timeout(request_header_timeout) -- read and parse request line: local line = socket:read(remaining_header_size_limit, "\n") if not line then return survive end @@ -991,12 +1003,14 @@ request.cookies[decode_uri(rawkey)] = decode_uri(rawvalue) end end - -- (re)set timeout: + -- (re)set timeout for handler: timeout(response_timeout or 0) -- call underlying handler and remember boolean result: if handler(request) ~= true then survive = false end -- finish request (unless already done by underlying handler): request:finish() + -- stop timeout timer: + timeout(0) until connection_close_responded return survive end diff -r 0eba766e9be2 -r 7014436d88ea moonbridge_io.c --- a/moonbridge_io.c Thu Apr 09 20:02:43 2015 +0200 +++ b/moonbridge_io.c Fri Apr 10 00:33:07 2015 +0200 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -1026,6 +1027,17 @@ } } +static int moonbr_io_timeref(lua_State *L) { + lua_Number sub; + struct timespec tp; + sub = luaL_optnumber(L, 1, 0); + if (clock_gettime(CLOCK_MONOTONIC, &tp)) { + return luaL_error(L, "Could not access CLOCK_MONOTONIC"); + } + lua_pushnumber(L, tp.tv_sec + tp.tv_nsec / 1.0e9 - sub); + return 1; +} + static const struct luaL_Reg moonbr_io_handle_methods[] = { {"read", moonbr_io_read}, {"read_nb", moonbr_io_read_nb}, @@ -1068,6 +1080,7 @@ {"locallisten", moonbr_io_locallisten}, {"tcplisten", moonbr_io_tcplisten}, {"poll", moonbr_io_poll}, + {"timeref", moonbr_io_timeref}, {NULL, NULL} };