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 () |