diff --git a/master/reference/http__connection_8h_source.html b/master/reference/http__connection_8h_source.html index 743a6ff1e..2fa8ad390 100644 --- a/master/reference/http__connection_8h_source.html +++ b/master/reference/http__connection_8h_source.html @@ -359,381 +359,369 @@
258  }
259  }
260 #endif
-
261  //if there is a redirection with a partial URL, treat the URL as a route.
-
262  std::string location = res.get_header_value("Location");
-
263  if (!location.empty() && location.find("://", 0) == std::string::npos)
-
264  {
-
265 #ifdef CROW_ENABLE_SSL
-
266  if (handler_->ssl_used())
-
267  location.insert(0, "https://" + req_.get_header_value("Host"));
-
268  else
-
269 #endif
-
270  location.insert(0, "http://" + req_.get_header_value("Host"));
-
271  res.set_header("location", location);
-
272  }
+
261 
+
262  prepare_buffers();
+
263 
+
264  if (res.is_static_type())
+
265  {
+
266  do_write_static();
+
267  }
+
268  else
+
269  {
+
270  do_write_general();
+
271  }
+
272  }
273 
-
274  prepare_buffers();
-
275 
-
276  if (res.is_static_type())
-
277  {
-
278  do_write_static();
-
279  }
-
280  else
+
274  private:
+
275  void prepare_buffers()
+
276  {
+
277  res.complete_request_handler_ = nullptr;
+
278  res.is_alive_helper_ = nullptr;
+
279 
+
280  if (!adaptor_.is_open())
281  {
-
282  do_write_general();
-
283  }
-
284  }
-
285 
-
286  private:
-
287  void prepare_buffers()
-
288  {
-
289  res.complete_request_handler_ = nullptr;
-
290  res.is_alive_helper_ = nullptr;
+
282  //CROW_LOG_DEBUG << this << " delete (socket is closed) " << is_reading << ' ' << is_writing;
+
283  //delete this;
+
284  return;
+
285  }
+
286  // TODO(EDev): HTTP version in status codes should be dynamic
+
287  // Keep in sync with common.h/status
+
288  static std::unordered_map<int, std::string> statusCodes = {
+
289  {status::CONTINUE, "HTTP/1.1 100 Continue\r\n"},
+
290  {status::SWITCHING_PROTOCOLS, "HTTP/1.1 101 Switching Protocols\r\n"},
291 
-
292  if (!adaptor_.is_open())
-
293  {
-
294  //CROW_LOG_DEBUG << this << " delete (socket is closed) " << is_reading << ' ' << is_writing;
-
295  //delete this;
-
296  return;
-
297  }
-
298  // TODO(EDev): HTTP version in status codes should be dynamic
-
299  // Keep in sync with common.h/status
-
300  static std::unordered_map<int, std::string> statusCodes = {
-
301  {status::CONTINUE, "HTTP/1.1 100 Continue\r\n"},
-
302  {status::SWITCHING_PROTOCOLS, "HTTP/1.1 101 Switching Protocols\r\n"},
-
303 
-
304  {status::OK, "HTTP/1.1 200 OK\r\n"},
-
305  {status::CREATED, "HTTP/1.1 201 Created\r\n"},
-
306  {status::ACCEPTED, "HTTP/1.1 202 Accepted\r\n"},
-
307  {status::NON_AUTHORITATIVE_INFORMATION, "HTTP/1.1 203 Non-Authoritative Information\r\n"},
-
308  {status::NO_CONTENT, "HTTP/1.1 204 No Content\r\n"},
-
309  {status::RESET_CONTENT, "HTTP/1.1 205 Reset Content\r\n"},
-
310  {status::PARTIAL_CONTENT, "HTTP/1.1 206 Partial Content\r\n"},
-
311 
-
312  {status::MULTIPLE_CHOICES, "HTTP/1.1 300 Multiple Choices\r\n"},
-
313  {status::MOVED_PERMANENTLY, "HTTP/1.1 301 Moved Permanently\r\n"},
-
314  {status::FOUND, "HTTP/1.1 302 Found\r\n"},
-
315  {status::SEE_OTHER, "HTTP/1.1 303 See Other\r\n"},
-
316  {status::NOT_MODIFIED, "HTTP/1.1 304 Not Modified\r\n"},
-
317  {status::TEMPORARY_REDIRECT, "HTTP/1.1 307 Temporary Redirect\r\n"},
-
318  {status::PERMANENT_REDIRECT, "HTTP/1.1 308 Permanent Redirect\r\n"},
-
319 
-
320  {status::BAD_REQUEST, "HTTP/1.1 400 Bad Request\r\n"},
-
321  {status::UNAUTHORIZED, "HTTP/1.1 401 Unauthorized\r\n"},
-
322  {status::FORBIDDEN, "HTTP/1.1 403 Forbidden\r\n"},
-
323  {status::NOT_FOUND, "HTTP/1.1 404 Not Found\r\n"},
-
324  {status::METHOD_NOT_ALLOWED, "HTTP/1.1 405 Method Not Allowed\r\n"},
-
325  {status::NOT_ACCEPTABLE, "HTTP/1.1 406 Not Acceptable\r\n"},
-
326  {status::PROXY_AUTHENTICATION_REQUIRED, "HTTP/1.1 407 Proxy Authentication Required\r\n"},
-
327  {status::CONFLICT, "HTTP/1.1 409 Conflict\r\n"},
-
328  {status::GONE, "HTTP/1.1 410 Gone\r\n"},
-
329  {status::PAYLOAD_TOO_LARGE, "HTTP/1.1 413 Payload Too Large\r\n"},
-
330  {status::UNSUPPORTED_MEDIA_TYPE, "HTTP/1.1 415 Unsupported Media Type\r\n"},
-
331  {status::RANGE_NOT_SATISFIABLE, "HTTP/1.1 416 Range Not Satisfiable\r\n"},
-
332  {status::EXPECTATION_FAILED, "HTTP/1.1 417 Expectation Failed\r\n"},
-
333  {status::PRECONDITION_REQUIRED, "HTTP/1.1 428 Precondition Required\r\n"},
-
334  {status::TOO_MANY_REQUESTS, "HTTP/1.1 429 Too Many Requests\r\n"},
-
335  {status::UNAVAILABLE_FOR_LEGAL_REASONS, "HTTP/1.1 451 Unavailable For Legal Reasons\r\n"},
-
336 
-
337  {status::INTERNAL_SERVER_ERROR, "HTTP/1.1 500 Internal Server Error\r\n"},
-
338  {status::NOT_IMPLEMENTED, "HTTP/1.1 501 Not Implemented\r\n"},
-
339  {status::BAD_GATEWAY, "HTTP/1.1 502 Bad Gateway\r\n"},
-
340  {status::SERVICE_UNAVAILABLE, "HTTP/1.1 503 Service Unavailable\r\n"},
-
341  {status::GATEWAY_TIMEOUT, "HTTP/1.1 504 Gateway Timeout\r\n"},
-
342  {status::VARIANT_ALSO_NEGOTIATES, "HTTP/1.1 506 Variant Also Negotiates\r\n"},
-
343  };
-
344 
-
345  static const std::string seperator = ": ";
-
346 
-
347  buffers_.clear();
-
348  buffers_.reserve(4 * (res.headers.size() + 5) + 3);
-
349 
-
350  if (!statusCodes.count(res.code))
-
351  {
-
352  CROW_LOG_WARNING << this << " status code "
-
353  << "(" << res.code << ")"
-
354  << " not defined, returning 500 instead";
-
355  res.code = 500;
-
356  }
-
357 
-
358  auto& status = statusCodes.find(res.code)->second;
-
359  buffers_.emplace_back(status.data(), status.size());
-
360 
-
361  if (res.code >= 400 && res.body.empty())
-
362  res.body = statusCodes[res.code].substr(9);
-
363 
-
364  for (auto& kv : res.headers)
-
365  {
-
366  buffers_.emplace_back(kv.first.data(), kv.first.size());
-
367  buffers_.emplace_back(seperator.data(), seperator.size());
-
368  buffers_.emplace_back(kv.second.data(), kv.second.size());
-
369  buffers_.emplace_back(crlf.data(), crlf.size());
-
370  }
-
371 
-
372  if (!res.manual_length_header && !res.headers.count("content-length"))
-
373  {
-
374  content_length_ = std::to_string(res.body.size());
-
375  static std::string content_length_tag = "Content-Length: ";
-
376  buffers_.emplace_back(content_length_tag.data(), content_length_tag.size());
-
377  buffers_.emplace_back(content_length_.data(), content_length_.size());
-
378  buffers_.emplace_back(crlf.data(), crlf.size());
-
379  }
-
380  if (!res.headers.count("server"))
-
381  {
-
382  static std::string server_tag = "Server: ";
-
383  buffers_.emplace_back(server_tag.data(), server_tag.size());
-
384  buffers_.emplace_back(server_name_.data(), server_name_.size());
-
385  buffers_.emplace_back(crlf.data(), crlf.size());
-
386  }
-
387  if (!res.headers.count("date"))
-
388  {
-
389  static std::string date_tag = "Date: ";
-
390  date_str_ = get_cached_date_str();
-
391  buffers_.emplace_back(date_tag.data(), date_tag.size());
-
392  buffers_.emplace_back(date_str_.data(), date_str_.size());
-
393  buffers_.emplace_back(crlf.data(), crlf.size());
-
394  }
-
395  if (add_keep_alive_)
-
396  {
-
397  static std::string keep_alive_tag = "Connection: Keep-Alive";
-
398  buffers_.emplace_back(keep_alive_tag.data(), keep_alive_tag.size());
-
399  buffers_.emplace_back(crlf.data(), crlf.size());
-
400  }
-
401 
-
402  buffers_.emplace_back(crlf.data(), crlf.size());
-
403  }
-
404 
-
405  void do_write_static()
-
406  {
-
407  asio::write(adaptor_.socket(), buffers_);
-
408 
-
409  if (res.file_info.statResult == 0)
-
410  {
-
411  std::ifstream is(res.file_info.path.c_str(), std::ios::in | std::ios::binary);
-
412  std::vector<asio::const_buffer> buffers{1};
-
413  char buf[16384];
-
414  is.read(buf, sizeof(buf));
-
415  while (is.gcount() > 0)
-
416  {
-
417  buffers[0] = asio::buffer(buf, is.gcount());
-
418  do_write_sync(buffers);
-
419  is.read(buf, sizeof(buf));
-
420  }
-
421  }
-
422  if (close_connection_)
-
423  {
-
424  adaptor_.shutdown_readwrite();
-
425  adaptor_.close();
-
426  CROW_LOG_DEBUG << this << " from write (static)";
-
427  }
-
428 
-
429  res.end();
-
430  res.clear();
-
431  buffers_.clear();
-
432  parser_.clear();
-
433  }
-
434 
-
435  void do_write_general()
-
436  {
-
437  if (res.body.length() < res_stream_threshold_)
-
438  {
-
439  res_body_copy_.swap(res.body);
-
440  buffers_.emplace_back(res_body_copy_.data(), res_body_copy_.size());
-
441 
-
442  do_write();
-
443 
-
444  if (need_to_start_read_after_complete_)
-
445  {
-
446  need_to_start_read_after_complete_ = false;
-
447  start_deadline();
-
448  do_read();
-
449  }
-
450  }
-
451  else
-
452  {
-
453  asio::write(adaptor_.socket(), buffers_); // Write the response start / headers
-
454  cancel_deadline_timer();
-
455  if (res.body.length() > 0)
-
456  {
-
457  std::vector<asio::const_buffer> buffers{1};
-
458  const uint8_t* data = reinterpret_cast<const uint8_t*>(res.body.data());
-
459  size_t length = res.body.length();
-
460  for (size_t transferred = 0; transferred < length;)
-
461  {
-
462  size_t to_transfer = CROW_MIN(16384UL, length - transferred);
-
463  buffers[0] = asio::const_buffer(data + transferred, to_transfer);
-
464  do_write_sync(buffers);
-
465  transferred += to_transfer;
-
466  }
-
467  }
-
468  if (close_connection_)
-
469  {
-
470  adaptor_.shutdown_readwrite();
-
471  adaptor_.close();
-
472  CROW_LOG_DEBUG << this << " from write (res_stream)";
-
473  }
-
474 
-
475  res.end();
-
476  res.clear();
-
477  buffers_.clear();
-
478  parser_.clear();
-
479  }
-
480  }
-
481 
-
482  void do_read()
-
483  {
-
484  auto self = this->shared_from_this();
-
485  adaptor_.socket().async_read_some(
-
486  asio::buffer(buffer_),
-
487  [self](const error_code& ec, std::size_t bytes_transferred) {
-
488  bool error_while_reading = true;
-
489  if (!ec)
-
490  {
-
491  bool ret = self->parser_.feed(self->buffer_.data(), bytes_transferred);
-
492  if (ret && self->adaptor_.is_open())
-
493  {
-
494  error_while_reading = false;
-
495  }
-
496  }
-
497 
-
498  if (error_while_reading)
-
499  {
-
500  self->cancel_deadline_timer();
-
501  self->parser_.done();
-
502  self->adaptor_.shutdown_read();
-
503  self->adaptor_.close();
-
504  CROW_LOG_DEBUG << self << " from read(1) with description: \"" << http_errno_description(static_cast<http_errno>(self->parser_.http_errno)) << '\"';
-
505  }
-
506  else if (self->close_connection_)
-
507  {
-
508  self->cancel_deadline_timer();
-
509  self->parser_.done();
-
510  // adaptor will close after write
-
511  }
-
512  else if (!self->need_to_call_after_handlers_)
-
513  {
-
514  self->start_deadline();
-
515  self->do_read();
-
516  }
-
517  else
-
518  {
-
519  // res will be completed later by user
-
520  self->need_to_start_read_after_complete_ = true;
-
521  }
-
522  });
-
523  }
-
524 
-
525  void do_write()
-
526  {
-
527  auto self = this->shared_from_this();
-
528  asio::async_write(
-
529  adaptor_.socket(), buffers_,
-
530  [self](const error_code& ec, std::size_t /*bytes_transferred*/) {
-
531  self->res.clear();
-
532  self->res_body_copy_.clear();
-
533  if (!self->continue_requested)
-
534  {
-
535  self->parser_.clear();
-
536  }
-
537  else
-
538  {
-
539  self->continue_requested = false;
-
540  }
-
541 
-
542  if (!ec)
-
543  {
-
544  if (self->close_connection_)
-
545  {
-
546  self->adaptor_.shutdown_write();
-
547  self->adaptor_.close();
-
548  CROW_LOG_DEBUG << self << " from write(1)";
-
549  }
-
550  }
-
551  else
-
552  {
-
553  CROW_LOG_DEBUG << self << " from write(2)";
-
554  }
-
555  });
-
556  }
-
557 
-
558  inline void do_write_sync(std::vector<asio::const_buffer>& buffers)
-
559  {
-
560 
-
561  asio::write(adaptor_.socket(), buffers, [&](error_code ec, std::size_t) {
-
562  if (!ec)
-
563  {
-
564  return false;
-
565  }
-
566  else
-
567  {
-
568  CROW_LOG_ERROR << ec << " - happened while sending buffers";
-
569  CROW_LOG_DEBUG << this << " from write (sync)(2)";
-
570  return true;
-
571  }
-
572  });
-
573  }
-
574 
-
575  void cancel_deadline_timer()
-
576  {
-
577  CROW_LOG_DEBUG << this << " timer cancelled: " << &task_timer_ << ' ' << task_id_;
-
578  task_timer_.cancel(task_id_);
-
579  }
-
580 
-
581  void start_deadline(/*int timeout = 5*/)
-
582  {
-
583  cancel_deadline_timer();
+
292  {status::OK, "HTTP/1.1 200 OK\r\n"},
+
293  {status::CREATED, "HTTP/1.1 201 Created\r\n"},
+
294  {status::ACCEPTED, "HTTP/1.1 202 Accepted\r\n"},
+
295  {status::NON_AUTHORITATIVE_INFORMATION, "HTTP/1.1 203 Non-Authoritative Information\r\n"},
+
296  {status::NO_CONTENT, "HTTP/1.1 204 No Content\r\n"},
+
297  {status::RESET_CONTENT, "HTTP/1.1 205 Reset Content\r\n"},
+
298  {status::PARTIAL_CONTENT, "HTTP/1.1 206 Partial Content\r\n"},
+
299 
+
300  {status::MULTIPLE_CHOICES, "HTTP/1.1 300 Multiple Choices\r\n"},
+
301  {status::MOVED_PERMANENTLY, "HTTP/1.1 301 Moved Permanently\r\n"},
+
302  {status::FOUND, "HTTP/1.1 302 Found\r\n"},
+
303  {status::SEE_OTHER, "HTTP/1.1 303 See Other\r\n"},
+
304  {status::NOT_MODIFIED, "HTTP/1.1 304 Not Modified\r\n"},
+
305  {status::TEMPORARY_REDIRECT, "HTTP/1.1 307 Temporary Redirect\r\n"},
+
306  {status::PERMANENT_REDIRECT, "HTTP/1.1 308 Permanent Redirect\r\n"},
+
307 
+
308  {status::BAD_REQUEST, "HTTP/1.1 400 Bad Request\r\n"},
+
309  {status::UNAUTHORIZED, "HTTP/1.1 401 Unauthorized\r\n"},
+
310  {status::FORBIDDEN, "HTTP/1.1 403 Forbidden\r\n"},
+
311  {status::NOT_FOUND, "HTTP/1.1 404 Not Found\r\n"},
+
312  {status::METHOD_NOT_ALLOWED, "HTTP/1.1 405 Method Not Allowed\r\n"},
+
313  {status::NOT_ACCEPTABLE, "HTTP/1.1 406 Not Acceptable\r\n"},
+
314  {status::PROXY_AUTHENTICATION_REQUIRED, "HTTP/1.1 407 Proxy Authentication Required\r\n"},
+
315  {status::CONFLICT, "HTTP/1.1 409 Conflict\r\n"},
+
316  {status::GONE, "HTTP/1.1 410 Gone\r\n"},
+
317  {status::PAYLOAD_TOO_LARGE, "HTTP/1.1 413 Payload Too Large\r\n"},
+
318  {status::UNSUPPORTED_MEDIA_TYPE, "HTTP/1.1 415 Unsupported Media Type\r\n"},
+
319  {status::RANGE_NOT_SATISFIABLE, "HTTP/1.1 416 Range Not Satisfiable\r\n"},
+
320  {status::EXPECTATION_FAILED, "HTTP/1.1 417 Expectation Failed\r\n"},
+
321  {status::PRECONDITION_REQUIRED, "HTTP/1.1 428 Precondition Required\r\n"},
+
322  {status::TOO_MANY_REQUESTS, "HTTP/1.1 429 Too Many Requests\r\n"},
+
323  {status::UNAVAILABLE_FOR_LEGAL_REASONS, "HTTP/1.1 451 Unavailable For Legal Reasons\r\n"},
+
324 
+
325  {status::INTERNAL_SERVER_ERROR, "HTTP/1.1 500 Internal Server Error\r\n"},
+
326  {status::NOT_IMPLEMENTED, "HTTP/1.1 501 Not Implemented\r\n"},
+
327  {status::BAD_GATEWAY, "HTTP/1.1 502 Bad Gateway\r\n"},
+
328  {status::SERVICE_UNAVAILABLE, "HTTP/1.1 503 Service Unavailable\r\n"},
+
329  {status::GATEWAY_TIMEOUT, "HTTP/1.1 504 Gateway Timeout\r\n"},
+
330  {status::VARIANT_ALSO_NEGOTIATES, "HTTP/1.1 506 Variant Also Negotiates\r\n"},
+
331  };
+
332 
+
333  static const std::string seperator = ": ";
+
334 
+
335  buffers_.clear();
+
336  buffers_.reserve(4 * (res.headers.size() + 5) + 3);
+
337 
+
338  if (!statusCodes.count(res.code))
+
339  {
+
340  CROW_LOG_WARNING << this << " status code "
+
341  << "(" << res.code << ")"
+
342  << " not defined, returning 500 instead";
+
343  res.code = 500;
+
344  }
+
345 
+
346  auto& status = statusCodes.find(res.code)->second;
+
347  buffers_.emplace_back(status.data(), status.size());
+
348 
+
349  if (res.code >= 400 && res.body.empty())
+
350  res.body = statusCodes[res.code].substr(9);
+
351 
+
352  for (auto& kv : res.headers)
+
353  {
+
354  buffers_.emplace_back(kv.first.data(), kv.first.size());
+
355  buffers_.emplace_back(seperator.data(), seperator.size());
+
356  buffers_.emplace_back(kv.second.data(), kv.second.size());
+
357  buffers_.emplace_back(crlf.data(), crlf.size());
+
358  }
+
359 
+
360  if (!res.manual_length_header && !res.headers.count("content-length"))
+
361  {
+
362  content_length_ = std::to_string(res.body.size());
+
363  static std::string content_length_tag = "Content-Length: ";
+
364  buffers_.emplace_back(content_length_tag.data(), content_length_tag.size());
+
365  buffers_.emplace_back(content_length_.data(), content_length_.size());
+
366  buffers_.emplace_back(crlf.data(), crlf.size());
+
367  }
+
368  if (!res.headers.count("server"))
+
369  {
+
370  static std::string server_tag = "Server: ";
+
371  buffers_.emplace_back(server_tag.data(), server_tag.size());
+
372  buffers_.emplace_back(server_name_.data(), server_name_.size());
+
373  buffers_.emplace_back(crlf.data(), crlf.size());
+
374  }
+
375  if (!res.headers.count("date"))
+
376  {
+
377  static std::string date_tag = "Date: ";
+
378  date_str_ = get_cached_date_str();
+
379  buffers_.emplace_back(date_tag.data(), date_tag.size());
+
380  buffers_.emplace_back(date_str_.data(), date_str_.size());
+
381  buffers_.emplace_back(crlf.data(), crlf.size());
+
382  }
+
383  if (add_keep_alive_)
+
384  {
+
385  static std::string keep_alive_tag = "Connection: Keep-Alive";
+
386  buffers_.emplace_back(keep_alive_tag.data(), keep_alive_tag.size());
+
387  buffers_.emplace_back(crlf.data(), crlf.size());
+
388  }
+
389 
+
390  buffers_.emplace_back(crlf.data(), crlf.size());
+
391  }
+
392 
+
393  void do_write_static()
+
394  {
+
395  asio::write(adaptor_.socket(), buffers_);
+
396 
+
397  if (res.file_info.statResult == 0)
+
398  {
+
399  std::ifstream is(res.file_info.path.c_str(), std::ios::in | std::ios::binary);
+
400  std::vector<asio::const_buffer> buffers{1};
+
401  char buf[16384];
+
402  is.read(buf, sizeof(buf));
+
403  while (is.gcount() > 0)
+
404  {
+
405  buffers[0] = asio::buffer(buf, is.gcount());
+
406  do_write_sync(buffers);
+
407  is.read(buf, sizeof(buf));
+
408  }
+
409  }
+
410  if (close_connection_)
+
411  {
+
412  adaptor_.shutdown_readwrite();
+
413  adaptor_.close();
+
414  CROW_LOG_DEBUG << this << " from write (static)";
+
415  }
+
416 
+
417  res.end();
+
418  res.clear();
+
419  buffers_.clear();
+
420  parser_.clear();
+
421  }
+
422 
+
423  void do_write_general()
+
424  {
+
425  if (res.body.length() < res_stream_threshold_)
+
426  {
+
427  res_body_copy_.swap(res.body);
+
428  buffers_.emplace_back(res_body_copy_.data(), res_body_copy_.size());
+
429 
+
430  do_write();
+
431 
+
432  if (need_to_start_read_after_complete_)
+
433  {
+
434  need_to_start_read_after_complete_ = false;
+
435  start_deadline();
+
436  do_read();
+
437  }
+
438  }
+
439  else
+
440  {
+
441  asio::write(adaptor_.socket(), buffers_); // Write the response start / headers
+
442  cancel_deadline_timer();
+
443  if (res.body.length() > 0)
+
444  {
+
445  std::vector<asio::const_buffer> buffers{1};
+
446  const uint8_t* data = reinterpret_cast<const uint8_t*>(res.body.data());
+
447  size_t length = res.body.length();
+
448  for (size_t transferred = 0; transferred < length;)
+
449  {
+
450  size_t to_transfer = CROW_MIN(16384UL, length - transferred);
+
451  buffers[0] = asio::const_buffer(data + transferred, to_transfer);
+
452  do_write_sync(buffers);
+
453  transferred += to_transfer;
+
454  }
+
455  }
+
456  if (close_connection_)
+
457  {
+
458  adaptor_.shutdown_readwrite();
+
459  adaptor_.close();
+
460  CROW_LOG_DEBUG << this << " from write (res_stream)";
+
461  }
+
462 
+
463  res.end();
+
464  res.clear();
+
465  buffers_.clear();
+
466  parser_.clear();
+
467  }
+
468  }
+
469 
+
470  void do_read()
+
471  {
+
472  auto self = this->shared_from_this();
+
473  adaptor_.socket().async_read_some(
+
474  asio::buffer(buffer_),
+
475  [self](const error_code& ec, std::size_t bytes_transferred) {
+
476  bool error_while_reading = true;
+
477  if (!ec)
+
478  {
+
479  bool ret = self->parser_.feed(self->buffer_.data(), bytes_transferred);
+
480  if (ret && self->adaptor_.is_open())
+
481  {
+
482  error_while_reading = false;
+
483  }
+
484  }
+
485 
+
486  if (error_while_reading)
+
487  {
+
488  self->cancel_deadline_timer();
+
489  self->parser_.done();
+
490  self->adaptor_.shutdown_read();
+
491  self->adaptor_.close();
+
492  CROW_LOG_DEBUG << self << " from read(1) with description: \"" << http_errno_description(static_cast<http_errno>(self->parser_.http_errno)) << '\"';
+
493  }
+
494  else if (self->close_connection_)
+
495  {
+
496  self->cancel_deadline_timer();
+
497  self->parser_.done();
+
498  // adaptor will close after write
+
499  }
+
500  else if (!self->need_to_call_after_handlers_)
+
501  {
+
502  self->start_deadline();
+
503  self->do_read();
+
504  }
+
505  else
+
506  {
+
507  // res will be completed later by user
+
508  self->need_to_start_read_after_complete_ = true;
+
509  }
+
510  });
+
511  }
+
512 
+
513  void do_write()
+
514  {
+
515  auto self = this->shared_from_this();
+
516  asio::async_write(
+
517  adaptor_.socket(), buffers_,
+
518  [self](const error_code& ec, std::size_t /*bytes_transferred*/) {
+
519  self->res.clear();
+
520  self->res_body_copy_.clear();
+
521  if (!self->continue_requested)
+
522  {
+
523  self->parser_.clear();
+
524  }
+
525  else
+
526  {
+
527  self->continue_requested = false;
+
528  }
+
529 
+
530  if (!ec)
+
531  {
+
532  if (self->close_connection_)
+
533  {
+
534  self->adaptor_.shutdown_write();
+
535  self->adaptor_.close();
+
536  CROW_LOG_DEBUG << self << " from write(1)";
+
537  }
+
538  }
+
539  else
+
540  {
+
541  CROW_LOG_DEBUG << self << " from write(2)";
+
542  }
+
543  });
+
544  }
+
545 
+
546  inline void do_write_sync(std::vector<asio::const_buffer>& buffers)
+
547  {
+
548 
+
549  asio::write(adaptor_.socket(), buffers, [&](error_code ec, std::size_t) {
+
550  if (!ec)
+
551  {
+
552  return false;
+
553  }
+
554  else
+
555  {
+
556  CROW_LOG_ERROR << ec << " - happened while sending buffers";
+
557  CROW_LOG_DEBUG << this << " from write (sync)(2)";
+
558  return true;
+
559  }
+
560  });
+
561  }
+
562 
+
563  void cancel_deadline_timer()
+
564  {
+
565  CROW_LOG_DEBUG << this << " timer cancelled: " << &task_timer_ << ' ' << task_id_;
+
566  task_timer_.cancel(task_id_);
+
567  }
+
568 
+
569  void start_deadline(/*int timeout = 5*/)
+
570  {
+
571  cancel_deadline_timer();
+
572 
+
573  auto self = this->shared_from_this();
+
574  task_id_ = task_timer_.schedule([self] {
+
575  if (!self->adaptor_.is_open())
+
576  {
+
577  return;
+
578  }
+
579  self->adaptor_.shutdown_readwrite();
+
580  self->adaptor_.close();
+
581  });
+
582  CROW_LOG_DEBUG << this << " timer added: " << &task_timer_ << ' ' << task_id_;
+
583  }
584 
-
585  auto self = this->shared_from_this();
-
586  task_id_ = task_timer_.schedule([self] {
-
587  if (!self->adaptor_.is_open())
-
588  {
-
589  return;
-
590  }
-
591  self->adaptor_.shutdown_readwrite();
-
592  self->adaptor_.close();
-
593  });
-
594  CROW_LOG_DEBUG << this << " timer added: " << &task_timer_ << ' ' << task_id_;
-
595  }
-
596 
-
597  private:
-
598  Adaptor adaptor_;
-
599  Handler* handler_;
+
585  private:
+
586  Adaptor adaptor_;
+
587  Handler* handler_;
+
588 
+
589  std::array<char, 4096> buffer_;
+
590 
+
591  HTTPParser<Connection> parser_;
+
592  std::unique_ptr<routing_handle_result> routing_handle_result_;
+
593  request& req_;
+
594  response res;
+
595 
+
596  bool close_connection_ = false;
+
597 
+
598  const std::string& server_name_;
+
599  std::vector<asio::const_buffer> buffers_;
600 
-
601  std::array<char, 4096> buffer_;
-
602 
-
603  HTTPParser<Connection> parser_;
-
604  std::unique_ptr<routing_handle_result> routing_handle_result_;
-
605  request& req_;
-
606  response res;
-
607 
-
608  bool close_connection_ = false;
-
609 
-
610  const std::string& server_name_;
-
611  std::vector<asio::const_buffer> buffers_;
-
612 
-
613  std::string content_length_;
-
614  std::string date_str_;
-
615  std::string res_body_copy_;
-
616 
-
617  detail::task_timer::identifier_type task_id_{};
-
618 
-
619  bool continue_requested{};
-
620  bool need_to_call_after_handlers_{};
-
621  bool need_to_start_read_after_complete_{};
-
622  bool add_keep_alive_{};
-
623 
-
624  std::tuple<Middlewares...>* middlewares_;
-
625  detail::context<Middlewares...> ctx_;
-
626 
-
627  std::function<std::string()>& get_cached_date_str;
-
628  detail::task_timer& task_timer_;
-
629 
-
630  size_t res_stream_threshold_;
-
631 
-
632  std::atomic<unsigned int>& queue_length_;
-
633  };
-
634 
-
635 } // namespace crow
+
601  std::string content_length_;
+
602  std::string date_str_;
+
603  std::string res_body_copy_;
+
604 
+
605  detail::task_timer::identifier_type task_id_{};
+
606 
+
607  bool continue_requested{};
+
608  bool need_to_call_after_handlers_{};
+
609  bool need_to_start_read_after_complete_{};
+
610  bool add_keep_alive_{};
+
611 
+
612  std::tuple<Middlewares...>* middlewares_;
+
613  detail::context<Middlewares...> ctx_;
+
614 
+
615  std::function<std::string()>& get_cached_date_str;
+
616  detail::task_timer& task_timer_;
+
617 
+
618  size_t res_stream_threshold_;
+
619 
+
620  std::atomic<unsigned int>& queue_length_;
+
621  };
+
622 
+
623 } // namespace crow
crow::Connection
An HTTP connection.
Definition: http_connection.h:48
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:217
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:87
diff --git a/master/reference/routing_8h_source.html b/master/reference/routing_8h_source.html index 1087f3934..7df684ea4 100644 --- a/master/reference/routing_8h_source.html +++ b/master/reference/routing_8h_source.html @@ -1563,391 +1563,373 @@
1462  {
1463  CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
1464  res = response(301);
-
1465 
-
1466  // TODO(ipkn) absolute url building
-
1467  if (req.get_header_value("Host").empty())
-
1468  {
-
1469  res.add_header("Location", req.url + "/");
-
1470  }
-
1471  else
-
1472  {
-
1473  res.add_header("Location", (using_ssl ? "https://" : "http://") + req.get_header_value("Host") + req.url + "/");
-
1474  }
-
1475  res.end();
-
1476  return;
-
1477  }
-
1478 
-
1479  CROW_LOG_DEBUG << "Matched rule (upgrade) '" << rules[rule_index]->rule_ << "' " << static_cast<uint32_t>(req.method) << " / " << rules[rule_index]->get_methods();
-
1480 
-
1481  try
-
1482  {
-
1483  rules[rule_index]->handle_upgrade(req, res, std::move(adaptor));
-
1484  }
-
1485  catch (...)
-
1486  {
-
1487  exception_handler_(res);
-
1488  res.end();
-
1489  return;
-
1490  }
-
1491  }
-
1492 
-
1493  void get_found_bp(std::vector<uint16_t>& bp_i, std::vector<Blueprint*>& blueprints, std::vector<Blueprint*>& found_bps, uint16_t index = 0)
-
1494  {
-
1495  // This statement makes 3 assertions:
-
1496  // 1. The index is above 0.
-
1497  // 2. The index does not lie outside the given blueprint list.
-
1498  // 3. The next blueprint we're adding has a prefix that starts the same as the already added blueprint + a slash (the rest is irrelevant).
-
1499  //
-
1500  // This is done to prevent a blueprint that has a prefix of "bp_prefix2" to be assumed as a child of one that has "bp_prefix".
-
1501  //
-
1502  // If any of the assertions is untrue, we delete the last item added, and continue using the blueprint list of the blueprint found before, the topmost being the router's list
-
1503  auto verify_prefix = [&bp_i, &index, &blueprints, &found_bps]() {
-
1504  return index > 0 &&
-
1505  bp_i[index] < blueprints.size() &&
-
1506  blueprints[bp_i[index]]->prefix().substr(0, found_bps[index - 1]->prefix().length() + 1).compare(std::string(found_bps[index - 1]->prefix() + '/')) == 0;
-
1507  };
-
1508  if (index < bp_i.size())
-
1509  {
-
1510 
-
1511  if (verify_prefix())
-
1512  {
-
1513  found_bps.push_back(blueprints[bp_i[index]]);
-
1514  get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
-
1515  }
-
1516  else
-
1517  {
-
1518  if (found_bps.size() < 2)
-
1519  {
-
1520  found_bps.clear();
-
1521  found_bps.push_back(blueprints_[bp_i[index]]);
-
1522  }
-
1523  else
-
1524  {
-
1525  found_bps.pop_back();
-
1526  Blueprint* last_element = found_bps.back();
-
1527  found_bps.push_back(last_element->blueprints_[bp_i[index]]);
-
1528  }
-
1529  get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
-
1530  }
-
1531  }
-
1532  }
-
1533 
-
1534  /// Is used to handle errors, you insert the error code, found route, request, and response. and it'll either call the appropriate catchall route (considering the blueprint system) and send you a status string (which is mainly used for debug messages), or just set the response code to the proper error code.
-
1535  std::string get_error(unsigned short code, routing_handle_result& found, const request& req, response& res)
-
1536  {
-
1537  res.code = code;
-
1538  std::vector<Blueprint*> bps_found;
-
1539  get_found_bp(found.blueprint_indices, blueprints_, bps_found);
-
1540  for (int i = bps_found.size() - 1; i > 0; i--)
-
1541  {
-
1542  std::vector<uint16_t> bpi = found.blueprint_indices;
-
1543  if (bps_found[i]->catchall_rule().has_handler())
-
1544  {
-
1545  try
-
1546  {
-
1547  bps_found[i]->catchall_rule().handler_(req, res);
-
1548  }
-
1549  catch (...)
-
1550  {
-
1551  exception_handler_(res);
-
1552  }
-
1553 #ifdef CROW_ENABLE_DEBUG
-
1554  return std::string("Redirected to Blueprint \"" + bps_found[i]->prefix() + "\" Catchall rule");
-
1555 #else
-
1556  return std::string();
-
1557 #endif
-
1558  }
-
1559  }
-
1560  if (catchall_rule_.has_handler())
-
1561  {
-
1562  try
-
1563  {
-
1564  catchall_rule_.handler_(req, res);
-
1565  }
-
1566  catch (...)
-
1567  {
-
1568  exception_handler_(res);
-
1569  }
-
1570 #ifdef CROW_ENABLE_DEBUG
-
1571  return std::string("Redirected to global Catchall rule");
-
1572 #else
-
1573  return std::string();
-
1574 #endif
-
1575  }
-
1576  return std::string();
-
1577  }
-
1578 
-
1579  std::unique_ptr<routing_handle_result> handle_initial(request& req, response& res)
-
1580  {
-
1581  HTTPMethod method_actual = req.method;
-
1582 
-
1583  std::unique_ptr<routing_handle_result> found{
-
1584  new routing_handle_result(
-
1585  0,
-
1586  std::vector<uint16_t>(),
-
1587  routing_params(),
-
1588  HTTPMethod::InternalMethodCount)}; // This is always returned to avoid a null pointer dereference.
-
1589 
-
1590  // NOTE(EDev): This most likely will never run since the parser should handle this situation and close the connection before it gets here.
-
1591  if (CROW_UNLIKELY(req.method >= HTTPMethod::InternalMethodCount))
-
1592  return found;
-
1593  else if (req.method == HTTPMethod::Head)
-
1594  {
-
1595  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
-
1596  // support HEAD requests using GET if not defined as method for the requested URL
-
1597  if (!found->rule_index)
-
1598  {
-
1599  method_actual = HTTPMethod::Get;
-
1600  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
-
1601  if (!found->rule_index) // If a route is still not found, return a 404 without executing the rest of the HEAD specific code.
-
1602  {
-
1603  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
-
1604  res = response(404); //TODO(EDev): Should this redirect to catchall?
-
1605  res.end();
-
1606  return found;
-
1607  }
-
1608  }
-
1609 
-
1610  res.skip_body = true;
-
1611  found->method = method_actual;
-
1612  return found;
-
1613  }
-
1614  else if (req.method == HTTPMethod::Options)
-
1615  {
-
1616  std::string allow = "OPTIONS, HEAD, ";
-
1617 
-
1618  if (req.url == "/*")
-
1619  {
-
1620  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
-
1621  {
-
1622  if (static_cast<int>(HTTPMethod::Head) == i)
-
1623  continue; // HEAD is always allowed
-
1624 
-
1625  if (!per_methods_[i].trie.is_empty())
-
1626  {
-
1627  allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
-
1628  }
-
1629  }
-
1630  allow = allow.substr(0, allow.size() - 2);
-
1631  res = response(204);
-
1632  res.set_header("Allow", allow);
-
1633  res.end();
-
1634  found->method = method_actual;
-
1635  return found;
-
1636  }
-
1637  else
-
1638  {
-
1639  bool rules_matched = false;
-
1640  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
-
1641  {
-
1642  if (per_methods_[i].trie.find(req.url).rule_index)
-
1643  {
-
1644  rules_matched = true;
-
1645 
-
1646  if (static_cast<int>(HTTPMethod::Head) == i)
-
1647  continue; // HEAD is always allowed
-
1648 
-
1649  allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
-
1650  }
+
1465  res.add_header("Location", req.url + "/");
+
1466  res.end();
+
1467  return;
+
1468  }
+
1469 
+
1470  CROW_LOG_DEBUG << "Matched rule (upgrade) '" << rules[rule_index]->rule_ << "' " << static_cast<uint32_t>(req.method) << " / " << rules[rule_index]->get_methods();
+
1471 
+
1472  try
+
1473  {
+
1474  rules[rule_index]->handle_upgrade(req, res, std::move(adaptor));
+
1475  }
+
1476  catch (...)
+
1477  {
+
1478  exception_handler_(res);
+
1479  res.end();
+
1480  return;
+
1481  }
+
1482  }
+
1483 
+
1484  void get_found_bp(std::vector<uint16_t>& bp_i, std::vector<Blueprint*>& blueprints, std::vector<Blueprint*>& found_bps, uint16_t index = 0)
+
1485  {
+
1486  // This statement makes 3 assertions:
+
1487  // 1. The index is above 0.
+
1488  // 2. The index does not lie outside the given blueprint list.
+
1489  // 3. The next blueprint we're adding has a prefix that starts the same as the already added blueprint + a slash (the rest is irrelevant).
+
1490  //
+
1491  // This is done to prevent a blueprint that has a prefix of "bp_prefix2" to be assumed as a child of one that has "bp_prefix".
+
1492  //
+
1493  // If any of the assertions is untrue, we delete the last item added, and continue using the blueprint list of the blueprint found before, the topmost being the router's list
+
1494  auto verify_prefix = [&bp_i, &index, &blueprints, &found_bps]() {
+
1495  return index > 0 &&
+
1496  bp_i[index] < blueprints.size() &&
+
1497  blueprints[bp_i[index]]->prefix().substr(0, found_bps[index - 1]->prefix().length() + 1).compare(std::string(found_bps[index - 1]->prefix() + '/')) == 0;
+
1498  };
+
1499  if (index < bp_i.size())
+
1500  {
+
1501 
+
1502  if (verify_prefix())
+
1503  {
+
1504  found_bps.push_back(blueprints[bp_i[index]]);
+
1505  get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
+
1506  }
+
1507  else
+
1508  {
+
1509  if (found_bps.size() < 2)
+
1510  {
+
1511  found_bps.clear();
+
1512  found_bps.push_back(blueprints_[bp_i[index]]);
+
1513  }
+
1514  else
+
1515  {
+
1516  found_bps.pop_back();
+
1517  Blueprint* last_element = found_bps.back();
+
1518  found_bps.push_back(last_element->blueprints_[bp_i[index]]);
+
1519  }
+
1520  get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
+
1521  }
+
1522  }
+
1523  }
+
1524 
+
1525  /// Is used to handle errors, you insert the error code, found route, request, and response. and it'll either call the appropriate catchall route (considering the blueprint system) and send you a status string (which is mainly used for debug messages), or just set the response code to the proper error code.
+
1526  std::string get_error(unsigned short code, routing_handle_result& found, const request& req, response& res)
+
1527  {
+
1528  res.code = code;
+
1529  std::vector<Blueprint*> bps_found;
+
1530  get_found_bp(found.blueprint_indices, blueprints_, bps_found);
+
1531  for (int i = bps_found.size() - 1; i > 0; i--)
+
1532  {
+
1533  std::vector<uint16_t> bpi = found.blueprint_indices;
+
1534  if (bps_found[i]->catchall_rule().has_handler())
+
1535  {
+
1536  try
+
1537  {
+
1538  bps_found[i]->catchall_rule().handler_(req, res);
+
1539  }
+
1540  catch (...)
+
1541  {
+
1542  exception_handler_(res);
+
1543  }
+
1544 #ifdef CROW_ENABLE_DEBUG
+
1545  return std::string("Redirected to Blueprint \"" + bps_found[i]->prefix() + "\" Catchall rule");
+
1546 #else
+
1547  return std::string();
+
1548 #endif
+
1549  }
+
1550  }
+
1551  if (catchall_rule_.has_handler())
+
1552  {
+
1553  try
+
1554  {
+
1555  catchall_rule_.handler_(req, res);
+
1556  }
+
1557  catch (...)
+
1558  {
+
1559  exception_handler_(res);
+
1560  }
+
1561 #ifdef CROW_ENABLE_DEBUG
+
1562  return std::string("Redirected to global Catchall rule");
+
1563 #else
+
1564  return std::string();
+
1565 #endif
+
1566  }
+
1567  return std::string();
+
1568  }
+
1569 
+
1570  std::unique_ptr<routing_handle_result> handle_initial(request& req, response& res)
+
1571  {
+
1572  HTTPMethod method_actual = req.method;
+
1573 
+
1574  std::unique_ptr<routing_handle_result> found{
+
1575  new routing_handle_result(
+
1576  0,
+
1577  std::vector<uint16_t>(),
+
1578  routing_params(),
+
1579  HTTPMethod::InternalMethodCount)}; // This is always returned to avoid a null pointer dereference.
+
1580 
+
1581  // NOTE(EDev): This most likely will never run since the parser should handle this situation and close the connection before it gets here.
+
1582  if (CROW_UNLIKELY(req.method >= HTTPMethod::InternalMethodCount))
+
1583  return found;
+
1584  else if (req.method == HTTPMethod::Head)
+
1585  {
+
1586  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
+
1587  // support HEAD requests using GET if not defined as method for the requested URL
+
1588  if (!found->rule_index)
+
1589  {
+
1590  method_actual = HTTPMethod::Get;
+
1591  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
+
1592  if (!found->rule_index) // If a route is still not found, return a 404 without executing the rest of the HEAD specific code.
+
1593  {
+
1594  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
+
1595  res = response(404); //TODO(EDev): Should this redirect to catchall?
+
1596  res.end();
+
1597  return found;
+
1598  }
+
1599  }
+
1600 
+
1601  res.skip_body = true;
+
1602  found->method = method_actual;
+
1603  return found;
+
1604  }
+
1605  else if (req.method == HTTPMethod::Options)
+
1606  {
+
1607  std::string allow = "OPTIONS, HEAD, ";
+
1608 
+
1609  if (req.url == "/*")
+
1610  {
+
1611  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
+
1612  {
+
1613  if (static_cast<int>(HTTPMethod::Head) == i)
+
1614  continue; // HEAD is always allowed
+
1615 
+
1616  if (!per_methods_[i].trie.is_empty())
+
1617  {
+
1618  allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
+
1619  }
+
1620  }
+
1621  allow = allow.substr(0, allow.size() - 2);
+
1622  res = response(204);
+
1623  res.set_header("Allow", allow);
+
1624  res.end();
+
1625  found->method = method_actual;
+
1626  return found;
+
1627  }
+
1628  else
+
1629  {
+
1630  bool rules_matched = false;
+
1631  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
+
1632  {
+
1633  if (per_methods_[i].trie.find(req.url).rule_index)
+
1634  {
+
1635  rules_matched = true;
+
1636 
+
1637  if (static_cast<int>(HTTPMethod::Head) == i)
+
1638  continue; // HEAD is always allowed
+
1639 
+
1640  allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
+
1641  }
+
1642  }
+
1643  if (rules_matched)
+
1644  {
+
1645  allow = allow.substr(0, allow.size() - 2);
+
1646  res = response(204);
+
1647  res.set_header("Allow", allow);
+
1648  res.end();
+
1649  found->method = method_actual;
+
1650  return found;
1651  }
-
1652  if (rules_matched)
+
1652  else
1653  {
-
1654  allow = allow.substr(0, allow.size() - 2);
-
1655  res = response(204);
-
1656  res.set_header("Allow", allow);
-
1657  res.end();
-
1658  found->method = method_actual;
-
1659  return found;
-
1660  }
-
1661  else
-
1662  {
-
1663  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
-
1664  res = response(404); //TODO(EDev): Should this redirect to catchall?
-
1665  res.end();
-
1666  return found;
-
1667  }
-
1668  }
-
1669  }
-
1670  else // Every request that isn't a HEAD or OPTIONS request
-
1671  {
-
1672  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
-
1673  // TODO(EDev): maybe ending the else here would allow the requests coming from above (after removing the return statement) to be checked on whether they actually point to a route
-
1674  if (!found->rule_index)
-
1675  {
-
1676  for (auto& per_method : per_methods_)
-
1677  {
-
1678  if (per_method.trie.find(req.url).rule_index) //Route found, but in another method
-
1679  {
-
1680  const std::string error_message(get_error(405, *found, req, res));
-
1681  CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(method_actual) << ". " << error_message;
-
1682  res.end();
-
1683  return found;
-
1684  }
-
1685  }
-
1686  //Route does not exist anywhere
-
1687 
-
1688  const std::string error_message(get_error(404, *found, req, res));
-
1689  CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". " << error_message;
-
1690  res.end();
-
1691  return found;
-
1692  }
-
1693 
-
1694  found->method = method_actual;
-
1695  return found;
-
1696  }
-
1697  }
-
1698 
-
1699  template<typename App>
-
1700  void handle(request& req, response& res, routing_handle_result found)
-
1701  {
-
1702  HTTPMethod method_actual = found.method;
-
1703  auto& rules = per_methods_[static_cast<int>(method_actual)].rules;
-
1704  unsigned rule_index = found.rule_index;
-
1705 
-
1706  if (rule_index >= rules.size())
-
1707  throw std::runtime_error("Trie internal structure corrupted!");
+
1654  CROW_LOG_DEBUG << "Cannot match rules " << req.url;
+
1655  res = response(404); //TODO(EDev): Should this redirect to catchall?
+
1656  res.end();
+
1657  return found;
+
1658  }
+
1659  }
+
1660  }
+
1661  else // Every request that isn't a HEAD or OPTIONS request
+
1662  {
+
1663  *found = per_methods_[static_cast<int>(method_actual)].trie.find(req.url);
+
1664  // TODO(EDev): maybe ending the else here would allow the requests coming from above (after removing the return statement) to be checked on whether they actually point to a route
+
1665  if (!found->rule_index)
+
1666  {
+
1667  for (auto& per_method : per_methods_)
+
1668  {
+
1669  if (per_method.trie.find(req.url).rule_index) //Route found, but in another method
+
1670  {
+
1671  const std::string error_message(get_error(405, *found, req, res));
+
1672  CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(method_actual) << ". " << error_message;
+
1673  res.end();
+
1674  return found;
+
1675  }
+
1676  }
+
1677  //Route does not exist anywhere
+
1678 
+
1679  const std::string error_message(get_error(404, *found, req, res));
+
1680  CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". " << error_message;
+
1681  res.end();
+
1682  return found;
+
1683  }
+
1684 
+
1685  found->method = method_actual;
+
1686  return found;
+
1687  }
+
1688  }
+
1689 
+
1690  template<typename App>
+
1691  void handle(request& req, response& res, routing_handle_result found)
+
1692  {
+
1693  HTTPMethod method_actual = found.method;
+
1694  auto& rules = per_methods_[static_cast<int>(method_actual)].rules;
+
1695  unsigned rule_index = found.rule_index;
+
1696 
+
1697  if (rule_index >= rules.size())
+
1698  throw std::runtime_error("Trie internal structure corrupted!");
+
1699 
+
1700  if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
+
1701  {
+
1702  CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
+
1703  res = response(301);
+
1704  res.add_header("Location", req.url + "/");
+
1705  res.end();
+
1706  return;
+
1707  }
1708 
-
1709  if (rule_index == RULE_SPECIAL_REDIRECT_SLASH)
-
1710  {
-
1711  CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
-
1712  res = response(301);
-
1713 
-
1714  // TODO(ipkn) absolute url building
-
1715  if (req.get_header_value("Host").empty())
-
1716  {
-
1717  res.add_header("Location", req.url + "/");
-
1718  }
-
1719  else
-
1720  {
-
1721  res.add_header("Location", (using_ssl ? "https://" : "http://") + req.get_header_value("Host") + req.url + "/");
-
1722  }
-
1723  res.end();
-
1724  return;
-
1725  }
-
1726 
-
1727  CROW_LOG_DEBUG << "Matched rule '" << rules[rule_index]->rule_ << "' " << static_cast<uint32_t>(req.method) << " / " << rules[rule_index]->get_methods();
-
1728 
-
1729  try
-
1730  {
-
1731  auto& rule = rules[rule_index];
-
1732  handle_rule<App>(rule, req, res, found.r_params);
-
1733  }
-
1734  catch (...)
-
1735  {
-
1736  exception_handler_(res);
-
1737  res.end();
-
1738  return;
-
1739  }
-
1740  }
-
1741 
-
1742  template<typename App>
-
1743  typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value != 0, void>::type
-
1744  handle_rule(BaseRule* rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
-
1745  {
-
1746  if (!rule->mw_indices_.empty())
-
1747  {
-
1748  auto& ctx = *reinterpret_cast<typename App::context_t*>(req.middleware_context);
-
1749  auto& container = *reinterpret_cast<typename App::mw_container_t*>(req.middleware_container);
-
1750  detail::middleware_call_criteria_dynamic<false> crit_fwd(rule->mw_indices_.indices());
-
1751 
-
1752  auto glob_completion_handler = std::move(res.complete_request_handler_);
-
1753  res.complete_request_handler_ = [] {};
-
1754 
-
1755  detail::middleware_call_helper<decltype(crit_fwd),
-
1756  0, typename App::context_t, typename App::mw_container_t>(crit_fwd, container, req, res, ctx);
-
1757 
-
1758  if (res.completed_)
-
1759  {
-
1760  glob_completion_handler();
-
1761  return;
-
1762  }
-
1763 
-
1764  res.complete_request_handler_ = [&rule, &ctx, &container, &req, &res, glob_completion_handler] {
-
1765  detail::middleware_call_criteria_dynamic<true> crit_bwd(rule->mw_indices_.indices());
+
1709  CROW_LOG_DEBUG << "Matched rule '" << rules[rule_index]->rule_ << "' " << static_cast<uint32_t>(req.method) << " / " << rules[rule_index]->get_methods();
+
1710 
+
1711  try
+
1712  {
+
1713  auto& rule = rules[rule_index];
+
1714  handle_rule<App>(rule, req, res, found.r_params);
+
1715  }
+
1716  catch (...)
+
1717  {
+
1718  exception_handler_(res);
+
1719  res.end();
+
1720  return;
+
1721  }
+
1722  }
+
1723 
+
1724  template<typename App>
+
1725  typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value != 0, void>::type
+
1726  handle_rule(BaseRule* rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
+
1727  {
+
1728  if (!rule->mw_indices_.empty())
+
1729  {
+
1730  auto& ctx = *reinterpret_cast<typename App::context_t*>(req.middleware_context);
+
1731  auto& container = *reinterpret_cast<typename App::mw_container_t*>(req.middleware_container);
+
1732  detail::middleware_call_criteria_dynamic<false> crit_fwd(rule->mw_indices_.indices());
+
1733 
+
1734  auto glob_completion_handler = std::move(res.complete_request_handler_);
+
1735  res.complete_request_handler_ = [] {};
+
1736 
+
1737  detail::middleware_call_helper<decltype(crit_fwd),
+
1738  0, typename App::context_t, typename App::mw_container_t>(crit_fwd, container, req, res, ctx);
+
1739 
+
1740  if (res.completed_)
+
1741  {
+
1742  glob_completion_handler();
+
1743  return;
+
1744  }
+
1745 
+
1746  res.complete_request_handler_ = [&rule, &ctx, &container, &req, &res, glob_completion_handler] {
+
1747  detail::middleware_call_criteria_dynamic<true> crit_bwd(rule->mw_indices_.indices());
+
1748 
+
1749  detail::after_handlers_call_helper<
+
1750  decltype(crit_bwd),
+
1751  std::tuple_size<typename App::mw_container_t>::value - 1,
+
1752  typename App::context_t,
+
1753  typename App::mw_container_t>(crit_bwd, container, ctx, req, res);
+
1754  glob_completion_handler();
+
1755  };
+
1756  }
+
1757  rule->handle(req, res, rp);
+
1758  }
+
1759 
+
1760  template<typename App>
+
1761  typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value == 0, void>::type
+
1762  handle_rule(BaseRule* rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
+
1763  {
+
1764  rule->handle(req, res, rp);
+
1765  }
1766 
-
1767  detail::after_handlers_call_helper<
-
1768  decltype(crit_bwd),
-
1769  std::tuple_size<typename App::mw_container_t>::value - 1,
-
1770  typename App::context_t,
-
1771  typename App::mw_container_t>(crit_bwd, container, ctx, req, res);
-
1772  glob_completion_handler();
-
1773  };
-
1774  }
-
1775  rule->handle(req, res, rp);
-
1776  }
-
1777 
-
1778  template<typename App>
-
1779  typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value == 0, void>::type
-
1780  handle_rule(BaseRule* rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
+
1767  void debug_print()
+
1768  {
+
1769  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
+
1770  {
+
1771  Trie& trie_ = per_methods_[i].trie;
+
1772  if (!trie_.is_empty())
+
1773  {
+
1774  CROW_LOG_DEBUG << method_name(static_cast<HTTPMethod>(i));
+
1775  trie_.debug_print();
+
1776  }
+
1777  }
+
1778  }
+
1779 
+
1780  std::vector<Blueprint*>& blueprints()
1781  {
-
1782  rule->handle(req, res, rp);
+
1782  return blueprints_;
1783  }
1784 
-
1785  void debug_print()
+
1785  std::function<void(crow::response&)>& exception_handler()
1786  {
-
1787  for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
-
1788  {
-
1789  Trie& trie_ = per_methods_[i].trie;
-
1790  if (!trie_.is_empty())
-
1791  {
-
1792  CROW_LOG_DEBUG << method_name(static_cast<HTTPMethod>(i));
-
1793  trie_.debug_print();
-
1794  }
-
1795  }
-
1796  }
-
1797 
-
1798  std::vector<Blueprint*>& blueprints()
-
1799  {
-
1800  return blueprints_;
-
1801  }
-
1802 
-
1803  std::function<void(crow::response&)>& exception_handler()
-
1804  {
-
1805  return exception_handler_;
-
1806  }
-
1807 
-
1808  static void default_exception_handler(response& res)
-
1809  {
-
1810  // any uncaught exceptions become 500s
-
1811  res = response(500);
-
1812 
-
1813  try
-
1814  {
-
1815  throw;
-
1816  }
-
1817  catch (const bad_request& e)
-
1818  {
-
1819  res = response (400);
-
1820  res.body = e.what();
-
1821  }
-
1822  catch (const std::exception& e)
-
1823  {
-
1824  CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what();
-
1825  }
-
1826  catch (...)
-
1827  {
-
1828  CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available.";
-
1829  }
-
1830  }
-
1831 
-
1832  private:
-
1833  CatchallRule catchall_rule_;
-
1834 
-
1835  struct PerMethod
-
1836  {
-
1837  std::vector<BaseRule*> rules;
-
1838  Trie trie;
-
1839 
-
1840  // rule index 0, 1 has special meaning; preallocate it to avoid duplication.
-
1841  PerMethod():
-
1842  rules(2) {}
-
1843  };
-
1844  std::array<PerMethod, static_cast<int>(HTTPMethod::InternalMethodCount)> per_methods_;
-
1845  std::vector<std::unique_ptr<BaseRule>> all_rules_;
-
1846  std::vector<Blueprint*> blueprints_;
-
1847  std::function<void(crow::response&)> exception_handler_ = &default_exception_handler;
-
1848  };
-
1849 } // namespace crow
+
1787  return exception_handler_;
+
1788  }
+
1789 
+
1790  static void default_exception_handler(response& res)
+
1791  {
+
1792  // any uncaught exceptions become 500s
+
1793  res = response(500);
+
1794 
+
1795  try
+
1796  {
+
1797  throw;
+
1798  }
+
1799  catch (const bad_request& e)
+
1800  {
+
1801  res = response (400);
+
1802  res.body = e.what();
+
1803  }
+
1804  catch (const std::exception& e)
+
1805  {
+
1806  CROW_LOG_ERROR << "An uncaught exception occurred: " << e.what();
+
1807  }
+
1808  catch (...)
+
1809  {
+
1810  CROW_LOG_ERROR << "An uncaught exception occurred. The type was unknown so no information was available.";
+
1811  }
+
1812  }
+
1813 
+
1814  private:
+
1815  CatchallRule catchall_rule_;
+
1816 
+
1817  struct PerMethod
+
1818  {
+
1819  std::vector<BaseRule*> rules;
+
1820  Trie trie;
+
1821 
+
1822  // rule index 0, 1 has special meaning; preallocate it to avoid duplication.
+
1823  PerMethod():
+
1824  rules(2) {}
+
1825  };
+
1826  std::array<PerMethod, static_cast<int>(HTTPMethod::InternalMethodCount)> per_methods_;
+
1827  std::vector<std::unique_ptr<BaseRule>> all_rules_;
+
1828  std::vector<Blueprint*> blueprints_;
+
1829  std::function<void(crow::response&)> exception_handler_ = &default_exception_handler;
+
1830  };
+
1831 } // namespace crow
crow::BaseRule
A base class for all rules.
Definition: routing.h:90
crow::Blueprint
A blueprint can be considered a smaller section of a Crow app, specifically where the router is conce...
Definition: routing.h:1114
crow::CatchallRule
Definition: routing.h:345
@@ -1955,7 +1937,7 @@
crow::Crow::websocket_max_payload
self_t & websocket_max_payload(uint64_t max_payload)
Set the default max payload size for websockets.
Definition: app.h:278
crow::DynamicRule
A rule that can change its parameters during runtime.
Definition: routing.h:585
crow::Router
Handles matching requests to existing rules and upgrade requests.
Definition: routing.h:1268
-
crow::Router::get_error
std::string get_error(unsigned short code, routing_handle_result &found, const request &req, response &res)
Is used to handle errors, you insert the error code, found route, request, and response....
Definition: routing.h:1535
+
crow::Router::get_error
std::string get_error(unsigned short code, routing_handle_result &found, const request &req, response &res)
Is used to handle errors, you insert the error code, found route, request, and response....
Definition: routing.h:1526
crow::TaggedRule
Default rule created when CROW_ROUTE is called.
Definition: routing.h:662
crow::Trie
A search tree.
Definition: routing.h:726
crow::Trie::is_empty
bool is_empty()
Check whether or not the trie is empty.
Definition: routing.h:760