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;
12 local traceback = debug.traceback;
13 local tostring = tostring;
14 local codes = require "net.http.codes";
23 local function is_wildcard_event(event)
24 return event:sub(-2, -1) == "/*";
26 local function is_wildcard_match(wildcard_event, event)
27 return wildcard_event:sub(1, -2) == event:sub(1, #wildcard_event-1);
30 local recent_wildcard_events, max_cached_wildcard_events = {}, 10000;
32 local event_map = events._event_map;
33 setmetatable(events._handlers, {
34 -- Called when firing an event that doesn't exist (but may match a wildcard handler)
35 __index = function (handlers, curr_event)
36 if is_wildcard_event(curr_event) then return; end -- Wildcard events cannot be fired
37 -- Find all handlers that could match this event, sort them
38 -- and then put the array into handlers[curr_event] (and return it)
39 local matching_handlers_set = {};
40 local handlers_array = {};
41 for event, handlers_set in pairs(event_map) do
42 if event == curr_event or
43 is_wildcard_event(event) and is_wildcard_match(event, curr_event) then
44 for handler, priority in pairs(handlers_set) do
45 matching_handlers_set[handler] = { (select(2, event:gsub("/", "%1"))), is_wildcard_event(event) and 0 or 1, priority };
46 table.insert(handlers_array, handler);
50 if #handlers_array > 0 then
51 table.sort(handlers_array, function(b, a)
52 local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b];
53 for i = 1, #a_score do
54 if a_score[i] ~= b_score[i] then -- If equal, compare next score value
55 return a_score[i] < b_score[i];
61 handlers_array = false;
63 rawset(handlers, curr_event, handlers_array);
64 if not event_map[curr_event] then -- Only wildcard handlers match, if any
65 table.insert(recent_wildcard_events, curr_event);
66 if #recent_wildcard_events > max_cached_wildcard_events then
67 rawset(handlers, table.remove(recent_wildcard_events, 1), nil);
70 return handlers_array;
72 __newindex = function (handlers, curr_event, handlers_array)
73 if handlers_array == nil
74 and is_wildcard_event(curr_event) then
75 -- Invalidate the indexes of all matching events
76 for event in pairs(handlers) do
77 if is_wildcard_match(curr_event, event) then
78 handlers[event] = nil;
82 rawset(handlers, curr_event, handlers_array);
88 local function _handle_request() return handle_request(_1, _2, _3); end
91 local function _traceback_handler(err) last_err = err; log("error", "Traceback[httpserver]: %s", traceback(tostring(err), 2)); end
92 events.add_handler("http-error", function (error)
93 return "Error processing request: "..codes[error.code]..". Check your error log for more information.";
96 function listener.onconnect(conn)
97 local secure = conn:ssl() and true or nil;
99 local waiting = false;
100 local function process_next()
101 if waiting then return; end -- log("debug", "can't process_next, waiting");
103 while sessions[conn] and #pending > 0 do
104 local request = t_remove(pending);
105 --log("debug", "process_next: %s", request.path);
106 --handle_request(conn, request, process_next);
107 _1, _2, _3 = conn, request, process_next;
108 if not xpcall(_handle_request, _traceback_handler) then
109 conn:write("HTTP/1.0 500 Internal Server Error\r\n\r\n"..events.fire_event("http-error", { code = 500, private_message = last_err }));
113 --log("debug", "ready for more");
116 local function success_cb(request)
117 --log("debug", "success_cb: %s", request.path);
119 log("error", "http connection handler is not reentrant: %s", request.path);
120 assert(false, "http connection handler is not reentrant");
122 request.secure = secure;
123 t_insert(pending, request);
126 local function error_cb(err)
127 log("debug", "error_cb: %s", err or "<nil>");
128 -- FIXME don't close immediately, wait until we process current stuff
129 -- FIXME if err, send off a bad-request response
130 sessions[conn] = nil;
133 sessions[conn] = parser_new(success_cb, error_cb);
136 function listener.ondisconnect(conn)
137 local open_response = conn._http_open_response;
138 if open_response and open_response.on_destroy then
139 open_response.finished = true;
140 open_response:on_destroy();
142 sessions[conn] = nil;
145 function listener.ondetach(conn)
146 sessions[conn] = nil;
149 function listener.onincoming(conn, data)
150 sessions[conn]:feed(data);
153 local headerfix = setmetatable({}, {
154 __index = function(t, k)
155 local v = "\r\n"..k:gsub("_", "-"):gsub("%f[%w].", s_upper)..": ";
161 function _M.hijack_response(response, listener)
164 function handle_request(conn, request, finish_cb)
165 --log("debug", "handler: %s", request.path);
167 for k,v in pairs(request.headers) do headers[k:gsub("-", "_")] = v; end
168 request.headers = headers;
171 local date_header = os_date('!%a, %d %b %Y %H:%M:%S GMT'); -- FIXME use
172 local conn_header = request.headers.connection;
173 conn_header = conn_header and ","..conn_header:gsub("[ \t]", ""):lower().."," or ""
174 local httpversion = request.httpversion
175 local persistent = conn_header:find(",keep-alive,", 1, true)
176 or (httpversion == "1.1" and not conn_header:find(",close,", 1, true));
178 local response_conn_header;
180 response_conn_header = "Keep-Alive";
182 response_conn_header = httpversion == "1.1" and "close" or nil
188 headers = { date = date_header, connection = response_conn_header };
189 persistent = persistent;
191 send = _M.send_response;
192 done = _M.finish_response;
193 finish_cb = finish_cb;
195 conn._http_open_response = response;
197 local host = (request.headers.host or ""):match("[^:]+");
199 -- Some sanity checking
201 if not request.path then
202 err_code, err = 400, "Invalid path";
203 elseif not hosts[host] then
204 if hosts[default_host] then
207 err_code, err = 404, "Unknown host: "..host;
209 err_code, err = 400, "Missing or invalid 'Host' header";
214 response.status_code = err_code;
215 response:send(events.fire_event("http-error", { code = err_code, message = err }));
219 local event = request.method.." "..host..request.path:match("[^?]*");
220 local payload = { request = request, response = response };
221 log("debug", "Firing event: %s", event);
222 local result = events.fire_event(event, payload);
223 if result ~= nil then
224 if result ~= true then
226 local result_type = type(result);
227 if result_type == "number" then
228 response.status_code = result;
229 if result >= 400 then
230 body = events.fire_event("http-error", { code = result });
232 elseif result_type == "string" then
234 elseif result_type == "table" then
235 for k, v in pairs(result) do
236 if k ~= "headers" then
239 for header_name, header_value in pairs(v) do
240 response.headers[header_name] = header_value;
250 -- if handler not called, return 404
251 response.status_code = 404;
252 response:send(events.fire_event("http-error", { code = 404 }));
254 local function prepare_header(response)
255 local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]);
256 local headers = response.headers;
257 local output = { status_line };
258 for k,v in pairs(headers) do
259 t_insert(output, headerfix[k]..v);
261 t_insert(output, "\r\n\r\n");
264 _M.prepare_header = prepare_header;
265 function _M.send_response(response, body)
266 if response.finished then return; end
267 body = body or response.body or "";
268 response.headers.content_length = #body;
269 local output = prepare_header(response);
270 t_insert(output, body);
271 response.conn:write(t_concat(output));
274 function _M.finish_response(response)
275 if response.finished then return; end
276 response.finished = true;
277 response.conn._http_open_response = nil;
278 if response.on_destroy then
279 response:on_destroy();
280 response.on_destroy = nil;
282 if response.persistent then
283 response:finish_cb();
285 response.conn:close();
288 function _M.add_handler(event, handler, priority)
289 events.add_handler(event, handler, priority);
291 function _M.remove_handler(event, handler)
292 events.remove_handler(event, handler);
295 function _M.listen_on(port, interface, ssl)
296 addserver(interface or "*", port, listener, "*a", ssl);
298 function _M.add_host(host)
301 function _M.remove_host(host)
304 function _M.set_default_host(host)
307 function _M.fire_event(event, ...)
308 return events.fire_event(event, ...);
311 _M.listener = listener;