Optimized HTTP parser

Also (likely) fixed problem with streaming without keeping connection
alive
Also fixed problem with multipart hanging on request
Also updated TODO entries to indicate Writer
This commit is contained in:
The-EDev 2022-02-05 18:15:19 +03:00
parent 05274f1900
commit 4cdde73388
No known key found for this signature in database
GPG Key ID: 51C45DC0C413DCD9
14 changed files with 627 additions and 1447 deletions

View File

@ -6,8 +6,21 @@
#include <iostream>
#include "crow/utility.h"
// TODO(EDev): Adding C++20's [[likely]] and [[unlikely]] attributes might be useful
#if defined(__GNUG__) || defined(__clang__)
#define CROW_LIKELY(X) __builtin_expect(!!(X), 1)
#define CROW_UNLIKELY(X) __builtin_expect(!!(X), 0)
#else
#define CROW_LIKELY(X) (X)
#define CROW_UNLIKELY(X) (X)
#endif
namespace crow
{
const char cr = '\r';
const char lf = '\n';
const std::string crlf("\r\n");
enum class HTTPMethod : char
{
#ifndef DELETE
@ -16,11 +29,43 @@ namespace crow
HEAD,
POST,
PUT,
CONNECT,
OPTIONS,
TRACE,
PATCH,
PURGE,
COPY,
LOCK,
MKCOL,
MOVE,
PROPFIND,
PROPPATCH,
SEARCH,
UNLOCK,
BIND,
REBIND,
UNBIND,
ACL,
REPORT,
MKACTIVITY,
CHECKOUT,
MERGE,
MSEARCH,
NOTIFY,
SUBSCRIBE,
UNSUBSCRIBE,
MKCALENDAR,
LINK,
UNLINK,
SOURCE,
#endif
Delete = 0,
@ -28,17 +73,104 @@ namespace crow
Head,
Post,
Put,
Connect,
Options,
Trace,
Patch,
Purge,
Copy,
Lock,
MkCol,
Move,
Propfind,
Proppatch,
Search,
Unlock,
Bind,
Rebind,
Unbind,
Acl,
Report,
MkActivity,
Checkout,
Merge,
MSearch,
Notify,
Subscribe,
Unsubscribe,
Mkcalendar,
Link,
Unlink,
Source,
InternalMethodCount,
// should not add an item below this line: used for array count
};
const char* method_strings[] =
{
"DELETE",
"GET",
"HEAD",
"POST",
"PUT",
"CONNECT",
"OPTIONS",
"TRACE",
"PATCH",
"PURGE",
"COPY",
"LOCK",
"MKCOL",
"MOVE",
"PROPFIND",
"PROPPATCH",
"SEARCH",
"UNLOCK",
"BIND",
"REBIND",
"UNBIND",
"ACL",
"REPORT",
"MKACTIVITY",
"CHECKOUT",
"MERGE",
"M-SEARCH",
"NOTIFY",
"SUBSCRIBE",
"UNSUBSCRIBE",
"MKCALENDAR",
"LINK",
"UNLINK",
"SOURCE"};
inline std::string method_name(HTTPMethod method)
{
if (CROW_LIKELY(method < HTTPMethod::InternalMethodCount))
{
return method_strings[(unsigned char)method];
}
return "invalid";
}
// clang-format off
enum status
@ -85,25 +217,6 @@ namespace crow
VARIANT_ALSO_NEGOTIATES = 506
};
inline std::string method_name(HTTPMethod method)
{
switch(method)
{
case HTTPMethod::Delete: return "DELETE";
case HTTPMethod::Get: return "GET";
case HTTPMethod::Head: return "HEAD";
case HTTPMethod::Post: return "POST";
case HTTPMethod::Put: return "PUT";
case HTTPMethod::Connect: return "CONNECT";
case HTTPMethod::Options: return "OPTIONS";
case HTTPMethod::Trace: return "TRACE";
case HTTPMethod::Patch: return "PATCH";
case HTTPMethod::Purge: return "PURGE";
default: return "invalid";
}
return "invalid";
}
// clang-format on
enum class ParamType : char
@ -173,16 +286,47 @@ namespace crow
#ifndef CROW_MSVC_WORKAROUND
constexpr crow::HTTPMethod operator"" _method(const char* str, size_t /*len*/)
{
return crow::black_magic::is_equ_p(str, "GET", 3) ? crow::HTTPMethod::Get :
crow::black_magic::is_equ_p(str, "DELETE", 6) ? crow::HTTPMethod::Delete :
crow::black_magic::is_equ_p(str, "HEAD", 4) ? crow::HTTPMethod::Head :
crow::black_magic::is_equ_p(str, "POST", 4) ? crow::HTTPMethod::Post :
crow::black_magic::is_equ_p(str, "PUT", 3) ? crow::HTTPMethod::Put :
return crow::black_magic::is_equ_p(str, "GET", 3) ? crow::HTTPMethod::Get :
crow::black_magic::is_equ_p(str, "DELETE", 6) ? crow::HTTPMethod::Delete :
crow::black_magic::is_equ_p(str, "HEAD", 4) ? crow::HTTPMethod::Head :
crow::black_magic::is_equ_p(str, "POST", 4) ? crow::HTTPMethod::Post :
crow::black_magic::is_equ_p(str, "PUT", 3) ? crow::HTTPMethod::Put :
crow::black_magic::is_equ_p(str, "OPTIONS", 7) ? crow::HTTPMethod::Options :
crow::black_magic::is_equ_p(str, "CONNECT", 7) ? crow::HTTPMethod::Connect :
crow::black_magic::is_equ_p(str, "TRACE", 5) ? crow::HTTPMethod::Trace :
crow::black_magic::is_equ_p(str, "PATCH", 5) ? crow::HTTPMethod::Patch :
crow::black_magic::is_equ_p(str, "PURGE", 5) ? crow::HTTPMethod::Purge :
throw std::runtime_error("invalid http method");
crow::black_magic::is_equ_p(str, "PATCH", 5) ? crow::HTTPMethod::Patch :
crow::black_magic::is_equ_p(str, "PURGE", 5) ? crow::HTTPMethod::Purge :
crow::black_magic::is_equ_p(str, "COPY", 4) ? crow::HTTPMethod::Copy :
crow::black_magic::is_equ_p(str, "LOCK", 4) ? crow::HTTPMethod::Lock :
crow::black_magic::is_equ_p(str, "MKCOL", 5) ? crow::HTTPMethod::MkCol :
crow::black_magic::is_equ_p(str, "MOVE", 4) ? crow::HTTPMethod::Move :
crow::black_magic::is_equ_p(str, "PROPFIND", 8) ? crow::HTTPMethod::Propfind :
crow::black_magic::is_equ_p(str, "PROPPATCH", 9) ? crow::HTTPMethod::Proppatch :
crow::black_magic::is_equ_p(str, "SEARCH", 6) ? crow::HTTPMethod::Search :
crow::black_magic::is_equ_p(str, "UNLOCK", 6) ? crow::HTTPMethod::Unlock :
crow::black_magic::is_equ_p(str, "BIND", 4) ? crow::HTTPMethod::Bind :
crow::black_magic::is_equ_p(str, "REBIND", 6) ? crow::HTTPMethod::Rebind :
crow::black_magic::is_equ_p(str, "UNBIND", 6) ? crow::HTTPMethod::Unbind :
crow::black_magic::is_equ_p(str, "ACL", 3) ? crow::HTTPMethod::Acl :
crow::black_magic::is_equ_p(str, "REPORT", 6) ? crow::HTTPMethod::Report :
crow::black_magic::is_equ_p(str, "MKACTIVITY", 10) ? crow::HTTPMethod::MkActivity :
crow::black_magic::is_equ_p(str, "CHECKOUT", 8) ? crow::HTTPMethod::Checkout :
crow::black_magic::is_equ_p(str, "MERGE", 5) ? crow::HTTPMethod::Merge :
crow::black_magic::is_equ_p(str, "MSEARCH", 7) ? crow::HTTPMethod::MSearch :
crow::black_magic::is_equ_p(str, "NOTIFY", 6) ? crow::HTTPMethod::Notify :
crow::black_magic::is_equ_p(str, "SUBSCRIBE", 9) ? crow::HTTPMethod::Subscribe :
crow::black_magic::is_equ_p(str, "UNSUBSCRIBE", 11) ? crow::HTTPMethod::Unsubscribe :
crow::black_magic::is_equ_p(str, "MKCALENDAR", 10) ? crow::HTTPMethod::Mkcalendar :
crow::black_magic::is_equ_p(str, "LINK", 4) ? crow::HTTPMethod::Link :
crow::black_magic::is_equ_p(str, "UNLINK", 6) ? crow::HTTPMethod::Unlink :
crow::black_magic::is_equ_p(str, "SOURCE", 6) ? crow::HTTPMethod::Source :
throw std::runtime_error("invalid http method");
}
#endif

