Software /
code /
prosody
Comparison
net/http/server.lua @ 11200:bf8f2da84007
Merge 0.11->trunk
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Thu, 05 Nov 2020 22:31:25 +0100 |
parent | 11160:e9eeaefa09a7 |
child | 11371:73f7acf8a61f |
comparison
equal
deleted
inserted
replaced
11199:6c7c50a4de32 | 11200:bf8f2da84007 |
---|---|
11 local xpcall = require "util.xpcall".xpcall; | 11 local xpcall = require "util.xpcall".xpcall; |
12 local traceback = debug.traceback; | 12 local traceback = debug.traceback; |
13 local tostring = tostring; | 13 local tostring = tostring; |
14 local cache = require "util.cache"; | 14 local cache = require "util.cache"; |
15 local codes = require "net.http.codes"; | 15 local codes = require "net.http.codes"; |
16 local promise = require "util.promise"; | |
17 local errors = require "util.error"; | |
16 local blocksize = 2^16; | 18 local blocksize = 2^16; |
17 | 19 |
18 local _M = {}; | 20 local _M = {}; |
19 | 21 |
20 local sessions = {}; | 22 local sessions = {}; |
168 t[k] = v; | 170 t[k] = v; |
169 return v; | 171 return v; |
170 end | 172 end |
171 }); | 173 }); |
172 | 174 |
175 local function handle_result(request, response, result) | |
176 if result == nil then | |
177 result = 404; | |
178 end | |
179 | |
180 if result == true then | |
181 return; | |
182 end | |
183 | |
184 local body; | |
185 local result_type = type(result); | |
186 if result_type == "number" then | |
187 response.status_code = result; | |
188 if result >= 400 then | |
189 body = events.fire_event("http-error", { request = request, response = response, code = result }); | |
190 end | |
191 elseif result_type == "string" then | |
192 body = result; | |
193 elseif errors.is_err(result) then | |
194 response.status_code = result.code or 500; | |
195 body = events.fire_event("http-error", { request = request, response = response, code = result.code or 500, error = result }); | |
196 elseif promise.is_promise(result) then | |
197 result:next(function (ret) | |
198 handle_result(request, response, ret); | |
199 end, function (err) | |
200 response.status_code = 500; | |
201 handle_result(request, response, err or 500); | |
202 end); | |
203 return true; | |
204 elseif result_type == "table" then | |
205 for k, v in pairs(result) do | |
206 if k ~= "headers" then | |
207 response[k] = v; | |
208 else | |
209 for header_name, header_value in pairs(v) do | |
210 response.headers[header_name] = header_value; | |
211 end | |
212 end | |
213 end | |
214 end | |
215 return response:send(body); | |
216 end | |
217 | |
173 function _M.hijack_response(response, listener) -- luacheck: ignore | 218 function _M.hijack_response(response, listener) -- luacheck: ignore |
174 error("TODO"); | 219 error("TODO"); |
175 end | 220 end |
176 function handle_request(conn, request, finish_cb) | 221 function handle_request(conn, request, finish_cb) |
177 --log("debug", "handler: %s", request.path); | 222 --log("debug", "handler: %s", request.path); |
192 response_conn_header = "Keep-Alive"; | 237 response_conn_header = "Keep-Alive"; |
193 else | 238 else |
194 response_conn_header = httpversion == "1.1" and "close" or nil | 239 response_conn_header = httpversion == "1.1" and "close" or nil |
195 end | 240 end |
196 | 241 |
242 local is_head_request = request.method == "HEAD"; | |
243 | |
197 local response = { | 244 local response = { |
198 request = request; | 245 request = request; |
246 is_head_request = is_head_request; | |
199 status_code = 200; | 247 status_code = 200; |
200 headers = { date = date_header, connection = response_conn_header }; | 248 headers = { date = date_header, connection = response_conn_header }; |
201 persistent = persistent; | 249 persistent = persistent; |
202 conn = conn; | 250 conn = conn; |
203 send = _M.send_response; | 251 send = _M.send_response; |
225 local global_event = request.method.." "..request.path:match("[^?]*"); | 273 local global_event = request.method.." "..request.path:match("[^?]*"); |
226 | 274 |
227 local payload = { request = request, response = response }; | 275 local payload = { request = request, response = response }; |
228 log("debug", "Firing event: %s", global_event); | 276 log("debug", "Firing event: %s", global_event); |
229 local result = events.fire_event(global_event, payload); | 277 local result = events.fire_event(global_event, payload); |
278 if result == nil and is_head_request then | |
279 local global_head_event = "GET "..request.path:match("[^?]*"); | |
280 log("debug", "Firing event: %s", global_head_event); | |
281 result = events.fire_event(global_head_event, payload); | |
282 end | |
230 if result == nil then | 283 if result == nil then |
231 if not hosts[host] then | 284 if not hosts[host] then |
232 if hosts[default_host] then | 285 if hosts[default_host] then |
233 host = default_host; | 286 host = default_host; |
234 elseif host then | 287 elseif host then |
245 end | 298 end |
246 | 299 |
247 local host_event = request.method.." "..host..request.path:match("[^?]*"); | 300 local host_event = request.method.." "..host..request.path:match("[^?]*"); |
248 log("debug", "Firing event: %s", host_event); | 301 log("debug", "Firing event: %s", host_event); |
249 result = events.fire_event(host_event, payload); | 302 result = events.fire_event(host_event, payload); |
250 end | 303 |
251 if result ~= nil then | 304 if result == nil and is_head_request then |
252 if result ~= true then | 305 local host_head_event = "GET "..host..request.path:match("[^?]*"); |
253 local body; | 306 log("debug", "Firing event: %s", host_head_event); |
254 local result_type = type(result); | 307 result = events.fire_event(host_head_event, payload); |
255 if result_type == "number" then | 308 end |
256 response.status_code = result; | 309 end |
257 if result >= 400 then | 310 |
258 payload.code = result; | 311 return handle_result(request, response, result); |
259 body = events.fire_event("http-error", payload); | 312 end |
260 end | 313 |
261 elseif result_type == "string" then | |
262 body = result; | |
263 elseif result_type == "table" then | |
264 for k, v in pairs(result) do | |
265 if k ~= "headers" then | |
266 response[k] = v; | |
267 else | |
268 for header_name, header_value in pairs(v) do | |
269 response.headers[header_name] = header_value; | |
270 end | |
271 end | |
272 end | |
273 end | |
274 response:send(body); | |
275 end | |
276 return; | |
277 end | |
278 | |
279 -- if handler not called, return 404 | |
280 response.status_code = 404; | |
281 payload.code = 404; | |
282 response:send(events.fire_event("http-error", payload)); | |
283 end | |
284 local function prepare_header(response) | 314 local function prepare_header(response) |
285 local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); | 315 local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); |
286 local headers = response.headers; | 316 local headers = response.headers; |
287 local output = { status_line }; | 317 local output = { status_line }; |
288 for k,v in pairs(headers) do | 318 for k,v in pairs(headers) do |
290 end | 320 end |
291 t_insert(output, "\r\n\r\n"); | 321 t_insert(output, "\r\n\r\n"); |
292 return output; | 322 return output; |
293 end | 323 end |
294 _M.prepare_header = prepare_header; | 324 _M.prepare_header = prepare_header; |
325 function _M.send_head_response(response) | |
326 if response.finished then return; end | |
327 local output = prepare_header(response); | |
328 response.conn:write(t_concat(output)); | |
329 response:done(); | |
330 end | |
295 function _M.send_response(response, body) | 331 function _M.send_response(response, body) |
296 if response.finished then return; end | 332 if response.finished then return; end |
297 body = body or response.body or ""; | 333 body = body or response.body or ""; |
298 -- Per RFC 7230, informational (1xx) and 204 (no content) should have no c-l header | 334 -- Per RFC 7230, informational (1xx) and 204 (no content) should have no c-l header |
299 if response.status_code > 199 and response.status_code ~= 204 then | 335 if response.status_code > 199 and response.status_code ~= 204 then |
300 response.headers.content_length = #body; | 336 response.headers.content_length = ("%d"):format(#body); |
337 end | |
338 if response.is_head_request then | |
339 return _M.send_head_response(response) | |
301 end | 340 end |
302 local output = prepare_header(response); | 341 local output = prepare_header(response); |
303 t_insert(output, body); | 342 t_insert(output, body); |
304 response.conn:write(t_concat(output)); | 343 response.conn:write(t_concat(output)); |
305 response:done(); | 344 response:done(); |
306 end | 345 end |
307 function _M.send_file(response, f) | 346 function _M.send_file(response, f) |
347 if response.is_head_request then | |
348 if f.close then f:close(); end | |
349 return _M.send_head_response(response); | |
350 end | |
308 if response.finished then return; end | 351 if response.finished then return; end |
309 local chunked = not response.headers.content_length; | 352 local chunked = not response.headers.content_length; |
310 if chunked then response.headers.transfer_encoding = "chunked"; end | 353 if chunked then response.headers.transfer_encoding = "chunked"; end |
311 incomplete[response.conn] = response; | 354 incomplete[response.conn] = response; |
312 response._send_more = function () | 355 response._send_more = function () |