c108a398b7b86b3fc9a9434ee4592d76acd460d0
[prosody.git] / net / http / server.lua
1
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;
8 local pairs = pairs;
9 local s_upper = string.upper;
10 local setmetatable = setmetatable;
11 local xpcall = xpcall;
12 local debug = debug;
13 local tostring = tostring;
14 local codes = require "net.http.codes";
15 local _G = _G;
16 local legacy_httpserver = require "net.httpserver";
17
18 local _M = {};
19
20 local sessions = {};
21 local handlers = {};
22
23 local listener = {};
24
25 local function is_wildcard_event(event)
26         return event:sub(-2, -1) == "/*";
27 end
28 local function is_wildcard_match(wildcard_event, event)
29         return wildcard_event:sub(1, -2) == event:sub(1, #wildcard_event-1);
30 end
31
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"))), priority };
45                                         table.insert(handlers_array, handler);
46                                 end
47                         end
48                 end
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 ~= b then -- If equal, compare next score value
54                                         return a_score[i] < b_score[i];
55                                 end
56                         end
57                         return false;
58                 end);
59                 handlers[curr_event] = handlers_array;
60                 return handlers_array;
61         end;
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;
69                                 end
70                         end
71                 end
72         end;
73 });
74
75 local handle_request;
76 local _1, _2, _3;
77 local function _handle_request() return handle_request(_1, _2, _3); end
78 local function _traceback_handler(err) log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end
79
80 function listener.onconnect(conn)
81         local secure = conn:ssl() and true or nil;
82         local pending = {};
83         local waiting = false;
84         local function process_next(last_response)
85                 --if waiting then log("debug", "can't process_next, waiting"); return; end
86                 if sessions[conn] and #pending > 0 then
87                         local request = t_remove(pending);
88                         --log("debug", "process_next: %s", request.path);
89                         waiting = true;
90                         --handle_request(conn, request, process_next);
91                         _1, _2, _3 = conn, request, process_next;
92                         if not xpcall(_handle_request, _traceback_handler) then
93                                 conn:write("HTTP/1.0 503 Internal Server Error\r\n\r\nAn error occured during the processing of this request.");
94                                 conn:close();
95                         end
96                 else
97                         --log("debug", "ready for more");
98                         waiting = false;
99                 end
100         end
101         local function success_cb(request)
102                 --log("debug", "success_cb: %s", request.path);
103                 request.secure = secure;
104                 t_insert(pending, request);
105                 if not waiting then
106                         process_next();
107                 end
108         end
109         local function error_cb(err)
110                 log("debug", "error_cb: %s", err or "<nil>");
111                 -- FIXME don't close immediately, wait until we process current stuff
112                 -- FIXME if err, send off a bad-request response
113                 sessions[conn] = nil;
114                 conn:close();
115         end
116         sessions[conn] = parser_new(success_cb, error_cb);
117 end
118
119 function listener.ondisconnect(conn)
120         local open_response = conn._http_open_response;
121         if open_response and open_response.on_destroy then
122                 open_response.finished = true;
123                 open_response:on_destroy();
124         end
125         sessions[conn] = nil;
126 end
127
128 function listener.onincoming(conn, data)
129         sessions[conn]:feed(data);
130 end
131
132 local headerfix = setmetatable({}, {
133         __index = function(t, k)
134                 local v = "\r\n"..k:gsub("_", "-"):gsub("%f[%w].", s_upper)..": ";
135                 t[k] = v;
136                 return v;
137         end
138 });
139
140 function _M.hijack_response(response, listener)
141         error("TODO");
142 end
143 function handle_request(conn, request, finish_cb)
144         --log("debug", "handler: %s", request.path);
145         local headers = {};
146         for k,v in pairs(request.headers) do headers[k:gsub("-", "_")] = v; end
147         request.headers = headers;
148         request.conn = conn;
149
150         local date_header = os_date('!%a, %d %b %Y %H:%M:%S GMT'); -- FIXME use
151         local conn_header = request.headers.connection;
152         local keep_alive = conn_header == "Keep-Alive" or (request.httpversion == "1.1" and conn_header ~= "close");
153
154         local response = {
155                 request = request;
156                 status_code = 200;
157                 headers = { date = date_header, connection = (keep_alive and "Keep-Alive" or "close") };
158                 conn = conn;
159                 send = _M.send_response;
160                 finish_cb = finish_cb;
161         };
162         conn._http_open_response = response;
163
164         if not request.headers.host then
165                 response.status_code = 400;
166                 response.headers.content_type = "text/html";
167                 response:send("<html><head>400 Bad Request</head><body>400 Bad Request: No Host header.</body></html>");
168         else
169                 local host = request.headers.host;
170                 if host then
171                         host = host:match("[^:]*"):lower();
172                         local event = request.method.." "..host..request.path:match("[^?]*");
173                         local payload = { request = request, response = response };
174                         --log("debug", "Firing event: %s", event);
175                         local result = events.fire_event(event, payload);
176                         if result ~= nil then
177                                 if result ~= true then
178                                         local code, body = 200, "";
179                                         local result_type = type(result);
180                                         if result_type == "number" then
181                                                 response.status_code = result;
182                                         elseif result_type == "string" then
183                                                 body = result;
184                                         elseif result_type == "table" then
185                                                 body = result.body;
186                                                 result.body = nil;
187                                                 for k, v in pairs(result) do
188                                                         response[k] = v;
189                                                 end
190                                         end
191                                         response:send(body);
192                                 end
193                                 return;
194                         end
195                 end
196
197                 -- if handler not called, return 404
198                 response.status_code = 404;
199                 response.headers.content_type = "text/html";
200                 response:send("<html><head><title>404 Not Found</title></head><body>404 Not Found: No such page.</body></html>");
201         end
202 end
203 function _M.send_response(response, body)
204         if response.finished then return; end
205         response.finished = true;
206         response.conn._http_open_response = nil;
207         
208         local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]);
209         local headers = response.headers;
210         body = body or "";
211         headers.content_length = #body;
212
213         local output = { status_line };
214         for k,v in pairs(headers) do
215                 t_insert(output, headerfix[k]..v);
216         end
217         t_insert(output, "\r\n\r\n");
218         t_insert(output, body);
219
220         response.conn:write(t_concat(output));
221         if response.on_destroy then
222                 response:on_destroy();
223                 response.on_destroy = nil;
224         end
225         if headers.connection == "Keep-Alive" then
226                 response:finish_cb();
227         else
228                 response.conn:close();
229         end
230 end
231 function _M.add_handler(event, handler, priority)
232         events.add_handler(event, handler, priority);
233 end
234 function _M.remove_handler(event, handler)
235         events.remove_handler(event, handler);
236 end
237
238 function _M.listen_on(port, interface, ssl)
239         addserver(interface or "*", port, listener, "*a", ssl);
240 end
241
242 _M.listener = listener;
243 _M.codes = codes;
244 return _M;