-local server = require "net.server"
-local url_parse = require "socket.url".parse;
-local httpstream_new = require "util.httpstream".new;
-
-local connlisteners_start = require "net.connlisteners".start;
-local connlisteners_get = require "net.connlisteners".get;
-local listener;
-
-local t_insert, t_concat = table.insert, table.concat;
-local tonumber, tostring, pairs, ipairs, type = tonumber, tostring, pairs, ipairs, type;
-local xpcall = xpcall;
-local debug_traceback = debug.traceback;
-
-local urlencode = function (s) return s and (s:gsub("%W", function (c) return ("%%%02x"):format(c:byte()); end)); end
-
-local log = require "util.logger".init("httpserver");
-
-local http_servers = {};
-
-module "httpserver"
-
-local default_handler;
-
-local function expectbody(reqt)
- return reqt.method == "POST";
-end
-
-local function send_response(request, response)
- -- Write status line
- local resp;
- if response.body or response.headers then
- local body = response.body and tostring(response.body);
- log("debug", "Sending response to %s", request.id);
- resp = { "HTTP/1.0 "..(response.status or "200 OK").."\r\n" };
- local h = response.headers;
- if h then
- for k, v in pairs(h) do
- t_insert(resp, k..": "..v.."\r\n");
- end
- end
- if body and not (h and h["Content-Length"]) then
- t_insert(resp, "Content-Length: "..#body.."\r\n");
- end
- t_insert(resp, "\r\n");
-
- if body and request.method ~= "HEAD" then
- t_insert(resp, body);
- end
- request.write(t_concat(resp));
- else
- -- Response we have is just a string (the body)
- log("debug", "Sending 200 response to %s", request.id or "<none>");
-
- local resp = "HTTP/1.0 200 OK\r\n"
- .. "Connection: close\r\n"
- .. "Content-Type: text/html\r\n"
- .. "Content-Length: "..#response.."\r\n"
- .. "\r\n"
- .. response;
-
- request.write(resp);
- end
- if not request.stayopen then
- request:destroy();
- end
-end
-
-local function call_callback(request, err)
- if request.handled then return; end
- request.handled = true;
- local callback = request.callback;
- if not callback and request.path then
- local path = request.url.path;
- local base = path:match("^/([^/?]+)");
- if not base then
- base = path:match("^http://[^/?]+/([^/?]+)");
- end
-
- callback = (request.server and request.server.handlers[base]) or default_handler;
- end
- if callback then
- local _callback = callback;
- function callback(a, b, c)
- local status, result = xpcall(function() _callback(a, b, c) end, debug_traceback);
- if status then return result; end
- log("error", "Error in HTTP server handler: %s", result);
- end
- if err then
- log("debug", "Request error: "..err);
- if not callback(nil, err, request) then
- destroy_request(request);
- end
- return;
- end
-
- local response = callback(request.method, request.body and t_concat(request.body), request);
- if response then
- if response == true and not request.destroyed then
- -- Keep connection open, we will reply later
- log("debug", "Request %s left open, on_destroy is %s", request.id, tostring(request.on_destroy));
- elseif response ~= true then
- -- Assume response
- send_response(request, response);
- destroy_request(request);
- end
- else
- log("debug", "Request handler provided no response, destroying request...");
- -- No response, close connection
- destroy_request(request);
- end
- end
-end
-
-local function request_reader(request, data, startpos)
- if not request.parser then
- local function success_cb(r)
- for k,v in pairs(r) do request[k] = v; end
- request.url = url_parse(request.path);
- request.url.path = request.url.path and request.url.path:gsub("%%(%x%x)", function(x) return x.char(tonumber(x, 16)) end);
- request.body = { request.body };
- call_callback(request);
- end
- local function error_cb(r)
- log("error", "Error in HTTP server handler: %s", r or "connection-closed");
- call_callback(request, r or "connection-closed");
- destroy_request(request);
- end
- request.parser = httpstream_new(success_cb, error_cb);
- end
- request.parser:feed(data);