2 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat;
3 local parser_new = require "net.http.parser".new;
4 local events = require "util.events".new();
5 local addserver = require "net.server".addserver;
6 local log = require "util.logger".init("http.server");
7 local os_date = os.date;
9 local s_upper = string.upper;
10 local setmetatable = setmetatable;
11 local xpcall = xpcall;
13 local tostring = tostring;
14 local codes = require "net.http.codes";
26 local function _handle_request() return handle_request(_1, _2, _3); end
27 local function _traceback_handler(err) log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end
29 function listener.onconnect(conn)
30 local secure = conn:ssl() and true or nil;
32 local waiting = false;
33 local function process_next(last_response)
34 --if waiting then log("debug", "can't process_next, waiting"); return; end
35 if sessions[conn] and #pending > 0 then
36 local request = t_remove(pending);
37 --log("debug", "process_next: %s", request.path);
39 --handle_request(conn, request, process_next);
40 _1, _2, _3 = conn, request, process_next;
41 if not xpcall(_handle_request, _traceback_handler) then
42 conn:write("HTTP/1.0 503 Internal Server Error\r\n\r\nAn error occured during the processing of this request.");
46 --log("debug", "ready for more");
50 local function success_cb(request)
51 --log("debug", "success_cb: %s", request.path);
52 request.secure = secure;
53 t_insert(pending, request);
58 local function error_cb(err)
59 log("debug", "error_cb: %s", err or "<nil>");
60 -- FIXME don't close immediately, wait until we process current stuff
61 -- FIXME if err, send off a bad-request response
65 sessions[conn] = parser_new(success_cb, error_cb);
68 function listener.ondisconnect(conn)
72 function listener.onincoming(conn, data)
73 sessions[conn]:feed(data);
76 local headerfix = setmetatable({}, {
77 __index = function(t, k)
78 local v = "\r\n"..k:gsub("_", "-"):gsub("%f[%w].", s_upper)..": ";
84 function _M.hijack_response(response, listener)
87 function handle_request(conn, request, finish_cb)
88 --log("debug", "handler: %s", request.path);
90 for k,v in pairs(request.headers) do headers[k:gsub("-", "_")] = v; end
91 request.headers = headers;
94 local date_header = os_date('!%a, %d %b %Y %H:%M:%S GMT'); -- FIXME use
95 local conn_header = request.headers.connection;
96 local keep_alive = conn_header == "Keep-Alive" or (request.httpversion == "1.1" and conn_header ~= "close");
101 headers = { date = date_header, connection = (keep_alive and "Keep-Alive" or "close") };
103 send = _M.send_response;
104 finish_cb = finish_cb;
107 if not request.headers.host then
108 response.status_code = 400;
109 response.headers.content_type = "text/html";
110 response:send("<html><head>400 Bad Request</head><body>400 Bad Request: No Host header.</body></html>");
113 --response.headers.content_type = "text/plain";
114 --response:send("host="..(request.headers.host or "").."\npath="..request.path.."\n"..(request.body or ""));
115 local host = request.headers.host;
117 host = host:match("[^:]*"):lower();
118 local event = request.method.." "..host..request.path:match("[^?]*");
119 local payload = { request = request, response = response };
121 if events.fire_event(event, payload) ~= nil then return; end
122 event = (event:sub(-1) == "/") and event:sub(1, -1) or event:gsub("[^/]*$", "");
123 if event:sub(-1) == "/" then
124 event = event:sub(1, -1);
126 event = event:gsub("[^/]*$", "");
128 until not event:find("/", 1, true);]]
129 --log("debug", "Event: %s", event);
130 if events.fire_event(event, payload) ~= nil then return; end
131 -- TODO try adding/stripping / at the end, but this needs to work via an HTTP redirect
134 -- if handler not called, fallback to legacy httpserver handlers
135 _M.legacy_handler(request, response);
138 function _M.send_response(response, body)
139 local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]);
140 local headers = response.headers;
142 headers.content_length = #body;
144 local output = { status_line };
145 for k,v in pairs(headers) do
146 t_insert(output, headerfix[k]..v);
148 t_insert(output, "\r\n\r\n");
149 t_insert(output, body);
151 response.conn:write(t_concat(output));
152 if headers.connection == "Keep-Alive" then
153 response:finish_cb();
155 response.conn:close();
158 function _M.legacy_handler(request, response)
159 log("debug", "Invoking legacy handler");
160 local base = request.path:match("^/([^/?]+)");
161 local legacy_server = _G.httpserver and _G.httpserver.new.http_servers[5280];
162 local handler = legacy_server and legacy_server.handlers[base];
163 if not handler then handler = _G.httpserver and _G.httpserver.set_default_handler.default_handler; end
165 -- add legacy properties to request object
166 request.url = { path = request.path };
167 request.handler = response.conn;
168 request.id = tostring{}:match("%x+$");
170 for k,v in pairs(request.headers) do
171 headers[k:gsub("_", "-")] = v;
173 request.headers = headers;
174 function request:send(resp)
175 if self.destroyed then return; end
176 if resp.body or resp.headers then
178 for k,v in pairs(resp.headers) do response.headers[k] = v; end
180 response:send(resp.body)
187 function request:destroy()
188 if self.destroyed then return; end
189 if not self.sent then return self:send(""); end
190 self.destroyed = true;
191 if self.on_destroy then
192 log("debug", "Request has destroy callback");
195 log("debug", "Request has no destroy callback");
198 local r = handler(request.method, request.body, request);
203 log("debug", "No handler found");
204 response.status_code = 404;
205 response.headers.content_type = "text/html";
206 response:send("<html><head><title>404 Not Found</title></head><body>404 Not Found: No such page.</body></html>");
210 function _M.add_handler(event, handler, priority)
211 events.add_handler(event, handler, priority);
213 function _M.remove_handler(event, handler)
214 events.remove_handler(event, handler);
217 function _M.listen_on(port, interface, ssl)
218 addserver(interface or "*", port, listener, "*a", ssl);
221 _M.listener = listener;