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