Mercurial > prosody-hg
comparison net/http.lua @ 8551:2bd2e94a0496
net.http: Refactor to use new net.connect API, brings support for async DNS
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Mon, 26 Feb 2018 15:27:00 +0000 |
| parents | b6f3b34ecc03 |
| children | 4f0f5b49bb03 |
comparison
equal
deleted
inserted
replaced
| 8550:f841d359da65 | 8551:2bd2e94a0496 |
|---|---|
| 11 local httpstream_new = require "net.http.parser".new; | 11 local httpstream_new = require "net.http.parser".new; |
| 12 local util_http = require "util.http"; | 12 local util_http = require "util.http"; |
| 13 local events = require "util.events"; | 13 local events = require "util.events"; |
| 14 local verify_identity = require"util.x509".verify_identity; | 14 local verify_identity = require"util.x509".verify_identity; |
| 15 | 15 |
| 16 local basic_resolver = require "net.resolvers.basic"; | |
| 17 local connect = require "net.connect".connect; | |
| 18 | |
| 16 local ssl_available = pcall(require, "ssl"); | 19 local ssl_available = pcall(require, "ssl"); |
| 17 | |
| 18 local server = require "net.server" | |
| 19 | 20 |
| 20 local t_insert, t_concat = table.insert, table.concat; | 21 local t_insert, t_concat = table.insert, table.concat; |
| 21 local pairs = pairs; | 22 local pairs = pairs; |
| 22 local tonumber, tostring, xpcall, traceback = | 23 local tonumber, tostring, xpcall, traceback = |
| 23 tonumber, tostring, xpcall, debug.traceback; | 24 tonumber, tostring, xpcall, debug.traceback; |
| 31 | 32 |
| 32 local function make_id(req) return (tostring(req):match("%x+$")); end | 33 local function make_id(req) return (tostring(req):match("%x+$")); end |
| 33 | 34 |
| 34 local listener = { default_port = 80, default_mode = "*a" }; | 35 local listener = { default_port = 80, default_mode = "*a" }; |
| 35 | 36 |
| 37 -- Request-related helper functions | |
| 38 local function handleerr(err) log("error", "Traceback[http]: %s", traceback(tostring(err), 2)); end | |
| 39 local function log_if_failed(id, ret, ...) | |
| 40 if not ret then | |
| 41 log("error", "Request '%s': error in callback: %s", id, tostring((...))); | |
| 42 end | |
| 43 return ...; | |
| 44 end | |
| 45 | |
| 46 local function destroy_request(request) | |
| 47 local conn = request.conn; | |
| 48 if conn then | |
| 49 request.conn = nil; | |
| 50 conn:close() | |
| 51 end | |
| 52 end | |
| 53 | |
| 54 local function request_reader(request, data, err) | |
| 55 if not request.parser then | |
| 56 local function error_cb(reason) | |
| 57 if request.callback then | |
| 58 request.callback(reason or "connection-closed", 0, request); | |
| 59 request.callback = nil; | |
| 60 end | |
| 61 destroy_request(request); | |
| 62 end | |
| 63 | |
| 64 if not data then | |
| 65 error_cb(err); | |
| 66 return; | |
| 67 end | |
| 68 | |
| 69 local function success_cb(r) | |
| 70 if request.callback then | |
| 71 request.callback(r.body, r.code, r, request); | |
| 72 request.callback = nil; | |
| 73 end | |
| 74 destroy_request(request); | |
| 75 end | |
| 76 local function options_cb() | |
| 77 return request; | |
| 78 end | |
| 79 request.parser = httpstream_new(success_cb, error_cb, "client", options_cb); | |
| 80 end | |
| 81 request.parser:feed(data); | |
| 82 end | |
| 83 | |
| 84 -- Connection listener callbacks | |
| 36 function listener.onconnect(conn) | 85 function listener.onconnect(conn) |
| 37 local req = requests[conn]; | 86 local req = requests[conn]; |
| 87 | |
| 88 -- Initialize request object | |
| 89 req.write = function (...) return req.conn:write(...); end | |
| 90 local callback = req.callback; | |
| 91 req.callback = function (content, code, response, request) | |
| 92 do | |
| 93 local event = { http = req.http, url = req.url, request = req, response = response, content = content, code = code, callback = req.callback }; | |
| 94 req.http.events.fire_event("response", event); | |
| 95 content, code, response = event.content, event.code, event.response; | |
| 96 end | |
| 97 | |
| 98 log("debug", "Request '%s': Calling callback, status %s", req.id, code or "---"); | |
| 99 return log_if_failed(req.id, xpcall(function () return callback(content, code, request, response) end, handleerr)); | |
| 100 end | |
| 101 req.reader = request_reader; | |
| 102 req.state = "status"; | |
| 103 | |
| 104 requests[req.conn] = req; | |
| 38 | 105 |
| 39 -- Validate certificate | 106 -- Validate certificate |
| 40 if not req.insecure and conn:ssl() then | 107 if not req.insecure and conn:ssl() then |
| 41 local sock = conn:socket(); | 108 local sock = conn:socket(); |
| 42 local chain_valid = sock.getpeerverification and sock:getpeerverification(); | 109 local chain_valid = sock.getpeerverification and sock:getpeerverification(); |
| 93 request:reader(nil, err or "closed"); | 160 request:reader(nil, err or "closed"); |
| 94 end | 161 end |
| 95 requests[conn] = nil; | 162 requests[conn] = nil; |
| 96 end | 163 end |
| 97 | 164 |
| 165 function listener.onattach(conn, req) | |
| 166 requests[conn] = req; | |
| 167 req.conn = conn; | |
| 168 end | |
| 169 | |
| 98 function listener.ondetach(conn) | 170 function listener.ondetach(conn) |
| 99 requests[conn] = nil; | 171 requests[conn] = nil; |
| 100 end | 172 end |
| 101 | 173 |
| 102 local function destroy_request(request) | 174 function listener.onfail(req, reason) |
| 103 local conn = request.conn; | 175 req.http.events.fire_event("request-connection-error", { http = req.http, request = req, url = req.url, err = reason }); |
| 104 if conn then | 176 req.callback(reason or "connection failed", 0, req); |
| 105 request.conn = nil; | |
| 106 conn:close() | |
| 107 end | |
| 108 end | |
| 109 | |
| 110 local function request_reader(request, data, err) | |
| 111 if not request.parser then | |
| 112 local function error_cb(reason) | |
| 113 if request.callback then | |
| 114 request.callback(reason or "connection-closed", 0, request); | |
| 115 request.callback = nil; | |
| 116 end | |
| 117 destroy_request(request); | |
| 118 end | |
| 119 | |
| 120 if not data then | |
| 121 error_cb(err); | |
| 122 return; | |
| 123 end | |
| 124 | |
| 125 local function success_cb(r) | |
| 126 if request.callback then | |
| 127 request.callback(r.body, r.code, r, request); | |
| 128 request.callback = nil; | |
| 129 end | |
| 130 destroy_request(request); | |
| 131 end | |
| 132 local function options_cb() | |
| 133 return request; | |
| 134 end | |
| 135 request.parser = httpstream_new(success_cb, error_cb, "client", options_cb); | |
| 136 end | |
| 137 request.parser:feed(data); | |
| 138 end | |
| 139 | |
| 140 local function handleerr(err) log("error", "Traceback[http]: %s", traceback(tostring(err), 2)); end | |
| 141 local function log_if_failed(id, ret, ...) | |
| 142 if not ret then | |
| 143 log("error", "Request '%s': error in callback: %s", id, tostring((...))); | |
| 144 end | |
| 145 return ...; | |
| 146 end | 177 end |
| 147 | 178 |
| 148 local function request(self, u, ex, callback) | 179 local function request(self, u, ex, callback) |
| 149 local req = url.parse(u); | 180 local req = url.parse(u); |
| 150 req.url = u; | 181 req.url = u; |
| 182 req.http = self; | |
| 151 | 183 |
| 152 if not (req and req.host) then | 184 if not (req and req.host) then |
| 153 callback("invalid-url", 0, req); | 185 callback("invalid-url", 0, req); |
| 154 return nil, "invalid-url"; | 186 return nil, "invalid-url"; |
| 155 end | 187 end |
| 164 local event = { http = self, url = u, request = req, options = ex, callback = callback }; | 196 local event = { http = self, url = u, request = req, options = ex, callback = callback }; |
| 165 local ret = self.events.fire_event("pre-request", event); | 197 local ret = self.events.fire_event("pre-request", event); |
| 166 if ret then | 198 if ret then |
| 167 return ret; | 199 return ret; |
| 168 end | 200 end |
| 169 req, u, ex, callback = event.request, event.url, event.options, event.callback; | 201 req, u, ex, req.callback = event.request, event.url, event.options, event.callback; |
| 170 end | 202 end |
| 171 | 203 |
| 172 local method, headers, body; | 204 local method, headers, body; |
| 173 | 205 |
| 174 local host, port = req.host, req.port; | 206 local host, port = req.host, req.port; |
| 220 local sslctx = false; | 252 local sslctx = false; |
| 221 if using_https then | 253 if using_https then |
| 222 sslctx = ex and ex.sslctx or self.options and self.options.sslctx; | 254 sslctx = ex and ex.sslctx or self.options and self.options.sslctx; |
| 223 end | 255 end |
| 224 | 256 |
| 225 local conn, ret = server.addclient(host, port_number, listener, "*a", sslctx) | 257 local http_service = basic_resolver.new(host, port_number); |
| 226 if not conn then | 258 connect(http_service, listener, { sslctx = sslctx }, req); |
| 227 self.events.fire_event("request-connection-error", { http = self, request = req, url = u, err = ret }); | |
| 228 callback(ret, 0, req); | |
| 229 return nil, ret; | |
| 230 end | |
| 231 req.conn = conn | |
| 232 req.write = function (...) return req.conn:write(...); end | |
| 233 | |
| 234 req.callback = function (content, code, response, request) | |
| 235 do | |
| 236 local event = { http = self, url = u, request = req, response = response, content = content, code = code, callback = callback }; | |
| 237 self.events.fire_event("response", event); | |
| 238 content, code, response = event.content, event.code, event.response; | |
| 239 end | |
| 240 | |
| 241 log("debug", "Request '%s': Calling callback, status %s", req.id, code or "---"); | |
| 242 return log_if_failed(req.id, xpcall(function () return callback(content, code, request, response) end, handleerr)); | |
| 243 end | |
| 244 req.reader = request_reader; | |
| 245 req.state = "status"; | |
| 246 | |
| 247 requests[req.conn] = req; | |
| 248 | 259 |
| 249 self.events.fire_event("request", { http = self, request = req, url = u }); | 260 self.events.fire_event("request", { http = self, request = req, url = u }); |
| 250 return req; | 261 return req; |
| 251 end | 262 end |
| 252 | 263 |
