Crow  0.3
A C++ microframework for the web
http_connection.h
1 #pragma once
2 #include <boost/asio.hpp>
3 #include <boost/algorithm/string/predicate.hpp>
4 #include <boost/lexical_cast.hpp>
5 #include <boost/array.hpp>
6 #include <atomic>
7 #include <chrono>
8 #include <vector>
9 
10 #include "crow/http_parser_merged.h"
11 
12 #include "crow/parser.h"
13 #include "crow/http_response.h"
14 #include "crow/logging.h"
15 #include "crow/settings.h"
16 #include "crow/dumb_timer_queue.h"
17 #include "crow/middleware_context.h"
18 #include "crow/socket_adaptors.h"
19 #include "crow/compression.h"
20 
21 namespace crow
22 {
23  using namespace boost;
24  using tcp = asio::ip::tcp;
25 
26  namespace detail
27  {
28  template <typename MW>
30  {
31  template <typename T,
32  void (T::*)(request&, response&, typename MW::context&) const = &T::before_handle
33  >
34  struct get
35  { };
36  };
37 
38  template <typename MW>
40  {
41  template <typename T,
42  void (T::*)(request&, response&, typename MW::context&) = &T::before_handle
43  >
44  struct get
45  { };
46  };
47 
48  template <typename MW>
50  {
51  template <typename T,
52  void (T::*)(request&, response&, typename MW::context&) const = &T::after_handle
53  >
54  struct get
55  { };
56  };
57 
58  template <typename MW>
60  {
61  template <typename T,
62  void (T::*)(request&, response&, typename MW::context&) = &T::after_handle
63  >
64  struct get
65  { };
66  };
67 
68  template <typename T>
70  {
71  template <typename C>
72  static std::true_type f(typename check_before_handle_arity_3_const<T>::template get<C>*);
73 
74  template <typename C>
75  static std::true_type f(typename check_before_handle_arity_3<T>::template get<C>*);
76 
77  template <typename C>
78  static std::false_type f(...);
79 
80  public:
81  static const bool value = decltype(f<T>(nullptr))::value;
82  };
83 
84  template <typename T>
86  {
87  template <typename C>
88  static std::true_type f(typename check_after_handle_arity_3_const<T>::template get<C>*);
89 
90  template <typename C>
91  static std::true_type f(typename check_after_handle_arity_3<T>::template get<C>*);
92 
93  template <typename C>
94  static std::false_type f(...);
95 
96  public:
97  static const bool value = decltype(f<T>(nullptr))::value;
98  };
99 
100  template <typename MW, typename Context, typename ParentContext>
101  typename std::enable_if<!is_before_handle_arity_3_impl<MW>::value>::type
102  before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
103  {
104  mw.before_handle(req, res, ctx.template get<MW>(), ctx);
105  }
106 
107  template <typename MW, typename Context, typename ParentContext>
108  typename std::enable_if<is_before_handle_arity_3_impl<MW>::value>::type
109  before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
110  {
111  mw.before_handle(req, res, ctx.template get<MW>());
112  }
113 
114  template <typename MW, typename Context, typename ParentContext>
115  typename std::enable_if<!is_after_handle_arity_3_impl<MW>::value>::type
116  after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
117  {
118  mw.after_handle(req, res, ctx.template get<MW>(), ctx);
119  }
120 
121  template <typename MW, typename Context, typename ParentContext>
122  typename std::enable_if<is_after_handle_arity_3_impl<MW>::value>::type
123  after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
124  {
125  mw.after_handle(req, res, ctx.template get<MW>());
126  }
127 
128  template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares>
129  bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx)
130  {
131  using parent_context_t = typename Context::template partial<N-1>;
132  before_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
133 
134  if (res.is_completed())
135  {
136  after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
137  return true;
138  }
139 
140  if (middleware_call_helper<N+1, Context, Container, Middlewares...>(middlewares, req, res, ctx))
141  {
142  after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
143  return true;
144  }
145 
146  return false;
147  }
148 
149  template <int N, typename Context, typename Container>
150  bool middleware_call_helper(Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/)
151  {
152  return false;
153  }
154 
155  template <int N, typename Context, typename Container>
156  typename std::enable_if<(N<0)>::type
157  after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/)
158  {
159  }
160 
161  template <int N, typename Context, typename Container>
162  typename std::enable_if<(N==0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
163  {
164  using parent_context_t = typename Context::template partial<N-1>;
165  using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
166  after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
167  }
168 
169  template <int N, typename Context, typename Container>
170  typename std::enable_if<(N>0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
171  {
172  using parent_context_t = typename Context::template partial<N-1>;
173  using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
174  after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
175  after_handlers_call_helper<N-1, Context, Container>(middlewares, ctx, req, res);
176  }
177  }
178 
179 #ifdef CROW_ENABLE_DEBUG
180  static std::atomic<int> connectionCount;
181 #endif
182 
183  /// An HTTP connection.
184  template <typename Adaptor, typename Handler, typename ... Middlewares>
186  {
187  friend struct crow::response;
188  public:
189  Connection(
190  boost::asio::io_service& io_service,
191  Handler* handler,
192  const std::string& server_name,
193  std::tuple<Middlewares...>* middlewares,
194  std::function<std::string()>& get_cached_date_str_f,
195  detail::dumb_timer_queue& timer_queue,
196  typename Adaptor::context* adaptor_ctx_
197  )
198  : adaptor_(io_service, adaptor_ctx_),
199  handler_(handler),
200  parser_(this),
201  server_name_(server_name),
202  middlewares_(middlewares),
203  get_cached_date_str(get_cached_date_str_f),
204  timer_queue(timer_queue)
205  {
206 #ifdef CROW_ENABLE_DEBUG
207  connectionCount ++;
208  CROW_LOG_DEBUG << "Connection open, total " << connectionCount << ", " << this;
209 #endif
210  }
211 
212  ~Connection()
213  {
214  res.complete_request_handler_ = nullptr;
215  cancel_deadline_timer();
216 #ifdef CROW_ENABLE_DEBUG
217  connectionCount --;
218  CROW_LOG_DEBUG << "Connection closed, total " << connectionCount << ", " << this;
219 #endif
220  }
221 
222  /// The TCP socket on top of which the connection is established.
223  decltype(std::declval<Adaptor>().raw_socket())& socket()
224  {
225  return adaptor_.raw_socket();
226  }
227 
228  void start()
229  {
230  adaptor_.start([this](const boost::system::error_code& ec) {
231  if (!ec)
232  {
233  start_deadline();
234 
235  do_read();
236  }
237  else
238  {
239  check_destroy();
240  }
241  });
242  }
243 
244  void handle_header()
245  {
246  // HTTP 1.1 Expect: 100-continue
247  if (parser_.check_version(1, 1) && parser_.headers.count("expect") && get_header_value(parser_.headers, "expect") == "100-continue")
248  {
249  buffers_.clear();
250  static std::string expect_100_continue = "HTTP/1.1 100 Continue\r\n\r\n";
251  buffers_.emplace_back(expect_100_continue.data(), expect_100_continue.size());
252  do_write();
253  }
254  }
255 
256  void handle()
257  {
258  cancel_deadline_timer();
259  bool is_invalid_request = false;
260  add_keep_alive_ = false;
261 
262  req_ = std::move(parser_.to_request());
263  request& req = req_;
264 
265  req.remoteIpAddress = adaptor_.remote_endpoint().address().to_string();
266 
267  if (parser_.check_version(1, 0))
268  {
269  // HTTP/1.0
270  if (req.headers.count("connection"))
271  {
272  if (boost::iequals(req.get_header_value("connection"),"Keep-Alive"))
273  add_keep_alive_ = true;
274  }
275  else
276  close_connection_ = true;
277  }
278  else if (parser_.check_version(1, 1))
279  {
280  // HTTP/1.1
281  if (req.headers.count("connection"))
282  {
283  if (req.get_header_value("connection") == "close")
284  close_connection_ = true;
285  else if (boost::iequals(req.get_header_value("connection"),"Keep-Alive"))
286  add_keep_alive_ = true;
287  }
288  if (!req.headers.count("host"))
289  {
290  is_invalid_request = true;
291  res = response(400);
292  }
293  if (parser_.is_upgrade())
294  {
295  if (req.get_header_value("upgrade") == "h2c")
296  {
297  // TODO HTTP/2
298  // currently, ignore upgrade header
299  }
300  else
301  {
302  close_connection_ = true;
303  handler_->handle_upgrade(req, res, std::move(adaptor_));
304  return;
305  }
306  }
307  }
308 
309  CROW_LOG_INFO << "Request: " << boost::lexical_cast<std::string>(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << parser_.http_major << "." << parser_.http_minor << ' '
310  << method_name(req.method) << " " << req.url;
311 
312 
313  need_to_call_after_handlers_ = false;
314  if (!is_invalid_request)
315  {
316  res.complete_request_handler_ = []{};
317  res.is_alive_helper_ = [this]()->bool{ return adaptor_.is_open(); };
318 
319  ctx_ = detail::context<Middlewares...>();
320  req.middleware_context = static_cast<void*>(&ctx_);
321  req.io_service = &adaptor_.get_io_service();
322  detail::middleware_call_helper<0, decltype(ctx_), decltype(*middlewares_), Middlewares...>(*middlewares_, req, res, ctx_);
323 
324  if (!res.completed_)
325  {
326  res.complete_request_handler_ = [this]{ this->complete_request(); };
327  need_to_call_after_handlers_ = true;
328  handler_->handle(req, res);
329  if (add_keep_alive_)
330  res.set_header("connection", "Keep-Alive");
331  }
332  else
333  {
334  complete_request();
335  }
336  }
337  else
338  {
339  complete_request();
340  }
341  }
342 
343  /// Call the after handle middleware and send the write the response to the connection.
345  {
346  CROW_LOG_INFO << "Response: " << this << ' ' << req_.raw_url << ' ' << res.code << ' ' << close_connection_;
347 
348  if (need_to_call_after_handlers_)
349  {
350  need_to_call_after_handlers_ = false;
351 
352  // call all after_handler of middlewares
353  detail::after_handlers_call_helper<
354  (static_cast<int>(sizeof...(Middlewares))-1),
355  decltype(ctx_),
356  decltype(*middlewares_)>
357  (*middlewares_, ctx_, req_, res);
358  }
359 #ifdef CROW_ENABLE_COMPRESSION
360  std::string accept_encoding = req_.get_header_value("Accept-Encoding");
361  if (!accept_encoding.empty() && res.compressed)
362  {
363  switch (handler_->compression_algorithm())
364  {
365  case compression::DEFLATE:
366  if (accept_encoding.find("deflate") != std::string::npos)
367  {
368  res.body = compression::compress_string(res.body, compression::algorithm::DEFLATE);
369  res.set_header("Content-Encoding", "deflate");
370  }
371  break;
372  case compression::GZIP:
373  if (accept_encoding.find("gzip") != std::string::npos)
374  {
375  res.body = compression::compress_string(res.body, compression::algorithm::GZIP);
376  res.set_header("Content-Encoding", "gzip");
377  }
378  break;
379  default:
380  break;
381  }
382  }
383 #endif
384  //if there is a redirection with a partial URL, treat the URL as a route.
385  std::string location = res.get_header_value("Location");
386  if (!location.empty() && location.find("://", 0) == std::string::npos)
387  {
388  #ifdef CROW_ENABLE_SSL
389  location.insert(0, "https://" + req_.get_header_value("Host"));
390  #else
391  location.insert(0, "http://" + req_.get_header_value("Host"));
392  #endif
393  res.set_header("location", location);
394  }
395 
396  prepare_buffers();
397 
398  if (res.is_static_type())
399  {
400  do_write_static();
401  }else {
402  do_write_general();
403  }
404 
405  }
406 
407  private:
408 
409  void prepare_buffers()
410  {
411  //auto self = this->shared_from_this();
412  res.complete_request_handler_ = nullptr;
413 
414  if (!adaptor_.is_open())
415  {
416  //CROW_LOG_DEBUG << this << " delete (socket is closed) " << is_reading << ' ' << is_writing;
417  //delete this;
418  return;
419  }
420 
421  static std::unordered_map<int, std::string> statusCodes = {
422  {200, "HTTP/1.1 200 OK\r\n"},
423  {201, "HTTP/1.1 201 Created\r\n"},
424  {202, "HTTP/1.1 202 Accepted\r\n"},
425  {204, "HTTP/1.1 204 No Content\r\n"},
426 
427  {300, "HTTP/1.1 300 Multiple Choices\r\n"},
428  {301, "HTTP/1.1 301 Moved Permanently\r\n"},
429  {302, "HTTP/1.1 302 Found\r\n"},
430  {303, "HTTP/1.1 303 See Other\r\n"},
431  {304, "HTTP/1.1 304 Not Modified\r\n"},
432  {307, "HTTP/1.1 307 Temporary Redirect\r\n"},
433  {308, "HTTP/1.1 308 Permanent Redirect\r\n"},
434 
435  {400, "HTTP/1.1 400 Bad Request\r\n"},
436  {401, "HTTP/1.1 401 Unauthorized\r\n"},
437  {403, "HTTP/1.1 403 Forbidden\r\n"},
438  {404, "HTTP/1.1 404 Not Found\r\n"},
439  {405, "HTTP/1.1 405 Method Not Allowed\r\n"},
440  {413, "HTTP/1.1 413 Payload Too Large\r\n"},
441  {422, "HTTP/1.1 422 Unprocessable Entity\r\n"},
442  {429, "HTTP/1.1 429 Too Many Requests\r\n"},
443 
444  {500, "HTTP/1.1 500 Internal Server Error\r\n"},
445  {501, "HTTP/1.1 501 Not Implemented\r\n"},
446  {502, "HTTP/1.1 502 Bad Gateway\r\n"},
447  {503, "HTTP/1.1 503 Service Unavailable\r\n"},
448  };
449 
450  static std::string seperator = ": ";
451  static std::string crlf = "\r\n";
452 
453  buffers_.clear();
454  buffers_.reserve(4*(res.headers.size()+5)+3);
455 
456  if (!statusCodes.count(res.code))
457  res.code = 500;
458  {
459  auto& status = statusCodes.find(res.code)->second;
460  buffers_.emplace_back(status.data(), status.size());
461  }
462 
463  if (res.code >= 400 && res.body.empty())
464  res.body = statusCodes[res.code].substr(9);
465 
466  for(auto& kv : res.headers)
467  {
468  buffers_.emplace_back(kv.first.data(), kv.first.size());
469  buffers_.emplace_back(seperator.data(), seperator.size());
470  buffers_.emplace_back(kv.second.data(), kv.second.size());
471  buffers_.emplace_back(crlf.data(), crlf.size());
472 
473  }
474 
475  if (!res.manual_length_header && !res.headers.count("content-length"))
476  {
477  content_length_ = std::to_string(res.body.size());
478  static std::string content_length_tag = "Content-Length: ";
479  buffers_.emplace_back(content_length_tag.data(), content_length_tag.size());
480  buffers_.emplace_back(content_length_.data(), content_length_.size());
481  buffers_.emplace_back(crlf.data(), crlf.size());
482  }
483  if (!res.headers.count("server"))
484  {
485  static std::string server_tag = "Server: ";
486  buffers_.emplace_back(server_tag.data(), server_tag.size());
487  buffers_.emplace_back(server_name_.data(), server_name_.size());
488  buffers_.emplace_back(crlf.data(), crlf.size());
489  }
490  if (!res.headers.count("date"))
491  {
492  static std::string date_tag = "Date: ";
493  date_str_ = get_cached_date_str();
494  buffers_.emplace_back(date_tag.data(), date_tag.size());
495  buffers_.emplace_back(date_str_.data(), date_str_.size());
496  buffers_.emplace_back(crlf.data(), crlf.size());
497  }
498  if (add_keep_alive_)
499  {
500  static std::string keep_alive_tag = "Connection: Keep-Alive";
501  buffers_.emplace_back(keep_alive_tag.data(), keep_alive_tag.size());
502  buffers_.emplace_back(crlf.data(), crlf.size());
503  }
504 
505  buffers_.emplace_back(crlf.data(), crlf.size());
506 
507  }
508 
509  void do_write_static()
510  {
511  is_writing = true;
512  boost::asio::write(adaptor_.socket(), buffers_);
513  res.do_stream_file(adaptor_);
514 
515  res.end();
516  res.clear();
517  buffers_.clear();
518  }
519 
520  void do_write_general()
521  {
522  if (res.body.length() < res_stream_threshold_)
523  {
524  res_body_copy_.swap(res.body);
525  buffers_.emplace_back(res_body_copy_.data(), res_body_copy_.size());
526 
527  do_write();
528 
529  if (need_to_start_read_after_complete_)
530  {
531  need_to_start_read_after_complete_ = false;
532  start_deadline();
533  do_read();
534  }
535  }
536  else
537  {
538  is_writing = true;
539  boost::asio::write(adaptor_.socket(), buffers_);
540  res.do_stream_body(adaptor_);
541 
542  res.end();
543  res.clear();
544  buffers_.clear();
545  }
546  }
547 
548  void do_read()
549  {
550  //auto self = this->shared_from_this();
551  is_reading = true;
552  adaptor_.socket().async_read_some(boost::asio::buffer(buffer_),
553  [this](const boost::system::error_code& ec, std::size_t bytes_transferred)
554  {
555  bool error_while_reading = true;
556  if (!ec)
557  {
558  bool ret = parser_.feed(buffer_.data(), bytes_transferred);
559  if (ret && adaptor_.is_open())
560  {
561  error_while_reading = false;
562  }
563  }
564 
565  if (error_while_reading)
566  {
567  cancel_deadline_timer();
568  parser_.done();
569  adaptor_.shutdown_read();
570  adaptor_.close();
571  is_reading = false;
572  CROW_LOG_DEBUG << this << " from read(1)";
573  check_destroy();
574  }
575  else if (close_connection_)
576  {
577  cancel_deadline_timer();
578  parser_.done();
579  is_reading = false;
580  check_destroy();
581  // adaptor will close after write
582  }
583  else if (!need_to_call_after_handlers_)
584  {
585  start_deadline();
586  do_read();
587  }
588  else
589  {
590  // res will be completed later by user
591  need_to_start_read_after_complete_ = true;
592  }
593  });
594  }
595 
596  void do_write()
597  {
598  //auto self = this->shared_from_this();
599  is_writing = true;
600  boost::asio::async_write(adaptor_.socket(), buffers_,
601  [&](const boost::system::error_code& ec, std::size_t /*bytes_transferred*/)
602  {
603  is_writing = false;
604  res.clear();
605  res_body_copy_.clear();
606  if (!ec)
607  {
608  if (close_connection_)
609  {
610  adaptor_.shutdown_write();
611  adaptor_.close();
612  CROW_LOG_DEBUG << this << " from write(1)";
613  check_destroy();
614  }
615  }
616  else
617  {
618  CROW_LOG_DEBUG << this << " from write(2)";
619  check_destroy();
620  }
621  });
622  }
623 
624  void check_destroy()
625  {
626  CROW_LOG_DEBUG << this << " is_reading " << is_reading << " is_writing " << is_writing;
627  if (!is_reading && !is_writing)
628  {
629  CROW_LOG_DEBUG << this << " delete (idle) ";
630  delete this;
631  }
632  }
633 
634  void cancel_deadline_timer()
635  {
636  CROW_LOG_DEBUG << this << " timer cancelled: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second;
637  timer_queue.cancel(timer_cancel_key_);
638  }
639 
640  void start_deadline(/*int timeout = 5*/)
641  {
642  cancel_deadline_timer();
643 
644  timer_cancel_key_ = timer_queue.add([this]
645  {
646  if (!adaptor_.is_open())
647  {
648  return;
649  }
650  adaptor_.shutdown_readwrite();
651  adaptor_.close();
652  });
653  CROW_LOG_DEBUG << this << " timer added: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second;
654  }
655 
656  private:
657  Adaptor adaptor_;
658  Handler* handler_;
659 
660  boost::array<char, 4096> buffer_;
661 
662  const unsigned res_stream_threshold_ = 1048576;
663 
664  HTTPParser<Connection> parser_;
665  request req_;
666  response res;
667 
668  bool close_connection_ = false;
669 
670  const std::string& server_name_;
671  std::vector<boost::asio::const_buffer> buffers_;
672 
673  std::string content_length_;
674  std::string date_str_;
675  std::string res_body_copy_;
676 
677  //boost::asio::deadline_timer deadline_;
678  detail::dumb_timer_queue::key timer_cancel_key_;
679 
680  bool is_reading{};
681  bool is_writing{};
682  bool need_to_call_after_handlers_{};
683  bool need_to_start_read_after_complete_{};
684  bool add_keep_alive_{};
685 
686  std::tuple<Middlewares...>* middlewares_;
687  detail::context<Middlewares...> ctx_;
688 
689  std::function<std::string()>& get_cached_date_str;
690  detail::dumb_timer_queue& timer_queue;
691  };
692 
693 }
crow::detail::is_before_handle_arity_3_impl
Definition: http_connection.h:69
crow::detail::check_before_handle_arity_3
Definition: http_connection.h:39
crow::detail::check_after_handle_arity_3_const
Definition: http_connection.h:49
crow::detail::check_after_handle_arity_3_const::get
Definition: http_connection.h:54
crow::detail::check_before_handle_arity_3_const::get
Definition: http_connection.h:34
crow::detail::dumb_timer_queue::add
key add(std::function< void()> f)
Add a function to the queue.
Definition: dumb_timer_queue.h:36
crow::detail::is_after_handle_arity_3_impl
Definition: http_connection.h:85
crow::detail::check_after_handle_arity_3::get
Definition: http_connection.h:64
crow::detail::check_before_handle_arity_3::get
Definition: http_connection.h:44
crow::request
An HTTP request.
Definition: http_request.h:26
crow::Connection
An HTTP connection.
Definition: http_connection.h:185
crow::Connection::complete_request
void complete_request()
Call the after handle middleware and send the write the response to the connection.
Definition: http_connection.h:344
crow::detail::dumb_timer_queue
Fast timer queue for fixed tick value.
Definition: dumb_timer_queue.h:17
crow::response
HTTP response.
Definition: http_response.h:23
crow::detail::check_after_handle_arity_3
Definition: http_connection.h:59
crow::Connection::socket
decltype(std::declval< Adaptor >().raw_socket()) & socket()
The TCP socket on top of which the connection is established.
Definition: http_connection.h:223
crow::detail::check_before_handle_arity_3_const
Definition: http_connection.h:29