Software /
code /
prosody
Comparison
net/http/server.lua @ 4797:e239668aa6d2
Merge 0.9->trunk
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Sun, 29 Apr 2012 02:10:55 +0100 |
parent | 4788:e14f5a156571 |
child | 5300:fcb1be0b4a5c |
comparison
equal
deleted
inserted
replaced
4796:04a34287dc12 | 4797:e239668aa6d2 |
---|---|
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 | |
16 local _M = {}; | |
17 | |
18 local sessions = {}; | |
19 local listener = {}; | |
20 local hosts = {}; | |
21 local default_host; | |
22 | |
23 local function is_wildcard_event(event) | |
24 return event:sub(-2, -1) == "/*"; | |
25 end | |
26 local function is_wildcard_match(wildcard_event, event) | |
27 return wildcard_event:sub(1, -2) == event:sub(1, #wildcard_event-1); | |
28 end | |
29 | |
30 local event_map = events._event_map; | |
31 setmetatable(events._handlers, { | |
32 __index = function (handlers, curr_event) | |
33 if is_wildcard_event(curr_event) then return; end -- Wildcard events cannot be fired | |
34 -- Find all handlers that could match this event, sort them | |
35 -- and then put the array into handlers[curr_event] (and return it) | |
36 local matching_handlers_set = {}; | |
37 local handlers_array = {}; | |
38 for event, handlers_set in pairs(event_map) do | |
39 if event == curr_event or | |
40 is_wildcard_event(event) and is_wildcard_match(event, curr_event) then | |
41 for handler, priority in pairs(handlers_set) do | |
42 matching_handlers_set[handler] = { (select(2, event:gsub("/", "%1"))), is_wildcard_event(event) and 0 or 1, priority }; | |
43 table.insert(handlers_array, handler); | |
44 end | |
45 end | |
46 end | |
47 if #handlers_array > 0 then | |
48 table.sort(handlers_array, function(b, a) | |
49 local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b]; | |
50 for i = 1, #a_score do | |
51 if a_score[i] ~= b_score[i] then -- If equal, compare next score value | |
52 return a_score[i] < b_score[i]; | |
53 end | |
54 end | |
55 return false; | |
56 end); | |
57 else | |
58 handlers_array = false; | |
59 end | |
60 rawset(handlers, curr_event, handlers_array); | |
61 return handlers_array; | |
62 end; | |
63 __newindex = function (handlers, curr_event, handlers_array) | |
64 if handlers_array == nil | |
65 and is_wildcard_event(curr_event) then | |
66 -- Invalidate the indexes of all matching events | |
67 for event in pairs(handlers) do | |
68 if is_wildcard_match(curr_event, event) then | |
69 handlers[event] = nil; | |
70 end | |
71 end | |
72 end | |
73 rawset(handlers, curr_event, handlers_array); | |
74 end; | |
75 }); | |
76 | |
77 local handle_request; | |
78 local _1, _2, _3; | |
79 local function _handle_request() return handle_request(_1, _2, _3); end | |
80 | |
81 local last_err; | |
82 local function _traceback_handler(err) last_err = err; log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end | |
83 events.add_handler("http-error", function (error) | |
84 return "Error processing request: "..codes[error.code]..". Check your error log for more information."; | |
85 end, -1); | |
86 | |
87 function listener.onconnect(conn) | |
88 local secure = conn:ssl() and true or nil; | |
89 local pending = {}; | |
90 local waiting = false; | |
91 local function process_next() | |
92 --if waiting then log("debug", "can't process_next, waiting"); return; end | |
93 if sessions[conn] and #pending > 0 then | |
94 local request = t_remove(pending); | |
95 --log("debug", "process_next: %s", request.path); | |
96 waiting = true; | |
97 --handle_request(conn, request, process_next); | |
98 _1, _2, _3 = conn, request, process_next; | |
99 if not xpcall(_handle_request, _traceback_handler) then | |
100 conn:write("HTTP/1.0 500 Internal Server Error\r\n\r\n"..events.fire_event("http-error", { code = 500, private_message = last_err })); | |
101 conn:close(); | |
102 end | |
103 else | |
104 --log("debug", "ready for more"); | |
105 waiting = false; | |
106 end | |
107 end | |
108 local function success_cb(request) | |
109 --log("debug", "success_cb: %s", request.path); | |
110 request.secure = secure; | |
111 t_insert(pending, request); | |
112 if not waiting then | |
113 process_next(); | |
114 end | |
115 end | |
116 local function error_cb(err) | |
117 log("debug", "error_cb: %s", err or "<nil>"); | |
118 -- FIXME don't close immediately, wait until we process current stuff | |
119 -- FIXME if err, send off a bad-request response | |
120 sessions[conn] = nil; | |
121 conn:close(); | |
122 end | |
123 sessions[conn] = parser_new(success_cb, error_cb); | |
124 end | |
125 | |
126 function listener.ondisconnect(conn) | |
127 local open_response = conn._http_open_response; | |
128 if open_response and open_response.on_destroy then | |
129 open_response.finished = true; | |
130 open_response:on_destroy(); | |
131 end | |
132 sessions[conn] = nil; | |
133 end | |
134 | |
135 function listener.onincoming(conn, data) | |
136 sessions[conn]:feed(data); | |
137 end | |
138 | |
139 local headerfix = setmetatable({}, { | |
140 __index = function(t, k) | |
141 local v = "\r\n"..k:gsub("_", "-"):gsub("%f[%w].", s_upper)..": "; | |
142 t[k] = v; | |
143 return v; | |
144 end | |
145 }); | |
146 | |
147 function _M.hijack_response(response, listener) | |
148 error("TODO"); | |
149 end | |
150 function handle_request(conn, request, finish_cb) | |
151 --log("debug", "handler: %s", request.path); | |
152 local headers = {}; | |
153 for k,v in pairs(request.headers) do headers[k:gsub("-", "_")] = v; end | |
154 request.headers = headers; | |
155 request.conn = conn; | |
156 | |
157 local date_header = os_date('!%a, %d %b %Y %H:%M:%S GMT'); -- FIXME use | |
158 local conn_header = request.headers.connection; | |
159 local keep_alive = conn_header == "Keep-Alive" or (request.httpversion == "1.1" and conn_header ~= "close"); | |
160 | |
161 local response = { | |
162 request = request; | |
163 status_code = 200; | |
164 headers = { date = date_header, connection = (keep_alive and "Keep-Alive" or "close") }; | |
165 conn = conn; | |
166 send = _M.send_response; | |
167 finish_cb = finish_cb; | |
168 }; | |
169 conn._http_open_response = response; | |
170 | |
171 local host = (request.headers.host or ""):match("[^:]+"); | |
172 | |
173 -- Some sanity checking | |
174 local err_code, err; | |
175 if not request.path then | |
176 err_code, err = 400, "Invalid path"; | |
177 elseif not hosts[host] then | |
178 if hosts[default_host] then | |
179 host = default_host; | |
180 elseif host then | |
181 err_code, err = 404, "Unknown host: "..host; | |
182 else | |
183 err_code, err = 400, "Missing or invalid 'Host' header"; | |
184 end | |
185 end | |
186 | |
187 if err then | |
188 response.status_code = err_code; | |
189 response:send(events.fire_event("http-error", { code = err_code, message = err })); | |
190 return; | |
191 end | |
192 | |
193 local event = request.method.." "..host..request.path:match("[^?]*"); | |
194 local payload = { request = request, response = response }; | |
195 --log("debug", "Firing event: %s", event); | |
196 local result = events.fire_event(event, payload); | |
197 if result ~= nil then | |
198 if result ~= true then | |
199 local body; | |
200 local result_type = type(result); | |
201 if result_type == "number" then | |
202 response.status_code = result; | |
203 if result >= 400 then | |
204 body = events.fire_event("http-error", { code = result }); | |
205 end | |
206 elseif result_type == "string" then | |
207 body = result; | |
208 elseif result_type == "table" then | |
209 for k, v in pairs(result) do | |
210 response[k] = v; | |
211 end | |
212 end | |
213 response:send(body); | |
214 end | |
215 return; | |
216 end | |
217 | |
218 -- if handler not called, return 404 | |
219 response.status_code = 404; | |
220 response:send(events.fire_event("http-error", { code = 404 })); | |
221 end | |
222 function _M.send_response(response, body) | |
223 if response.finished then return; end | |
224 response.finished = true; | |
225 response.conn._http_open_response = nil; | |
226 | |
227 local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); | |
228 local headers = response.headers; | |
229 body = body or response.body or ""; | |
230 headers.content_length = #body; | |
231 | |
232 local output = { status_line }; | |
233 for k,v in pairs(headers) do | |
234 t_insert(output, headerfix[k]..v); | |
235 end | |
236 t_insert(output, "\r\n\r\n"); | |
237 t_insert(output, body); | |
238 | |
239 response.conn:write(t_concat(output)); | |
240 if response.on_destroy then | |
241 response:on_destroy(); | |
242 response.on_destroy = nil; | |
243 end | |
244 if headers.connection == "Keep-Alive" then | |
245 response:finish_cb(); | |
246 else | |
247 response.conn:close(); | |
248 end | |
249 end | |
250 function _M.add_handler(event, handler, priority) | |
251 events.add_handler(event, handler, priority); | |
252 end | |
253 function _M.remove_handler(event, handler) | |
254 events.remove_handler(event, handler); | |
255 end | |
256 | |
257 function _M.listen_on(port, interface, ssl) | |
258 addserver(interface or "*", port, listener, "*a", ssl); | |
259 end | |
260 function _M.add_host(host) | |
261 hosts[host] = true; | |
262 end | |
263 function _M.remove_host(host) | |
264 hosts[host] = nil; | |
265 end | |
266 function _M.set_default_host(host) | |
267 default_host = host; | |
268 end | |
269 | |
270 _M.listener = listener; | |
271 _M.codes = codes; | |
272 _M._events = events; | |
273 return _M; |