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
132 if events.fire_event("*", payload) ~= nil then return; end
135 -- if handler not called, fallback to legacy httpserver handlers
136 _M.legacy_handler(request, response);
139 function _M.send_response(response, body)
140 local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]);
141 local headers = response.headers;
143 headers.content_length = #body;
145 local output = { status_line };
146 for k,v in pairs(headers) do
147 t_insert(output, headerfix[k]..v);
149 t_insert(output, "\r\n\r\n");
150 t_insert(output, body);
152 response.conn:write(t_concat(output));
153 if headers.connection == "Keep-Alive" then
154 response:finish_cb();
156 response.conn:close();
159 function _M.legacy_handler(request, response)
160 log("debug", "Invoking legacy handler");
161 local base = request.path:match("^/([^/?]+)");
162 local legacy_server = _G.httpserver and _G.httpserver.new.http_servers[5280];
163 local handler = legacy_server and legacy_server.handlers[base];
164 if not handler then handler = _G.httpserver and _G.httpserver.set_default_handler.default_handler; end
166 -- add legacy properties to request object
167 request.url = { path = request.path };
168 request.handler = response.conn;
169 request.id = tostring{}:match("%x+$");
171 for k,v in pairs(request.headers) do
172 headers[k:gsub("_", "-")] = v;
174 request.headers = headers;
175 function request:send(resp)
176 if self.destroyed then return; end
177 if resp.body or resp.headers then
179 for k,v in pairs(resp.headers) do response.headers[k] = v; end
181 response:send(resp.body)
188 function request:destroy()
189 if self.destroyed then return; end
190 if not self.sent then return self:send(""); end
191 self.destroyed = true;
192 if self.on_destroy then
193 log("debug", "Request has destroy callback");
196 log("debug", "Request has no destroy callback");
199 local r = handler(request.method, request.body, request);
204 log("debug", "No handler found");
205 response.status_code = 404;
206 response.headers.content_type = "text/html";
207 response:send("<html><head><title>404 Not Found</title></head><body>404 Not Found: No such page.</body></html>");
211 function _M.add_handler(event, handler, priority)
212 events.add_handler(event, handler, priority);
214 function _M.remove_handler(event, handler)
215 events.remove_handler(event, handler);
218 function _M.listen_on(port, interface, ssl)
219 addserver(interface or "*", port, listener, "*a", ssl);
222 _M.listener = listener;