Crow/include/crow/parser.h

192 lines
6.7 KiB
C
Raw Normal View History

2014-04-01 13:58:52 +00:00
#pragma once
#include <string>
2014-04-01 12:25:16 +00:00
#include <unordered_map>
#include <boost/algorithm/string.hpp>
#include <algorithm>
2014-04-01 12:25:16 +00:00
#include "crow/http_parser_merged.h"
#include "crow/http_request.h"
2014-04-01 13:58:52 +00:00
2014-04-26 17:19:59 +00:00
namespace crow
{
/// A wrapper for `nodejs/http-parser`.
///
/// Used to generate a \ref crow.request from the TCP socket buffer.
template<typename Handler>
struct HTTPParser : public http_parser
{
static int on_message_begin(http_parser* self_)
{
HTTPParser* self = static_cast<HTTPParser*>(self_);
2014-04-01 12:25:16 +00:00
self->clear();
return 0;
}
static int on_url(http_parser* self_, const char* at, size_t length)
{
HTTPParser* self = static_cast<HTTPParser*>(self_);
self->raw_url.insert(self->raw_url.end(), at, at + length);
return 0;
}
static int on_header_field(http_parser* self_, const char* at, size_t length)
{
HTTPParser* self = static_cast<HTTPParser*>(self_);
2014-04-01 12:25:16 +00:00
switch (self->header_building_state)
{
case 0:
if (!self->header_value.empty())
{
self->headers.emplace(std::move(self->header_field), std::move(self->header_value));
}
self->header_field.assign(at, at + length);
2014-04-01 12:25:16 +00:00
self->header_building_state = 1;
break;
case 1:
self->header_field.insert(self->header_field.end(), at, at + length);
2014-04-01 12:25:16 +00:00
break;
}
return 0;
}
static int on_header_value(http_parser* self_, const char* at, size_t length)
{
HTTPParser* self = static_cast<HTTPParser*>(self_);
2014-04-01 12:25:16 +00:00
switch (self->header_building_state)
{
case 0:
self->header_value.insert(self->header_value.end(), at, at + length);
2014-04-01 12:25:16 +00:00
break;
case 1:
self->header_building_state = 0;
self->header_value.assign(at, at + length);
2014-04-01 12:25:16 +00:00
break;
}
return 0;
}
static int on_headers_complete(http_parser* self_)
{
HTTPParser* self = static_cast<HTTPParser*>(self_);
2014-04-01 12:25:16 +00:00
if (!self->header_field.empty())
{
self->headers.emplace(std::move(self->header_field), std::move(self->header_value));
}
self->set_connection_parameters();
2014-05-02 12:54:25 +00:00
self->process_header();
return 0;
}
static int on_body(http_parser* self_, const char* at, size_t length)
{
HTTPParser* self = static_cast<HTTPParser*>(self_);
self->body.insert(self->body.end(), at, at + length);
return 0;
}
static int on_message_complete(http_parser* self_)
{
HTTPParser* self = static_cast<HTTPParser*>(self_);
// url params
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);
2014-04-01 12:25:16 +00:00
self->process_message();
return 0;
}
HTTPParser(Handler* handler):
handler_(handler)
2014-08-15 02:06:00 +00:00
{
http_parser_init(this);
2014-08-15 02:06:00 +00:00
}
// return false on error
/// Parse a buffer into the different sections of an HTTP request.
2014-08-15 02:06:00 +00:00
bool feed(const char* buffer, int length)
{
const static http_parser_settings settings_{
on_message_begin,
on_url,
on_header_field,
on_header_value,
on_headers_complete,
on_body,
on_message_complete,
2014-08-15 02:06:00 +00:00
};
int nparsed = http_parser_execute(this, &settings_, buffer, length);
if (http_errno != CHPE_OK)
{
return false;
}
2014-04-01 12:25:16 +00:00
return nparsed == length;
}
2014-04-01 12:25:16 +00:00
bool done()
{
2014-08-15 02:06:00 +00:00
return feed(nullptr, 0);
}
2014-04-01 12:25:16 +00:00
void clear()
{
url.clear();
raw_url.clear();
2014-04-01 12:25:16 +00:00
header_field.clear();
header_value.clear();
headers.clear();
url_params.clear();
2014-04-01 12:25:16 +00:00
body.clear();
2022-02-21 14:32:09 +00:00
header_building_state = 0;
qs_point = 0;
http_major = 0;
http_minor = 0;
keep_alive = false;
close_connection = false;
2014-04-01 12:25:16 +00:00
}
inline void process_header()
2014-05-02 12:54:25 +00:00
{
handler_->handle_header();
}
inline void process_message()
2014-04-01 12:25:16 +00:00
{
handler_->handle();
}
inline void set_connection_parameters()
{
//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
keep_alive = (http_major == 1 && http_minor == 0) ?
2022-02-10 22:04:32 +00:00
((flags & F_CONNECTION_KEEP_ALIVE) ? true : false) :
((http_major == 1 && 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
close_connection = (http_major == 1 && http_minor == 0) ?
2022-02-10 22:04:32 +00:00
((flags & F_CONNECTION_KEEP_ALIVE) ? false : true) :
((http_major == 1 && http_minor == 1) ? ((flags & F_CONNECTION_CLOSE) ? true : false) : false);
}
/// Take the parsed HTTP request data and convert it to a \ref crow.request
2014-05-02 12:54:25 +00:00
request to_request() const
2014-04-01 13:58:52 +00:00
{
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)};
2014-05-02 12:54:25 +00:00
}
std::string raw_url;
2014-04-01 12:25:16 +00:00
std::string url;
2014-04-01 12:25:16 +00:00
int header_building_state = 0;
std::string header_field;
std::string header_value;
ci_map headers;
query_string url_params; ///< What comes after the `?` in the URL.
2014-04-01 12:25:16 +00:00
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.
2014-04-01 12:25:16 +00:00
Handler* handler_; ///< This is currently an HTTP connection object (\ref crow.Connection).
};
} // namespace crow