Skip to content

Commit

Permalink
Add option idle_timeout
Browse files Browse the repository at this point in the history
The main problem with current implementation of HTTP server is it's
cooperative scheduling. To control connections we need invent something
that will periodically checks open connections and close idles with
exceeded timeout. In proposed implementation we close idle connections
with exceeded idle_timeout in synchronous manner - before start
processing a new connection. In comparison to other solutions (like
running checking function in a separate fiber) closing connections
synchronously is much easier to implement and don't make module complex.

Fixes #137
  • Loading branch information
ligurio committed Jan 27, 2022
1 parent 9475480 commit eeef4c8
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 0 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ httpd = require('http.server').new(host, port[, { options } ])
* `keepalive_disable` - disables keep-alive connections with misbehaving
clients. Parameter accept a map that contains a user agents with non-nil
value. By default map is empty.
* `idle_timeout` - maximum amount of time an idle (keep-alive) connection will
remain idle before closing. When the idle timeout is exceeded, HTTP server
closes the keepalive connection. Default value: 0 seconds (disabled).

Example:

Expand Down
29 changes: 29 additions & 0 deletions http/server.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
local lib = require('http.lib')

local fio = require('fio')
local fiber = require('fiber')
local require = require
local package = package
local mime_types = require('http.mime_types')
Expand All @@ -15,6 +16,21 @@ local errno = require 'errno'

local DETACHED = 101

-- A table that maps fiber object to time when it was active last time.
-- Using for tracking idle connections.
local connections = {}

local function close_idle_connections(connections)
-- FIXME: complexity is O(N) and it is bad
for fiber_obj, start_time in pairs(connections) do
if fiber.clock() - start_time >= self.options.idle_timeout then
log.info("Connection closed, idle_timeout was exceeded (fiber id %d)\n", fiber_obj:id())
fiber_obj:cancel()
connections[fiber_obj] = nil
end
end
end

local function errorf(fmt, ...)
error(string.format(fmt, ...))
end
Expand Down Expand Up @@ -757,6 +773,11 @@ local function process_client(self, s, peer)
while true do
local hdrs = ''

connections[fiber.id] = fiber.clock()
if self.options.idle_timeout > 0 then
close_idle_connections(connections)
end

local is_eof = false
while true do
local chunk = s:read{
Expand Down Expand Up @@ -979,6 +1000,9 @@ local function httpd_stop(self)
self.tcp_server:close()
self.tcp_server = nil
end

connections = {}

return self
end

Expand Down Expand Up @@ -1303,6 +1327,10 @@ local exports = {
if type(options.keepalive_disable) ~= 'table' then
error('Option keepalive_disable must be a table.')
end
if options.idle_timeout ~= nil and
type(options.idle_timeout) ~= 'number' then
error('Option idle_timeout must be a number.')
end

local default = {
max_header_size = 4096,
Expand All @@ -1317,6 +1345,7 @@ local exports = {
log_errors = true,
display_errors = false,
keepalive_disable = {},
idle_timeout = 0, -- no timeout, option is disabled
}

local self = {
Expand Down
10 changes: 10 additions & 0 deletions test/integration/http_server_options_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ g.before_test('test_keepalive_disallowed', function()
g.httpd:start()
end)

g.before_test('test_idle_timeout_default', function()
g.httpd = helpers.cfgserv()
g.httpd:start()
end)

g.after_each(function()
helpers.teardown(g.httpd)
end)
Expand Down Expand Up @@ -73,3 +78,8 @@ g.test_keepalive_change_in_runtime = function(g)
["Mozilla/4.0"] = true
})
end

g.test_idle_timeout_default = function(g)
local httpd = g.httpd
t.assert_equals(httpd.options.idle_timeout, 0)
end

0 comments on commit eeef4c8

Please sign in to comment.