mirror of
https://github.com/CrowCpp/Crow.git
synced 2024-06-07 21:10:44 +00:00
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:
parent
05274f1900
commit
4cdde73388
@ -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
|
||||
|
@ -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
@ -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)
|
||||
{
|
||||
|
@ -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 = "";
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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).
|
||||
};
|
||||
|
@ -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 + "/");
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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];
|
||||
|
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user