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";
25 local function is_wildcard_event(event)
26 return event:sub(-2, -1) == "/*";
28 local function is_wildcard_match(wildcard_event, event)
29 return wildcard_event:sub(1, -2) == event:sub(1, #wildcard_event-1);
32 local event_map = events._event_map;
33 setmetatable(events._handlers, {
34 __index = function (handlers, curr_event)
35 if is_wildcard_event(curr_event) then return; end -- Wildcard events cannot be fired
36 -- Find all handlers that could match this event, sort them
37 -- and then put the array into handlers[curr_event] (and return it)
38 local matching_handlers_set = {};
39 local handlers_array = {};
40 for event, handlers_set in pairs(event_map) do
41 if event == curr_event or
42 is_wildcard_event(event) and is_wildcard_match(event, curr_event) then
43 for handler, priority in pairs(handlers_set) do
44 matching_handlers_set[handler] = { (select(2, event:gsub("/", "%1"))), is_wildcard_event(event) and 0 or 1, priority };
45 table.insert(handlers_array, handler);
49 if #handlers_array == 0 then return; end
50 table.sort(handlers_array, function(b, a)
51 local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b];
52 for i = 1, #a_score do
53 if a_score[i] ~= b_score[i] then -- If equal, compare next score value
54 return a_score[i] < b_score[i];
59 handlers[curr_event] = handlers_array;
60 return handlers_array;
62 __newindex = function (handlers, curr_event, handlers_array)
63 if handlers_array == nil
64 and is_wildcard_event(curr_event) then
65 -- Invalidate all matching
66 for event in pairs(handlers) do
67 if is_wildcard_match(curr_event, event) then
68 handlers[event] = nil;
77 local function _handle_request() return handle_request(_1, _2, _3); end
80 local function _traceback_handler(err) last_err = err; log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end
81 events.add_handler("http-error", function (error)
82 return "Error processing request: "..codes[error.code]..". Check your error log for more information.";
85 function listener.onconnect(conn)
86 local secure = conn:ssl() and true or nil;
88 local waiting = false;
89 local function process_next(last_response)
90 --if waiting then log("debug", "can't process_next, waiting"); return; end
91 if sessions[conn] and #pending > 0 then
92 local request = t_remove(pending);
93 --log("debug", "process_next: %s", request.path);
95 --handle_request(conn, request, process_next);
96 _1, _2, _3 = conn, request, process_next;
97 if not xpcall(_handle_request, _traceback_handler) then
98 conn:write("HTTP/1.0 500 Internal Server Error\r\n\r\n"..events.fire_event("http-error", { code = 500, private_message = last_err }));
102 --log("debug", "ready for more");
106 local function success_cb(request)
107 --log("debug", "success_cb: %s", request.path);
108 request.secure = secure;
109 t_insert(pending, request);
114 local function error_cb(err)
115 log("debug", "error_cb: %s", err or "<nil>");
116 -- FIXME don't close immediately, wait until we process current stuff
117 -- FIXME if err, send off a bad-request response
118 sessions[conn] = nil;
121 sessions[conn] = parser_new(success_cb, error_cb);
124 function listener.ondisconnect(conn)
125 local open_response = conn._http_open_response;
126 if open_response and open_response.on_destroy then
127 open_response.finished = true;
128 open_response:on_destroy();
130 sessions[conn] = nil;
133 function listener.onincoming(conn, data)
134 sessions[conn]:feed(data);
137 local headerfix = setmetatable({}, {
138 __index = function(t, k)
139 local v = "\r\n"..k:gsub("_", "-"):gsub("%f[%w].", s_upper)..": ";
145 function _M.hijack_response(response, listener)
148 function handle_request(conn, request, finish_cb)
149 --log("debug", "handler: %s", request.path);
151 for k,v in pairs(request.headers) do headers[k:gsub("-", "_")] = v; end
152 request.headers = headers;
155 local date_header = os_date('!%a, %d %b %Y %H:%M:%S GMT'); -- FIXME use
156 local conn_header = request.headers.connection;
157 local keep_alive = conn_header == "Keep-Alive" or (request.httpversion == "1.1" and conn_header ~= "close");
162 headers = { date = date_header, connection = (keep_alive and "Keep-Alive" or "close") };
164 send = _M.send_response;
165 finish_cb = finish_cb;
167 conn._http_open_response = response;
170 if not request.headers.host then
171 err = "No 'Host' header";
172 elseif not request.path then
173 err = "Invalid path";
177 response.status_code = 400;
178 response.headers.content_type = "text/html";
179 response:send(events.fire_event("http-error", { code = 400, message = err }));
181 local host = request.headers.host;
183 host = host:match("[^:]*"):lower();
184 local event = request.method.." "..host..request.path:match("[^?]*");
185 local payload = { request = request, response = response };
186 --log("debug", "Firing event: %s", event);
187 local result = events.fire_event(event, payload);
188 if result ~= nil then
189 if result ~= true then
190 local code, body = 200, "";
191 local result_type = type(result);
192 if result_type == "number" then
193 response.status_code = result;
194 if result >= 400 then
195 body = events.fire_event("http-error", { code = result });
197 elseif result_type == "string" then
199 elseif result_type == "table" then
202 for k, v in pairs(result) do
212 -- if handler not called, return 404
213 response.status_code = 404;
214 response.headers.content_type = "text/html";
215 response:send(events.fire_event("http-error", { code = 404 }));
218 function _M.send_response(response, body)
219 if response.finished then return; end
220 response.finished = true;
221 response.conn._http_open_response = nil;
223 local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]);
224 local headers = response.headers;
226 headers.content_length = #body;
228 local output = { status_line };
229 for k,v in pairs(headers) do
230 t_insert(output, headerfix[k]..v);
232 t_insert(output, "\r\n\r\n");
233 t_insert(output, body);
235 response.conn:write(t_concat(output));
236 if response.on_destroy then
237 response:on_destroy();
238 response.on_destroy = nil;
240 if headers.connection == "Keep-Alive" then
241 response:finish_cb();
243 response.conn:close();
246 function _M.add_handler(event, handler, priority)
247 events.add_handler(event, handler, priority);
249 function _M.remove_handler(event, handler)
250 events.remove_handler(event, handler);
253 function _M.listen_on(port, interface, ssl)
254 addserver(interface or "*", port, listener, "*a", ssl);
257 _M.listener = listener;