| #pragma once |
| |
| #include <algorithm> |
| #include <boost/algorithm/string.hpp> |
| #include <boost/tokenizer.hpp> |
| #include <string> |
| |
| #include "crow/http_parser_merged.h" |
| #include "crow/http_request.h" |
| |
| namespace crow { |
| template <typename Handler> |
| struct HTTPParser : public http_parser { |
| static int on_message_begin(http_parser* self_) { |
| auto* self = static_cast<HTTPParser*>(self_); |
| self->clear(); |
| return 0; |
| } |
| static int on_url(http_parser* self_, const char* at, size_t length) { |
| auto* 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) { |
| auto* self = static_cast<HTTPParser*>(self_); |
| 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); |
| self->header_building_state = 1; |
| break; |
| case 1: |
| self->header_field.insert(self->header_field.end(), at, at + length); |
| break; |
| } |
| return 0; |
| } |
| static int on_header_value(http_parser* self_, const char* at, |
| size_t length) { |
| auto* self = static_cast<HTTPParser*>(self_); |
| switch (self->header_building_state) { |
| case 0: |
| self->header_value.insert(self->header_value.end(), at, at + length); |
| break; |
| case 1: |
| self->header_building_state = 0; |
| self->header_value.assign(at, at + length); |
| break; |
| } |
| return 0; |
| } |
| static int on_headers_complete(http_parser* self_) { |
| auto* self = static_cast<HTTPParser*>(self_); |
| if (!self->header_field.empty()) { |
| self->headers.emplace(std::move(self->header_field), |
| std::move(self->header_value)); |
| } |
| self->process_header(); |
| return 0; |
| } |
| static int on_body(http_parser* self_, const char* at, size_t length) { |
| auto* self = static_cast<HTTPParser*>(self_); |
| self->body.insert(self->body.end(), at, at + length); |
| return 0; |
| } |
| static int on_message_complete(http_parser* self_) { |
| auto* self = static_cast<HTTPParser*>(self_); |
| |
| // url params |
| self->url = self->raw_url.substr(0, self->raw_url.find('?')); |
| self->url_params = query_string(self->raw_url); |
| |
| self->process_message(); |
| return 0; |
| } |
| explicit HTTPParser(Handler* handler) : http_parser(), handler_(handler) { |
| http_parser_init(this, HTTP_REQUEST); |
| } |
| |
| // return false on error |
| bool feed(const char* buffer, int length) { |
| const static http_parser_settings settings_{ |
| on_message_begin, on_url, |
| nullptr, on_header_field, |
| on_header_value, on_headers_complete, |
| on_body, on_message_complete, |
| }; |
| |
| int nparsed = http_parser_execute(this, &settings_, buffer, length); |
| return nparsed == length; |
| } |
| |
| bool done() { return feed(nullptr, 0); } |
| |
| void clear() { |
| url.clear(); |
| raw_url.clear(); |
| header_building_state = 0; |
| header_field.clear(); |
| header_value.clear(); |
| headers.clear(); |
| url_params.clear(); |
| body.clear(); |
| } |
| |
| void process_header() { handler_->handle_header(); } |
| |
| void process_message() { handler_->handle(); } |
| |
| request to_request() const { |
| return request{(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; |
| } |
| |
| std::string raw_url; |
| std::string url; |
| |
| int header_building_state = 0; |
| std::string header_field; |
| std::string header_value; |
| ci_map headers; |
| query_string url_params; |
| std::string body; |
| |
| Handler* handler_; |
| }; |
| } // namespace crow |