# HG changeset patch # User jbe # Date 1434845124 -7200 # Node ID 2ed3d94a0eb785658423cb7fb8a6784f4996b467 # Parent 4e72725118d0b156996739a6d2fe1ee7e238629e Improvements to timeout system of HTTP module diff -r 4e72725118d0 -r 2ed3d94a0eb7 moonbridge_http.lua --- a/moonbridge_http.lua Sat Jun 20 02:33:40 2015 +0200 +++ b/moonbridge_http.lua Sun Jun 21 02:05:24 2015 +0200 @@ -197,9 +197,10 @@ local output_chunk_size = options.minimum_output_chunk_size or options.chunk_size or 1024 local header_size_limit = options.header_size_limit or 1024*1024 local body_size_limit = options.body_size_limit or 64*1024*1024 - local request_idle_timeout = default("request_idle_timeout", 65) - local request_header_timeout = default("request_header_timeout", 30) - local response_timeout = default("response_timeout", 1800) + local idle_timeout = default("idle_timeout", 65) + local stall_timeout = default("stall_timeout", 60) + local request_header_timeout = default("request_header_timeout", 120) + local response_timeout = default("response_timeout", 3600) local drain_timeout = default("drain_timeout", 2) local poll = options.poll_function or moonbridge_io.poll -- return socket handler: @@ -218,28 +219,31 @@ -- function trying to unblock socket by reading: local function unblock() if consume then - poll(socket_set, socket_set) + if not poll(socket_set, socket_set, stall_timeout) then + socket:reset() + error("Client connection stalled") + end consume() else - poll(nil, socket_set) + if not poll(nil, socket_set, stall_timeout) then + socket:reset() + error("Client connection stalled") + end end end -- function that enforces consumption of all input: - local function consume_all() - local endtime - if consume == drain then - -- do not consume endlessly for the sole purpose to drain - endtime = moonbridge_io.timeref() + drain_timeout - end + local function consume_all(timeout) + local starttime = timeout and moonbridge_io.timeref() while consume do - if endtime then - if not poll(socket_set, nil, 0-moonbridge_io.timeref(endtime)) then - break + if timeout then + if not poll(socket_set, nil, timeout-moonbridge_io.timeref(starttime)) then + return false end else poll(socket_set, nil) end consume() + return true end end -- handle requests in a loop: @@ -407,9 +411,13 @@ send_flush() assert_close(socket:finish()) -- wait for EOF from peer to avoid immediate TCP RST condition: - consume_all() - -- fully close socket: - assert_close(socket:close()) + if consume_all(drain_timeout) then + -- fully close socket: + assert_close(socket:close()) + else + -- send TCP RST if draining input takes too long: + assert_close(socket:reset()) + end else -- flush outgoing data: send_flush() @@ -1071,7 +1079,7 @@ return true -- success end -- wait for input: - if not poll(socket_set, nil, request_idle_timeout) then + if not poll(socket_set, nil, idle_timeout) then return request_error(false, "408 Request Timeout", "Idle connection timed out") end -- read headers (with timeout): @@ -1081,11 +1089,16 @@ while true do local status, retval = coro() if status == nil then - local remaining + local timeout if request_header_timeout then - remaining = request_header_timeout - moonbridge_io.timeref(starttime) + timeout = request_header_timeout - moonbridge_io.timeref(starttime) + if stall_timeout and timeout > stall_timeout then + timeout = stall_timeout + end + else + timeout = stall_timeout end - if not poll(socket_set, nil, remaining) then + if not poll(socket_set, nil, timeout) then return request_error(false, "408 Request Timeout", "Timeout while receiving headers") end elseif status == false then