Software /
code /
prosody
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 |
parent | 8535:b6f3b34ecc03 |
child | 8555: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 |