View File

@ -200,7 +200,7 @@ namespace crow
{
#ifdef CROW_ENABLE_DEBUG
connectionCount++;
CROW_LOG_DEBUG << "Connection open, total " << connectionCount << ", " << this;
CROW_LOG_DEBUG << "Connection (" << this << ") allocated, total: " << connectionCount;
#endif
}
@ -210,7 +210,7 @@ namespace crow
cancel_deadline_timer();
#ifdef CROW_ENABLE_DEBUG
connectionCount--;
CROW_LOG_DEBUG << "Connection closed, total " << connectionCount << ", " << this;
CROW_LOG_DEBUG << "Connection (" << this << ") freed, total: " << connectionCount;
#endif
}
@ -240,7 +240,7 @@ namespace crow
void handle_header()
{
// HTTP 1.1 Expect: 100-continue
if (parser_.check_version(1, 1) && parser_.headers.count("expect") && get_header_value(parser_.headers, "expect") == "100-continue")
if (parser_.http_major == 1 && parser_.http_minor == 1 && get_header_value(parser_.headers, "expect") == "100-continue") // Using the parser because the request isn't made yet.
{
buffers_.clear();
static std::string expect_100_continue = "HTTP/1.1 100 Continue\r\n\r\n";
@ -260,39 +260,39 @@ namespace crow
req.remote_ip_address = adaptor_.remote_endpoint().address().to_string();
if (parser_.check_version(1, 0))
add_keep_alive_ = req.keep_alive;
close_connection_ = req.close_connection;
if (req.check_version(1, 1)) // HTTP/1.1
{
// HTTP/1.0
if (req.headers.count("connection"))
{
if (boost::iequals(req.get_header_value("connection"), "Keep-Alive"))
add_keep_alive_ = true;
}
else
close_connection_ = true;
}
else if (parser_.check_version(1, 1))
{
// HTTP/1.1
if (req.headers.count("connection"))
{
if (req.get_header_value("connection") == "close")
close_connection_ = true;
else if (boost::iequals(req.get_header_value("connection"), "Keep-Alive"))
add_keep_alive_ = true;
}
if (!req.headers.count("host"))
{
is_invalid_request = true;
res = response(400);
}
if (parser_.is_upgrade())
if (req.upgrade)
{
if (req.get_header_value("upgrade") == "h2c")
#ifdef CROW_ENABLE_SSL
if (handler_->ssl_used())
{
// TODO HTTP/2
if (req.get_header_value("upgrade") == "h2")
{
// TODO(ipkn): HTTP/2
// currently, ignore upgrade header
}
}
else if (req.get_header_value("upgrade") == "h2c")
{
// TODO(ipkn): HTTP/2
// currently, ignore upgrade header
}
#else
if (req.get_header_value("upgrade") == "h2c")
{
// TODO(ipkn): HTTP/2
// currently, ignore upgrade header
}
#endif
else
{
close_connection_ = true;
@ -302,8 +302,7 @@ namespace crow
}
}
CROW_LOG_INFO << "Request: " << boost::lexical_cast<std::string>(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << parser_.http_major << "." << parser_.http_minor << ' '
<< method_name(req.method) << " " << req.url;
CROW_LOG_INFO << "Request: " << boost::lexical_cast<std::string>(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << (char)(req.http_ver_major + 48) << "." << (char)(req.http_ver_minor + 48) << ' ' << method_name(req.method) << " " << req.url;
need_to_call_after_handlers_ = false;
@ -420,7 +419,7 @@ namespace crow
//delete this;
return;
}
// TODO(EDev): HTTP version in status codes should be dynamic
// Keep in sync with common.h/status
static std::unordered_map<int, std::string> statusCodes = {
{status::CONTINUE, "HTTP/1.1 100 Continue\r\n"},
@ -465,8 +464,7 @@ namespace crow
{status::VARIANT_ALSO_NEGOTIATES, "HTTP/1.1 506 Variant Also Negotiates\r\n"},
};
static std::string seperator = ": ";
static std::string crlf = "\r\n";
static const std::string seperator = ": ";
buffers_.clear();
buffers_.reserve(4 * (res.headers.size() + 5) + 3);
@ -538,6 +536,16 @@ namespace crow
do_write_sync(buffers);
}
}
is_writing = false;
if (close_connection_)
{
//boost::asio::socket_base::linger option(true, 30);
//adaptor_.raw_socket().set_option(option);
adaptor_.shutdown_readwrite();
adaptor_.close();
//CROW_LOG_DEBUG << this << " from write (sync)(1)";
check_destroy();
}
res.end();
res.clear();
@ -587,6 +595,16 @@ namespace crow
buffers.push_back(boost::asio::buffer(buf));
do_write_sync(buffers);
}
is_writing = false;
if (close_connection_)
{
//boost::asio::socket_base::linger option(true, 30);
//adaptor_.raw_socket().set_option(option);
adaptor_.shutdown_readwrite();
adaptor_.close();
//CROW_LOG_DEBUG << this << " from write (sync)(1)";
check_destroy();
}
res.end();
res.clear();
@ -618,7 +636,7 @@ namespace crow
adaptor_.shutdown_read();
adaptor_.close();
is_reading = false;
CROW_LOG_DEBUG << this << " from read(1)";
CROW_LOG_DEBUG << this << " from read(1) with description: \"" << http_errno_description(static_cast<http_errno>(parser_.http_errno)) << '\"';
check_destroy();
}
else if (close_connection_)
@ -676,13 +694,6 @@ namespace crow
boost::asio::write(adaptor_.socket(), buffers, [&](std::error_code ec, std::size_t) {
if (!ec)
{
if (close_connection_)
{
adaptor_.shutdown_write();
adaptor_.close();
CROW_LOG_DEBUG << this << " from write (sync)(1)";
check_destroy();
}
return false;
}
else

File diff suppressed because it is too large Load Diff

View File

@ -20,8 +20,6 @@ namespace crow
return empty;
}
struct DetachHelper;
/// An HTTP request.
struct request
{
@ -32,6 +30,8 @@ namespace crow
ci_map headers;
std::string body;
std::string remote_ip_address; ///< The IP address from which the request was sent.
unsigned char http_ver_major, http_ver_minor;
bool keep_alive, close_connection, upgrade;
void* middleware_context{};
boost::asio::io_service* io_service{};
@ -42,8 +42,8 @@ namespace crow
{}
/// Construct a request with all values assigned.
request(HTTPMethod method, std::string raw_url, std::string url, query_string url_params, ci_map headers, std::string body):
method(method), raw_url(std::move(raw_url)), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body))
request(HTTPMethod method, std::string raw_url, std::string url, query_string url_params, ci_map headers, std::string body, unsigned char http_major, unsigned char http_minor, bool has_keep_alive, bool has_close_connection, bool is_upgrade):
method(method), raw_url(std::move(raw_url)), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body)), http_ver_major(http_major), http_ver_minor(http_minor), keep_alive(has_keep_alive), close_connection(has_close_connection), upgrade(is_upgrade)
{}
void add_header(std::string key, std::string value)
@ -56,14 +56,19 @@ namespace crow
return crow::get_header_value(headers, key);
}
/// Send the request with a completion handler and return immediately.
bool check_version(unsigned char major, unsigned char minor) const
{
return http_ver_major == major && http_ver_minor == minor;
}
/// Send data to whoever made this request with a completion handler and return immediately.
template<typename CompletionHandler>
void post(CompletionHandler handler)
{
io_service->post(handler);
}
/// Send the request with a completion handler.
/// Send data to whoever made this request with a completion handler.
template<typename CompletionHandler>
void dispatch(CompletionHandler handler)
{

View File

@ -32,7 +32,7 @@ namespace crow
#ifdef CROW_ENABLE_COMPRESSION
bool compressed = true; ///< If compression is enabled and this is false, the individual response will not be compressed.
#endif
bool is_head_response = false; ///< Whether this is a response to a HEAD request.
bool skip_body = false; ///< Whether this is a response to a HEAD request.
bool manual_length_header = false; ///< Whether Crow should automatically add a "Content-Length" header.
/// Set the value of an existing header in the response.
@ -171,7 +171,7 @@ namespace crow
if (!completed_)
{
completed_ = true;
if (is_head_response)
if (skip_body)
{
set_header("Content-Length", std::to_string(body.size()));
body = "";

View File

@ -17,17 +17,10 @@
#include <boost/operators.hpp>
#include <vector>
#include "crow/common.h"
#include "crow/settings.h"
#include "crow/returnable.h"
#if defined(__GNUG__) || defined(__clang__)
#define crow_json_likely(x) __builtin_expect(x, 1)
#define crow_json_unlikely(x) __builtin_expect(x, 0)
#else
#define crow_json_likely(x) x
#define crow_json_unlikely(x) x
#endif
namespace crow
{
@ -830,7 +823,7 @@ namespace crow
bool consume(char c)
{
if (crow_json_unlikely(*data != c))
if (CROW_UNLIKELY(*data != c))
return false;
data++;
return true;
@ -844,13 +837,13 @@ namespace crow
rvalue decode_string()
{
if (crow_json_unlikely(!consume('"')))
if (CROW_UNLIKELY(!consume('"')))
return {};
char* start = data;
uint8_t has_escaping = 0;
while (1)
{
if (crow_json_likely(*data != '"' && *data != '\\' && *data != '\0'))
if (CROW_LIKELY(*data != '"' && *data != '\\' && *data != '\0'))
{
data++;
}
@ -905,13 +898,13 @@ namespace crow
rvalue decode_list()
{
rvalue ret(type::List);
if (crow_json_unlikely(!consume('[')))
if (CROW_UNLIKELY(!consume('[')))
{
ret.set_error();
return ret;
}
ws_skip();
if (crow_json_unlikely(*data == ']'))
if (CROW_UNLIKELY(*data == ']'))
{
data++;
return ret;
@ -920,7 +913,7 @@ namespace crow
while (1)
{
auto v = decode_value();
if (crow_json_unlikely(!v))
if (CROW_UNLIKELY(!v))
{
ret.set_error();
break;
@ -932,7 +925,7 @@ namespace crow
data++;
break;
}
if (crow_json_unlikely(!consume(',')))
if (CROW_UNLIKELY(!consume(',')))
{
ret.set_error();
break;
@ -957,7 +950,7 @@ namespace crow
DigitsAfterE,
Invalid,
} state{Minus};
while (crow_json_likely(state != Invalid))
while (CROW_LIKELY(state != Invalid))
{
switch (*data)
{
@ -1054,7 +1047,7 @@ namespace crow
return {};*/
break;
default:
if (crow_json_likely(state == NumberParsingState::ZeroFirst ||
if (CROW_LIKELY(state == NumberParsingState::ZeroFirst ||
state == NumberParsingState::Digits ||
state == NumberParsingState::DigitsAfterPoints ||
state == NumberParsingState::DigitsAfterE))
@ -1125,7 +1118,7 @@ namespace crow
rvalue decode_object()
{
rvalue ret(type::Object);
if (crow_json_unlikely(!consume('{')))
if (CROW_UNLIKELY(!consume('{')))
{
ret.set_error();
return ret;
@ -1133,7 +1126,7 @@ namespace crow
ws_skip();
if (crow_json_unlikely(*data == '}'))
if (CROW_UNLIKELY(*data == '}'))
{
data++;
return ret;
@ -1142,26 +1135,26 @@ namespace crow
while (1)
{
auto t = decode_string();
if (crow_json_unlikely(!t))
if (CROW_UNLIKELY(!t))
{
ret.set_error();
break;
}
ws_skip();
if (crow_json_unlikely(!consume(':')))
if (CROW_UNLIKELY(!consume(':')))
{
ret.set_error();
break;
}
// TODO caching key to speed up (flyweight?)
// TODO(ipkn) caching key to speed up (flyweight?)
// I have no idea how flyweight could apply here, but maybe some speedup can happen if we stopped checking type since decode_string returns a string anyway
auto key = t.s();
ws_skip();
auto v = decode_value();
if (crow_json_unlikely(!v))
if (CROW_UNLIKELY(!v))
{
ret.set_error();
break;
@ -1170,12 +1163,12 @@ namespace crow
v.key_ = std::move(key);
ret.emplace_back(std::move(v));
if (crow_json_unlikely(*data == '}'))
if (CROW_UNLIKELY(*data == '}'))
{
data++;
break;
}
if (crow_json_unlikely(!consume(',')))
if (CROW_UNLIKELY(!consume(',')))
{
ret.set_error();
break;
@ -1895,6 +1888,3 @@ namespace crow
//}
} // namespace json
} // namespace crow
#undef crow_json_likely
#undef crow_json_unlikely

View File

@ -13,7 +13,6 @@ namespace crow
namespace multipart
{
const std::string dd = "--";
const std::string crlf = "\r\n";
/// The first part in a section, contains metadata about the part
struct header
@ -28,6 +27,7 @@ namespace crow
/// It is usually separated from other sections by a `boundary`
struct part
{
//TODO(EDev): restructure this to an `unordered_map<string, header>` with string being `header::value.first`
std::vector<header> headers; ///< (optional) The first part before the data, Contains information regarding the type of data and encoding
std::string body; ///< The actual data in the part
};
@ -35,7 +35,7 @@ namespace crow
/// The parsed multipart request/response
struct message : public returnable
{
ci_map headers;
ci_map headers; ///< The request/response headers
std::string boundary; ///< The text boundary that separates different `parts`
std::vector<part> parts; ///< The individual parts of the message
@ -95,7 +95,14 @@ namespace crow
{
size_t found = header.find("boundary=");
if (found)
return header.substr(found + 9);
{
std::string to_return (header.substr(found + 9));
if (to_return[0] == '\"')
{
to_return = to_return.substr(1, to_return.length()-2);
}
return to_return;
}
return std::string();
}

View File

@ -70,6 +70,18 @@ namespace crow
{
self->headers.emplace(std::move(self->header_field), std::move(self->header_value));
}
//NOTE(EDev): it seems that the problem is with crow's policy on closing the connection for HTTP_VERSION < 1.0, the behaviour for that in crow is "don't close the connection, but don't send a keep-alive either"
// HTTP1.1 = always send keep_alive, HTTP1.0 = only send if header exists, HTTP?.? = never send
self->keep_alive = (self->http_major == 1 && self->http_minor == 0) ?
((self->flags & F_CONNECTION_KEEP_ALIVE) ? true : false) :
((self->http_major == 1 && self->http_minor == 1) ? true : false);
// HTTP1.1 = only close if close header exists, HTTP1.0 = always close unless keep_alive header exists, HTTP?.?= never close
self->close_connection = (self->http_major == 1 && self->http_minor == 0) ?
((self->flags & F_CONNECTION_KEEP_ALIVE) ? false : true) :
((self->http_major == 1 && self->http_minor == 1) ? ((self->flags & F_CONNECTION_CLOSE) ? true : false) : false);
self->process_header();
return 0;
}
@ -84,7 +96,7 @@ namespace crow
HTTPParser* self = static_cast<HTTPParser*>(self_);
// url params
self->url = self->raw_url.substr(0, self->raw_url.find("?"));
self->url = self->raw_url.substr(0, self->qs_point != 0 ? self->qs_point : std::string::npos);
self->url_params = query_string(self->raw_url);
self->process_message();
@ -93,7 +105,7 @@ namespace crow
HTTPParser(Handler* handler):
handler_(handler)
{
http_parser_init(this, HTTP_REQUEST);
http_parser_init(this);
}
// return false on error
@ -103,7 +115,6 @@ namespace crow
const static http_parser_settings settings_{
on_message_begin,
on_url,
nullptr,
on_header_field,
on_header_value,
on_headers_complete,
@ -112,7 +123,7 @@ namespace crow
};
int nparsed = http_parser_execute(this, &settings_, buffer, length);
if (http_errno != HPE_OK)
if (http_errno != CHPE_OK)
{
return false;
}
@ -136,12 +147,12 @@ namespace crow
body.clear();
}
void process_header()
inline void process_header()
{
handler_->handle_header();
}
void process_message()
inline void process_message()
{
handler_->handle();
}
@ -149,17 +160,7 @@ namespace crow
/// Take the parsed HTTP request data and convert it to a \ref crow.request
request to_request() const
{
return request{static_cast<HTTPMethod>(method), std::move(raw_url), std::move(url), std::move(url_params), std::move(headers), std::move(body)};
}
bool is_upgrade() const
{
return upgrade;
}
bool check_version(int major, int minor) const
{
return http_major == major && http_minor == minor;
return request{static_cast<HTTPMethod>(method), std::move(raw_url), std::move(url), std::move(url_params), std::move(headers), std::move(body), http_major, http_minor, keep_alive, close_connection, static_cast<bool>(upgrade)};
}
std::string raw_url;
@ -171,6 +172,8 @@ namespace crow
ci_map headers;
query_string url_params; ///< What comes after the `?` in the URL.
std::string body;
bool keep_alive; ///< Whether or not the server should send a `connection: Keep-Alive` header to the client.
bool close_connection; ///< Whether or not the server should shut down the TCP connection once a response is sent.
Handler* handler_; ///< This is currently an HTTP connection object (\ref crow.Connection).
};

View File

@ -19,7 +19,7 @@
namespace crow
{
constexpr const uint16_t INVALID_BP_ID{0xFFFF};
constexpr const uint16_t INVALID_BP_ID{((uint16_t)-1)};
/// A base class for all rules.
@ -1399,7 +1399,7 @@ namespace crow
CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
res = response(301);
// TODO absolute url building
// TODO(ipkn) absolute url building
if (req.get_header_value("Host").empty())
{
res.add_header("Location", req.url + "/");
@ -1515,7 +1515,7 @@ namespace crow
else if (req.method == HTTPMethod::Head)
{
method_actual = HTTPMethod::Get;
res.is_head_response = true;
res.skip_body = true;
}
else if (req.method == HTTPMethod::Options)
{
@ -1601,7 +1601,7 @@ namespace crow
CROW_LOG_INFO << "Redirecting to a url with trailing slash: " << req.url;
res = response(301);
// TODO absolute url building
// TODO(ipkn) absolute url building
if (req.get_header_value("Host").empty())
{
res.add_header("Location", req.url + "/");

View File

@ -1,6 +1,6 @@
#pragma once
// settings for crow
// TODO - replace with runtime config. libucl?
// TODO(ipkn) replace with runtime config. libucl?
/* #ifdef - enables debug mode */
//#define CROW_ENABLE_DEBUG

View File

@ -739,6 +739,7 @@ namespace crow
boost::ireplace_all(data, "LPT7", str_replacement);
boost::ireplace_all(data, "LPT8", str_replacement);
boost::ireplace_all(data, "LPT9", str_replacement);
}
} // namespace utility

View File

@ -230,7 +230,6 @@ namespace crow
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: ";
static std::string crlf = "\r\n";
write_buffers_.emplace_back(header);
write_buffers_.emplace_back(std::move(hello));
write_buffers_.emplace_back(crlf);

View File

@ -10,7 +10,7 @@
using namespace boost;
//TODO SSL test with .pem file
// TODO(EDev): SSL test with .pem file
TEST_CASE("SSL")
{
static char buf[2048];

View File

@ -624,7 +624,7 @@ TEST_CASE("json_read")
CHECK(1 == x.size());
CHECK(false == x.has("mess"));
REQUIRE_THROWS(x["mess"]);
// TODO returning false is better than exception
// TODO(ipkn) returning false is better than exception
// ASSERT_THROW(3 == x["message"]);
CHECK(12 == x["message"].size());
@ -823,7 +823,7 @@ TEST_CASE("json_copy_r_to_w_to_w_to_r")
CHECK("baz" == x["obj"]["other"]);
CHECK("other" == x["obj"]["other"].key());
} // json_copy_r_to_w_to_w_to_r
//TODO maybe combine these
//TODO(EDev): maybe combine these
TEST_CASE("json::wvalue::wvalue(bool)")
{
@ -1779,16 +1779,14 @@ TEST_CASE("send_file")
response res;
req.url = "/jpg";
req.http_ver_major = 1;
app.handle(req, res);
CHECK(200 == res.code);
CHECK("image/jpeg" == res.headers.find("Content-Type")->second);
CHECK(to_string(statbuf.st_size) ==
res.headers.find("Content-Length")->second);
CHECK(to_string(statbuf.st_size) == res.headers.find("Content-Length")->second);
}
//TODO test content
} // send_file
TEST_CASE("stream_response")
@ -1825,7 +1823,7 @@ TEST_CASE("stream_response")
//Total bytes received
unsigned int received = 0;
sendmsg = "GET /test\r\n\r\n";
sendmsg = "GET /test HTTP/1.0\r\n\r\n";
{
asio::streambuf b;
@ -1859,7 +1857,7 @@ TEST_CASE("stream_response")
CHECK(key_response.substr(received - n, n) == s);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
//std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
}
app.stop();
@ -2452,7 +2450,7 @@ TEST_CASE("get_port")
const std::uint16_t port = 12345;
std::thread runTest([&]() {
auto _ = async(launch::async, [&] {
app.port(port).run();
});
@ -2460,7 +2458,6 @@ TEST_CASE("get_port")
CHECK(app.port() == port);
app.stop();
runTest.join();
} // get_port
TEST_CASE("timeout")