1 /*
2 Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 /**
26 * API Facade around libevent's http interface
27 */
28 #include "mysqlrouter/http_common.h"
29
30 #include <cstring>
31 #include <iostream>
32 #include <sstream>
33 #include <stack>
34 #include <stdexcept>
35
36 #include <event2/buffer.h>
37 #include <event2/event.h>
38 #include <event2/http.h>
39 #include <event2/http_struct.h>
40 #include <event2/keyvalq_struct.h>
41 #include <event2/util.h>
42
43 #include "http_request_impl.h"
44 #include "mysql/harness/utility/string.h"
45
46 // wrap evhttp_uri
47
48 struct HttpUri::impl {
49 std::unique_ptr<evhttp_uri, std::function<void(evhttp_uri *)>> uri;
50 };
51
HttpUri()52 HttpUri::HttpUri() : pImpl_(new impl{{evhttp_uri_new(), &evhttp_uri_free}}) {}
53
HttpUri(std::unique_ptr<evhttp_uri,std::function<void (evhttp_uri *)>> uri)54 HttpUri::HttpUri(
55 std::unique_ptr<evhttp_uri, std::function<void(evhttp_uri *)>> uri) {
56 pImpl_.reset(new impl{std::move(uri)});
57 }
58
59 HttpUri::HttpUri(HttpUri &&) = default;
~HttpUri()60 HttpUri::~HttpUri() {}
61
operator bool() const62 HttpUri::operator bool() const { return pImpl_->uri.operator bool(); }
63
parse(const std::string & uri_str)64 HttpUri HttpUri::parse(const std::string &uri_str) {
65 // wrap a owned pointer
66 return HttpUri{std::unique_ptr<evhttp_uri, decltype(&evhttp_uri_free)>{
67 evhttp_uri_parse(uri_str.c_str()), &evhttp_uri_free}};
68 }
69
get_scheme() const70 std::string HttpUri::get_scheme() const {
71 const char *const u = evhttp_uri_get_scheme(pImpl_->uri.get());
72
73 return u != nullptr ? u : "";
74 }
75
set_scheme(const std::string & scheme)76 void HttpUri::set_scheme(const std::string &scheme) {
77 evhttp_uri_set_scheme(pImpl_->uri.get(), scheme.c_str());
78 }
79
get_userinfo() const80 std::string HttpUri::get_userinfo() const {
81 const char *const u = evhttp_uri_get_userinfo(pImpl_->uri.get());
82
83 return u != nullptr ? u : "";
84 }
set_userinfo(const std::string & userinfo)85 void HttpUri::set_userinfo(const std::string &userinfo) {
86 evhttp_uri_set_userinfo(pImpl_->uri.get(), userinfo.c_str());
87 }
88
get_host() const89 std::string HttpUri::get_host() const {
90 const char *const u = evhttp_uri_get_host(pImpl_->uri.get());
91
92 return u != nullptr ? u : "";
93 }
94
set_host(const std::string & host)95 void HttpUri::set_host(const std::string &host) {
96 evhttp_uri_set_host(pImpl_->uri.get(), host.c_str());
97 }
98
get_port() const99 uint16_t HttpUri::get_port() const {
100 return evhttp_uri_get_port(pImpl_->uri.get());
101 }
102
set_port(uint16_t port) const103 void HttpUri::set_port(uint16_t port) const {
104 evhttp_uri_set_port(pImpl_->uri.get(), port);
105 }
106
get_path() const107 std::string HttpUri::get_path() const {
108 const char *const u = evhttp_uri_get_path(pImpl_->uri.get());
109
110 return u != nullptr ? u : "";
111 }
112
set_path(const std::string & path)113 void HttpUri::set_path(const std::string &path) {
114 if (0 != evhttp_uri_set_path(pImpl_->uri.get(), path.c_str())) {
115 throw std::invalid_argument("URL path isn't valid: " + path);
116 }
117 }
118
get_fragment() const119 std::string HttpUri::get_fragment() const {
120 const char *const u = evhttp_uri_get_fragment(pImpl_->uri.get());
121
122 return u != nullptr ? u : "";
123 }
set_fragment(const std::string & fragment)124 void HttpUri::set_fragment(const std::string &fragment) {
125 evhttp_uri_set_fragment(pImpl_->uri.get(), fragment.c_str());
126 }
127
get_query() const128 std::string HttpUri::get_query() const {
129 const char *const u = evhttp_uri_get_query(pImpl_->uri.get());
130
131 return u != nullptr ? u : "";
132 }
set_query(const std::string & query)133 void HttpUri::set_query(const std::string &query) {
134 evhttp_uri_set_query(pImpl_->uri.get(), query.c_str());
135 }
136
join() const137 std::string HttpUri::join() const {
138 char buf[16 * 1024];
139 if (evhttp_uri_join(pImpl_->uri.get(), buf, sizeof(buf))) {
140 return buf;
141 }
142
143 throw std::invalid_argument("join failed");
144 }
145
http_uri_path_canonicalize(const std::string & uri_path)146 std::string http_uri_path_canonicalize(const std::string &uri_path) {
147 if (uri_path.empty()) return "/";
148
149 std::deque<std::string> sections;
150
151 std::istringstream ss(uri_path);
152 for (std::string section; std::getline(ss, section, '/');) {
153 if (section == "..") {
154 // remove last item on the stack
155 if (!sections.empty()) {
156 sections.pop_back();
157 }
158 } else if (section != "." && !section.empty()) {
159 sections.emplace_back(section);
160 }
161 }
162
163 bool has_trailing_slash = uri_path.back() == '/';
164 if (has_trailing_slash) sections.emplace_back("");
165
166 auto out = "/" + mysql_harness::join(sections, "/");
167
168 return out;
169 }
170
171 // wrap evbuffer
172
173 struct HttpBuffer::impl {
174 std::unique_ptr<evbuffer, std::function<void(evbuffer *)>> buffer;
175 };
176
177 // non-owning pointer
HttpBuffer(std::unique_ptr<evbuffer,std::function<void (evbuffer *)>> buffer)178 HttpBuffer::HttpBuffer(
179 std::unique_ptr<evbuffer, std::function<void(evbuffer *)>> buffer) {
180 pImpl_.reset(new impl{std::move(buffer)});
181 }
182
add(const char * data,size_t data_size)183 void HttpBuffer::add(const char *data, size_t data_size) {
184 evbuffer_add(pImpl_->buffer.get(), data, data_size);
185 }
186
add_file(int file_fd,off_t offset,off_t size)187 void HttpBuffer::add_file(int file_fd, off_t offset, off_t size) {
188 evbuffer_add_file(pImpl_->buffer.get(), file_fd, offset, size);
189 }
190
length() const191 size_t HttpBuffer::length() const {
192 return evbuffer_get_length(pImpl_->buffer.get());
193 }
194
pop_front(size_t len)195 std::vector<uint8_t> HttpBuffer::pop_front(size_t len) {
196 std::vector<uint8_t> data;
197 data.resize(len);
198
199 int bytes_read;
200 if (-1 == (bytes_read = evbuffer_remove(pImpl_->buffer.get(), data.data(),
201 data.size()))) {
202 throw std::runtime_error("couldn't pop bytes from front of buffer");
203 }
204
205 data.resize(bytes_read);
206 data.shrink_to_fit();
207
208 return data;
209 }
210
211 HttpBuffer::HttpBuffer(HttpBuffer &&) = default;
~HttpBuffer()212 HttpBuffer::~HttpBuffer() {}
213
214 // wrap evkeyvalq
215
216 struct HttpHeaders::impl {
217 std::unique_ptr<evkeyvalq, std::function<void(evkeyvalq *)>> hdrs;
218 };
219
HttpHeaders(std::unique_ptr<evkeyvalq,std::function<void (evkeyvalq *)>> hdrs)220 HttpHeaders::HttpHeaders(
221 std::unique_ptr<evkeyvalq, std::function<void(evkeyvalq *)>> hdrs) {
222 pImpl_.reset(new impl{std::move(hdrs)});
223 }
224
add(const char * key,const char * value)225 int HttpHeaders::add(const char *key, const char *value) {
226 return evhttp_add_header(pImpl_->hdrs.get(), key, value);
227 }
228
get(const char * key) const229 const char *HttpHeaders::get(const char *key) const {
230 return evhttp_find_header(pImpl_->hdrs.get(), key);
231 }
232
operator *()233 std::pair<std::string, std::string> HttpHeaders::Iterator::operator*() {
234 return {node_->key, node_->value};
235 }
236
operator ++()237 HttpHeaders::Iterator &HttpHeaders::Iterator::operator++() {
238 node_ = node_->next.tqe_next;
239
240 return *this;
241 }
242
operator !=(const Iterator & it) const243 bool HttpHeaders::Iterator::operator!=(const Iterator &it) const {
244 return node_ != it.node_;
245 }
246
begin()247 HttpHeaders::Iterator HttpHeaders::begin() { return pImpl_->hdrs->tqh_first; }
248
end()249 HttpHeaders::Iterator HttpHeaders::end() { return *(pImpl_->hdrs->tqh_last); }
250
251 HttpHeaders::HttpHeaders(HttpHeaders &&) = default;
~HttpHeaders()252 HttpHeaders::~HttpHeaders() {}
253
254 // wrap evhttp_request
255
HttpRequest(std::unique_ptr<evhttp_request,std::function<void (evhttp_request *)>> req)256 HttpRequest::HttpRequest(
257 std::unique_ptr<evhttp_request, std::function<void(evhttp_request *)>>
258 req) {
259 pImpl_.reset(new impl{std::move(req)});
260 }
261
262 struct RequestHandlerCtx {
263 HttpRequest *req;
264 HttpRequest::RequestHandler cb;
265 void *cb_data;
266 };
267
sync_callback(HttpRequest * req,void *)268 void HttpRequest::sync_callback(HttpRequest *req, void *) {
269 // if connection was successful, keep the request-object alive past this
270 // request-handler lifetime
271 evhttp_request *ev_req = req->pImpl_->req.get();
272 if (ev_req) {
273 #if LIBEVENT_VERSION_NUMBER >= 0x02010600
274 evhttp_request_own(ev_req);
275 #else
276 // swap the evhttp_request, as evhttp_request_own() is broken
277 // before libevent 2.1.6.
278 //
279 // see: https://github.com/libevent/libevent/issues/68
280 //
281 // libevent will free the event, let's make sure it only sees
282 // an empty evhttp_request
283 auto *copied_req = evhttp_request_new(nullptr, nullptr);
284
285 std::swap(copied_req->evcon, ev_req->evcon);
286 std::swap(copied_req->flags, ev_req->flags);
287
288 std::swap(copied_req->input_headers, ev_req->input_headers);
289 std::swap(copied_req->output_headers, ev_req->output_headers);
290 std::swap(copied_req->remote_host, ev_req->remote_host);
291 std::swap(copied_req->remote_port, ev_req->remote_port);
292 std::swap(copied_req->host_cache, ev_req->host_cache);
293 std::swap(copied_req->kind, ev_req->kind);
294 std::swap(copied_req->type, ev_req->type);
295 std::swap(copied_req->headers_size, ev_req->headers_size);
296 std::swap(copied_req->body_size, ev_req->body_size);
297 std::swap(copied_req->uri, ev_req->uri);
298 std::swap(copied_req->uri_elems, ev_req->uri_elems);
299 std::swap(copied_req->major, ev_req->major);
300 std::swap(copied_req->minor, ev_req->minor);
301 std::swap(copied_req->response_code, ev_req->response_code);
302 std::swap(copied_req->response_code_line, ev_req->response_code_line);
303 std::swap(copied_req->input_buffer, ev_req->input_buffer);
304 std::swap(copied_req->ntoread, ev_req->ntoread);
305 copied_req->chunked = ev_req->chunked;
306 copied_req->userdone = ev_req->userdone;
307 std::swap(copied_req->output_buffer, ev_req->output_buffer);
308
309 // release the old one, and let the event-loop free it
310 req->pImpl_->req.release();
311
312 // but take ownership of the new one
313 req->pImpl_->req.reset(copied_req);
314 req->pImpl_->own();
315 #endif
316 }
317 }
318
HttpRequest(HttpRequest::RequestHandler cb,void * cb_arg)319 HttpRequest::HttpRequest(HttpRequest::RequestHandler cb, void *cb_arg) {
320 auto *ev_req = evhttp_request_new(
321 [](evhttp_request *req, void *ev_cb_arg) {
322 auto *ctx = static_cast<RequestHandlerCtx *>(ev_cb_arg);
323
324 if ((req == nullptr) && (errno != 0)) {
325 // request failed. Try to capture the last errno and hope
326 // it is related to the failure
327 ctx->req->socket_error_code({errno, std::system_category()});
328 }
329
330 ctx->req->pImpl_->req.release(); // the old request object may already
331 // be free()ed in case of error
332 ctx->req->pImpl_->req.reset(req); // just update with what we have
333 ctx->cb(ctx->req, ctx->cb_data);
334
335 delete ctx;
336 },
337 new RequestHandlerCtx{this, cb, cb_arg});
338
339 #if LIBEVENT_VERSION_NUMBER >= 0x02010000
340 evhttp_request_set_error_cb(
341 ev_req, [](evhttp_request_error err_code, void *ev_cb_arg) {
342 auto *ctx = static_cast<RequestHandlerCtx *>(ev_cb_arg);
343
344 ctx->req->error_code(err_code);
345 });
346 #endif
347
348 pImpl_.reset(new impl{
349 std::unique_ptr<evhttp_request, std::function<void(evhttp_request *)>>(
350 ev_req, evhttp_request_free)});
351 }
352
HttpRequest(HttpRequest && rhs)353 HttpRequest::HttpRequest(HttpRequest &&rhs) : pImpl_{std::move(rhs.pImpl_)} {}
354
~HttpRequest()355 HttpRequest::~HttpRequest() {}
356
socket_error_code(std::error_code error_code)357 void HttpRequest::socket_error_code(std::error_code error_code) {
358 pImpl_->socket_error_code_ = error_code;
359 }
360
socket_error_code() const361 std::error_code HttpRequest::socket_error_code() const {
362 return pImpl_->socket_error_code_;
363 }
364
send_error(int status_code,std::string status_text)365 void HttpRequest::send_error(int status_code, std::string status_text) {
366 evhttp_send_error(pImpl_->req.get(), status_code, status_text.c_str());
367 }
368
send_reply(int status_code,std::string status_text,HttpBuffer & chunk)369 void HttpRequest::send_reply(int status_code, std::string status_text,
370 HttpBuffer &chunk) {
371 evhttp_send_reply(pImpl_->req.get(), status_code, status_text.c_str(),
372 chunk.pImpl_->buffer.get());
373 }
374
send_reply(int status_code,std::string status_text)375 void HttpRequest::send_reply(int status_code, std::string status_text) {
376 evhttp_send_reply(pImpl_->req.get(), status_code, status_text.c_str(),
377 nullptr);
378 }
379
operator bool() const380 HttpRequest::operator bool() const { return pImpl_->req.operator bool(); }
381
error_code(int err_code)382 void HttpRequest::error_code(int err_code) { pImpl_->error_code = err_code; }
383
error_code()384 int HttpRequest::error_code() { return pImpl_->error_code; }
385
error_msg()386 std::string HttpRequest::error_msg() {
387 switch (pImpl_->error_code) {
388 #if LIBEVENT_VERSION_NUMBER >= 0x02010000
389 case EVREQ_HTTP_TIMEOUT:
390 return "timeout";
391 case EVREQ_HTTP_EOF:
392 return "eof";
393 case EVREQ_HTTP_INVALID_HEADER:
394 return "invalid-header";
395 case EVREQ_HTTP_BUFFER_ERROR:
396 return "buffer-error";
397 case EVREQ_HTTP_REQUEST_CANCEL:
398 return "request-cancel";
399 case EVREQ_HTTP_DATA_TOO_LONG:
400 return "data-too-long";
401 #endif
402 default:
403 return "unknown";
404 }
405 }
406
get_uri() const407 HttpUri HttpRequest::get_uri() const {
408 // return a wrapper around a borrowed evhttp_uri
409 //
410 // it is owned by the HttpRequest, not by the HttpUri itself
411 return std::unique_ptr<evhttp_uri, std::function<void(evhttp_uri *)>>(
412 const_cast<evhttp_uri *>(
413 evhttp_request_get_evhttp_uri(pImpl_->req.get())),
414 [](evhttp_uri *) {});
415 }
416
get_output_headers()417 HttpHeaders HttpRequest::get_output_headers() {
418 auto *ev_req = pImpl_->req.get();
419
420 if (nullptr == ev_req) {
421 throw std::logic_error("request is null");
422 }
423 // wrap a non-owned pointer
424 return std::unique_ptr<evkeyvalq, std::function<void(evkeyvalq *)>>(
425 evhttp_request_get_output_headers(ev_req), [](evkeyvalq *) {});
426 }
427
get_input_headers() const428 HttpHeaders HttpRequest::get_input_headers() const {
429 auto *ev_req = pImpl_->req.get();
430
431 if (nullptr == ev_req) {
432 throw std::logic_error("request is null");
433 }
434 // wrap a non-owned pointer
435 return std::unique_ptr<evkeyvalq, std::function<void(evkeyvalq *)>>(
436 evhttp_request_get_input_headers(ev_req), [](evkeyvalq *) {});
437 }
438
get_output_buffer()439 HttpBuffer HttpRequest::get_output_buffer() {
440 // wrap a non-owned pointer
441 auto *ev_req = pImpl_->req.get();
442
443 if (nullptr == ev_req) {
444 throw std::logic_error("request is null");
445 }
446
447 return std::unique_ptr<evbuffer, std::function<void(evbuffer *)>>(
448 evhttp_request_get_output_buffer(ev_req), [](evbuffer *) {});
449 }
450
get_response_code() const451 unsigned HttpRequest::get_response_code() const {
452 auto *ev_req = pImpl_->req.get();
453
454 if (nullptr == ev_req) {
455 throw std::logic_error("request is null");
456 }
457
458 return evhttp_request_get_response_code(ev_req);
459 }
460
get_response_code_line() const461 std::string HttpRequest::get_response_code_line() const {
462 auto *ev_req = pImpl_->req.get();
463 if (nullptr == ev_req) {
464 throw std::logic_error("request is null");
465 }
466
467 #if LIBEVENT_VERSION_NUMBER >= 0x02010000
468 return evhttp_request_get_response_code_line(ev_req);
469 #else
470 return HttpStatusCode::get_default_status_text(
471 evhttp_request_get_response_code(ev_req));
472 #endif
473 }
474
get_input_buffer() const475 HttpBuffer HttpRequest::get_input_buffer() const {
476 auto *ev_req = pImpl_->req.get();
477
478 if (nullptr == ev_req) {
479 throw std::logic_error("request is null");
480 }
481
482 // wrap a non-owned pointer
483 return std::unique_ptr<evbuffer, std::function<void(evbuffer *)>>(
484 evhttp_request_get_input_buffer(ev_req), [](evbuffer *) {});
485 }
486
get_method() const487 HttpMethod::type HttpRequest::get_method() const {
488 return evhttp_request_get_command(pImpl_->req.get());
489 }
490
is_modified_since(time_t last_modified)491 bool HttpRequest::is_modified_since(time_t last_modified) {
492 auto req_hdrs = get_input_headers();
493
494 auto *if_mod_since = req_hdrs.get("If-Modified-Since");
495 if (if_mod_since != nullptr) {
496 try {
497 time_t if_mod_since_ts = time_from_rfc5322_fixdate(if_mod_since);
498
499 if (!(last_modified > if_mod_since_ts)) {
500 return false;
501 }
502 } catch (const std::exception &) {
503 return false;
504 }
505 }
506 return true;
507 }
508
add_last_modified(time_t last_modified)509 bool HttpRequest::add_last_modified(time_t last_modified) {
510 auto out_hdrs = get_output_headers();
511 char date_buf[50];
512
513 if (sizeof(date_buf) -
514 time_to_rfc5322_fixdate(last_modified, date_buf, sizeof(date_buf)) >
515 0) {
516 out_hdrs.add("Last-Modified", date_buf);
517
518 return true;
519 } else {
520 return false;
521 }
522 }
523