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";
16 local legacy_httpserver = require "net.httpserver";
24 local function is_wildcard_event(event)
25 return event:sub(-2, -1) == "/*";
27 local function is_wildcard_match(wildcard_event, event)
28 return wildcard_event:sub(1, -2) == event:sub(1, #wildcard_event-1);
31 local event_map = events._event_map;
32 setmetatable(events._handlers, {
33 __index = function (handlers, curr_event)
34 if is_wildcard_event(curr_event) then return; end -- Wildcard events cannot be fired
35 -- Find all handlers that could match this event, sort them
36 -- and then put the array into handlers[curr_event] (and return it)
37 local matching_handlers_set = {};
38 local handlers_array = {};
39 for event, handlers_set in pairs(event_map) do
40 if event == curr_event or
41 is_wildcard_event(event) and is_wildcard_match(event, curr_event) then
42 for handler, priority in pairs(handlers_set) do
43 matching_handlers_set[handler] = { (select(2, event:gsub("/", "%1"))), is_wildcard_event(event) and 0 or 1, priority };
44 table.insert(handlers_array, handler);
48 if #handlers_array > 0 then
49 table.sort(handlers_array, function(b, a)
50 local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b];
51 for i = 1, #a_score do
52 if a_score[i] ~= b_score[i] then -- If equal, compare next score value
53 return a_score[i] < b_score[i];
59 handlers_array = false;
61 rawset(handlers, curr_event, handlers_array);
62 return handlers_array;
64 __newindex = function (handlers, curr_event, handlers_array)
65 if handlers_array == nil
66 and is_wildcard_event(curr_event) then
67 -- Invalidate the indexes of all matching events
68 for event in pairs(handlers) do
69 if is_wildcard_match(curr_event, event) then
70 handlers[event] = nil;
74 rawset(handlers, curr_event, handlers_array);
80 local function _handle_request() return handle_request(_1, _2, _3); end
83 local function _traceback_handler(err) last_err = err; log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end
84 events.add_handler("http-error", function (error)
85 return "Error processing request: "..codes[error.code]..". Check your error log for more information.";
88 function listener.onconnect(conn)
89 local secure = conn:ssl() and true or nil;
91 local waiting = false;
92 local function process_next(last_response)
93 --if waiting then log("debug", "can't process_next, waiting"); return; end
94 if sessions[conn] and #pending > 0 then
95 local request = t_remove(pending);
96 --log("debug", "process_next: %s", request.path);
98 --handle_request(conn, request, process_next);
99 _1, _2, _3 = conn, request, process_next;
100 if not xpcall(_handle_request, _traceback_handler) then
101 conn:write("HTTP/1.0 500 Internal Server Error\r\n\r\n"..events.fire_event("http-error", { code = 500, private_message = last_err }));
105 --log("debug", "ready for more");
109 local function success_cb(request)
110 --log("debug", "success_cb: %s", request.path);
111 request.secure = secure;
112 t_insert(pending, request);
117 local function error_cb(err)
118 log("debug", "error_cb: %s", err or "<nil>");
119 -- FIXME don't close immediately, wait until we process current stuff
120 -- FIXME if err, send off a bad-request response
121 sessions[conn] = nil;
124 sessions[conn] = parser_new(success_cb, error_cb);
127 function listener.ondisconnect(conn)
128 local open_response = conn._http_open_response;
129 if open_response and open_response.on_destroy then
130 open_response.finished = true;
131 open_response:on_destroy();
133 sessions[conn] = nil;
136 function listener.onincoming(conn, data)
137 sessions[conn]:feed(data);
140 local headerfix = setmetatable({}, {
141 __index = function(t, k)
142 local v = "\r\n"..k:gsub("_", "-"):gsub("%f[%w].", s_upper)..": ";
148 function _M.hijack_response(response, listener)
151 function handle_request(conn, request, finish_cb)
152 --log("debug", "handler: %s", request.path);
154 for k,v in pairs(request.headers) do headers[k:gsub("-", "_")] = v; end
155 request.headers = headers;
158 local date_header = os_date('!%a, %d %b %Y %H:%M:%S GMT'); -- FIXME use
159 local conn_header = request.headers.connection;
160 local keep_alive = conn_header == "Keep-Alive" or (request.httpversion == "1.1" and conn_header ~= "close");
165 headers = { date = date_header, connection = (keep_alive and "Keep-Alive" or "close") };
167 send = _M.send_response;
168 finish_cb = finish_cb;
170 conn._http_open_response = response;
173 if not request.headers.host then
174 err = "No 'Host' header";
175 elseif not request.path then
176 err = "Invalid path";
180 response.status_code = 400;
181 response.headers.content_type = "text/html";
182 response:send(events.fire_event("http-error", { code = 400, message = err }));
184 local host = request.headers.host;
186 host = host:match("[^:]*"):lower();
187 local event = request.method.." "..host..request.path:match("[^?]*");
188 local payload = { request = request, response = response };
189 --log("debug", "Firing event: %s", event);
190 local result = events.fire_event(event, payload);
191 if result ~= nil then
192 if result ~= true then
193 local code, body = 200, "";
194 local result_type = type(result);
195 if result_type == "number" then
196 response.status_code = result;
197 if result >= 400 then
198 body = events.fire_event("http-error", { code = result });
200 elseif result_type == "string" then
202 elseif result_type == "table" then
205 for k, v in pairs(result) do
215 -- if handler not called, return 404
216 response.status_code = 404;
217 response.headers.content_type = "text/html";
218 response:send(events.fire_event("http-error", { code = 404 }));
221 function _M.send_response(response, body)
222 if response.finished then return; end
223 response.finished = true;
224 response.conn._http_open_response = nil;
226 local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]);
227 local headers = response.headers;
229 headers.content_length = #body;
231 local output = { status_line };
232 for k,v in pairs(headers) do
233 t_insert(output, headerfix[k]..v);
235 t_insert(output, "\r\n\r\n");
236 t_insert(output, body);
238 response.conn:write(t_concat(output));
239 if response.on_destroy then
240 response:on_destroy();
241 response.on_destroy = nil;
243 if headers.connection == "Keep-Alive" then
244 response:finish_cb();
246 response.conn:close();
249 function _M.add_handler(event, handler, priority)
250 events.add_handler(event, handler, priority);
252 function _M.remove_handler(event, handler)
253 events.remove_handler(event, handler);
256 function _M.listen_on(port, interface, ssl)
257 addserver(interface or "*", port, listener, "*a", ssl);
260 _M.listener = listener;