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