local debug = debug;
local tostring = tostring;
local codes = require "net.http.codes";
-local _G = _G;
-local legacy_httpserver = require "net.httpserver";
local _M = {};
local secure = conn:ssl() and true or nil;
local pending = {};
local waiting = false;
- local function process_next(last_response)
- --if waiting then log("debug", "can't process_next, waiting"); return; end
- if sessions[conn] and #pending > 0 then
+ local function process_next()
+ if waiting then log("debug", "can't process_next, waiting"); return; end
+ waiting = true;
+ while sessions[conn] and #pending > 0 do
local request = t_remove(pending);
--log("debug", "process_next: %s", request.path);
- waiting = true;
--handle_request(conn, request, process_next);
_1, _2, _3 = conn, request, process_next;
if not xpcall(_handle_request, _traceback_handler) then
conn:write("HTTP/1.0 500 Internal Server Error\r\n\r\n"..events.fire_event("http-error", { code = 500, private_message = last_err }));
conn:close();
end
- else
- --log("debug", "ready for more");
- waiting = false;
end
+ --log("debug", "ready for more");
+ waiting = false;
end
local function success_cb(request)
--log("debug", "success_cb: %s", request.path);
+ if waiting then
+ log("error", "http connection handler is not reentrant: %s", request.path);
+ assert(false, "http connection handler is not reentrant");
+ end
request.secure = secure;
t_insert(pending, request);
- if not waiting then
- process_next();
- end
+ process_next();
end
local function error_cb(err)
log("debug", "error_cb: %s", err or "<nil>");
local date_header = os_date('!%a, %d %b %Y %H:%M:%S GMT'); -- FIXME use
local conn_header = request.headers.connection;
- local keep_alive = conn_header == "Keep-Alive" or (request.httpversion == "1.1" and conn_header ~= "close");
+ conn_header = conn_header and ","..conn_header:gsub("[ \t]", ""):lower().."," or ""
+ local httpversion = request.httpversion
+ local persistent = conn_header:find(",keep-alive,", 1, true)
+ or (httpversion == "1.1" and not conn_header:find(",close,", 1, true));
+
+ local response_conn_header;
+ if persistent then
+ response_conn_header = "Keep-Alive";
+ else
+ response_conn_header = httpversion == "1.1" and "close" or nil
+ end
local response = {
request = request;
status_code = 200;
- headers = { date = date_header, connection = (keep_alive and "Keep-Alive" or "close") };
+ headers = { date = date_header, connection = response_conn_header };
+ persistent = persistent;
conn = conn;
send = _M.send_response;
finish_cb = finish_cb;
-- Some sanity checking
local err_code, err;
- if not host then
- err_code, err = 400, "Missing or invalid 'Host' header";
- elseif not request.path then
+ if not request.path then
err_code, err = 400, "Invalid path";
elseif not hosts[host] then
if hosts[default_host] then
host = default_host;
- else
+ elseif host then
err_code, err = 404, "Unknown host: "..host;
+ else
+ err_code, err = 400, "Missing or invalid 'Host' header";
end
end
local result = events.fire_event(event, payload);
if result ~= nil then
if result ~= true then
- local code, body = 200, "";
+ local body;
local result_type = type(result);
if result_type == "number" then
response.status_code = result;
elseif result_type == "string" then
body = result;
elseif result_type == "table" then
- body = result.body;
- result.body = nil;
for k, v in pairs(result) do
response[k] = v;
end
local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]);
local headers = response.headers;
- body = body or "";
+ body = body or response.body or "";
headers.content_length = #body;
local output = { status_line };
response:on_destroy();
response.on_destroy = nil;
end
- if headers.connection == "Keep-Alive" then
+ if response.persistent then
response:finish_cb();
else
response.conn:close();