Skip to content

Commit

Permalink
feat http: replace http-parser with llhttp
Browse files Browse the repository at this point in the history
c2d63a07bf35116d8ec97b0f030899688bf57558
  • Loading branch information
hrustim25 committed Mar 20, 2024
1 parent 223bf7a commit e6b635c
Show file tree
Hide file tree
Showing 17 changed files with 1,520 additions and 125 deletions.
7 changes: 6 additions & 1 deletion .mapping.json
Original file line number Diff line number Diff line change
Expand Up @@ -1469,7 +1469,6 @@
"external-deps/GrpcChannelz.yaml":"taxi/uservices/userver/external-deps/GrpcChannelz.yaml",
"external-deps/GssApi.yaml":"taxi/uservices/userver/external-deps/GssApi.yaml",
"external-deps/Hiredis.yaml":"taxi/uservices/userver/external-deps/Hiredis.yaml",
"external-deps/Http_parser.yaml":"taxi/uservices/userver/external-deps/Http_parser.yaml",
"external-deps/Jemalloc.yaml":"taxi/uservices/userver/external-deps/Jemalloc.yaml",
"external-deps/LibEv.yaml":"taxi/uservices/userver/external-deps/LibEv.yaml",
"external-deps/Nghttp2.yaml":"taxi/uservices/userver/external-deps/Nghttp2.yaml",
Expand Down Expand Up @@ -2976,6 +2975,12 @@
"third_party/function_backports/README.md":"taxi/uservices/userver/third_party/function_backports/README.md",
"third_party/function_backports/include/function_backports/function_ref.h":"taxi/uservices/userver/third_party/function_backports/include/function_backports/function_ref.h",
"third_party/function_backports/include/function_backports/functional_base.h":"taxi/uservices/userver/third_party/function_backports/include/function_backports/functional_base.h",
"third_party/http-parser/AUTHORS":"taxi/uservices/userver/third_party/http-parser/AUTHORS",
"third_party/http-parser/CMakeLists.txt":"taxi/uservices/userver/third_party/http-parser/CMakeLists.txt",
"third_party/http-parser/LICENSE-MIT":"taxi/uservices/userver/third_party/http-parser/LICENSE-MIT",
"third_party/http-parser/README.md":"taxi/uservices/userver/third_party/http-parser/README.md",
"third_party/http-parser/include/http_parser.h":"taxi/uservices/userver/third_party/http-parser/include/http_parser.h",
"third_party/http-parser/src/http_parser.c":"taxi/uservices/userver/third_party/http-parser/src/http_parser.c",
"third_party/libc_workarounds/.yandex_meta/devtools.copyrights.report":"taxi/uservices/userver/third_party/libc_workarounds/.yandex_meta/devtools.copyrights.report",
"third_party/libc_workarounds/.yandex_meta/devtools.licenses.report":"taxi/uservices/userver/third_party/libc_workarounds/.yandex_meta/devtools.licenses.report",
"third_party/libc_workarounds/README.md":"taxi/uservices/userver/third_party/libc_workarounds/README.md",
Expand Down
3 changes: 3 additions & 0 deletions THIRD_PARTY.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ Copyright (c) 2019-2022 Lars Melchior and contributors
**[llhttp library](https://github.com/nodejs/llhttp)**
Copyright (c) 2018 Fedor Indutny.

**[http-parser library](https://github.com/nodejs/http-parser)**
Copyright (c) Joyent, Inc. and other Node contributors.

**[Doxygen Awesome](https://github.com/jothepro/doxygen-awesome-css)**
Copyright (c) 2021 - 2023 jothepro

Expand Down
1 change: 0 additions & 1 deletion cmake/install/userver-core-config.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ find_package(CURL "7.68" REQUIRED)
find_package(ZLIB REQUIRED)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/..")
find_package(Http_Parser REQUIRED)
find_package(Nghttp2 REQUIRED)
find_package(LibEv REQUIRED)
find_package(UserverGTest REQUIRED)
Expand Down
10 changes: 4 additions & 6 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ if (USERVER_CONAN)
find_package(c-ares REQUIRED)
find_package(CURL REQUIRED)
find_package(cryptopp REQUIRED)
find_package(http_parser REQUIRED)
find_package(libnghttp2 REQUIRED)
find_package(libev REQUIRED)

Expand All @@ -73,7 +72,6 @@ else()
include(SetupCAres)
include(SetupCURL)
include(SetupCryptoPP)
find_package(Http_Parser REQUIRED)
find_package(Nghttp2 REQUIRED)
find_package(LibEv REQUIRED)
endif()
Expand Down Expand Up @@ -116,7 +114,10 @@ target_link_libraries(${PROJECT_NAME}
)

add_subdirectory(${USERVER_THIRD_PARTY_DIRS}/llhttp llhttp)
target_link_libraries(${PROJECT_NAME} PUBLIC userver-llhttp)

add_subdirectory(${USERVER_THIRD_PARTY_DIRS}/http-parser http-parser)

target_link_libraries(${PROJECT_NAME} PRIVATE userver-librseq userver-http-parser PUBLIC userver-llhttp)

if (USERVER_FEATURE_UBOOST_CORO)
add_subdirectory(${USERVER_THIRD_PARTY_DIRS}/uboost_coro uboost_coro_build)
Expand Down Expand Up @@ -148,7 +149,6 @@ if (USERVER_CONAN)
concurrentqueue::concurrentqueue
PRIVATE
cryptopp::cryptopp
http_parser::http_parser
libev::libev
libnghttp2::nghttp2
)
Expand All @@ -158,7 +158,6 @@ else()
c-ares::cares
PRIVATE
CryptoPP
Http_Parser
Nghttp2
LibEv
)
Expand Down Expand Up @@ -300,7 +299,6 @@ _userver_directory_install(COMPONENT core FILES
"${USERVER_ROOT_DIR}/cmake/UserverTestsuite.cmake"
"${USERVER_ROOT_DIR}/cmake/install/userver-core-config.cmake"
"${CMAKE_BINARY_DIR}/cmake_generated/Findc-ares.cmake"
"${CMAKE_BINARY_DIR}/cmake_generated/FindHttp_Parser.cmake"
"${CMAKE_BINARY_DIR}/cmake_generated/FindNghttp2.cmake"
"${CMAKE_BINARY_DIR}/cmake_generated/FindLibEv.cmake"
"${CMAKE_BINARY_DIR}/cmake_generated/FindUserverGTest.cmake"
Expand Down
26 changes: 18 additions & 8 deletions core/src/server/http/http_request_constructor.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "http_request_constructor.hpp"

#include <http_parser.h>

#include <algorithm>

#include <userver/http/common_headers.hpp>
Expand Down Expand Up @@ -41,13 +43,19 @@ void StripDuplicateStartingSlashes(std::string& s) {

} // namespace

struct HttpRequestConstructor::HttpParserUrl {
http_parser_url parsed_url;
};

HttpRequestConstructor::HttpRequestConstructor(
Config config, const HandlerInfoIndex& handler_info_index,
request::ResponseDataAccounter& data_accounter)
: config_(config),
handler_info_index_(handler_info_index),
request_(std::make_shared<HttpRequestImpl>(data_accounter)) {}

HttpRequestConstructor::~HttpRequestConstructor() = default;

void HttpRequestConstructor::SetMethod(HttpMethod method) {
request_->method_ = method;
}
Expand All @@ -72,15 +80,16 @@ void HttpRequestConstructor::ParseUrl() {
LOG_TRACE() << "parse path from '" << request_->url_ << '\'';
if (http_parser_parse_url(request_->url_.data(), request_->url_.size(),
request_->method_ == HttpMethod::kConnect,
&parsed_url_)) {
&parsed_url_pimpl_->parsed_url)) {
SetStatus(Status::kParseUrlError);
throw std::runtime_error("error in http_parser_parse_url() for url '" +
request_->url_ + '\'');
}

if (parsed_url_.field_set & (1 << http_parser_url_fields::UF_PATH)) {
const auto& str_info =
parsed_url_.field_data[http_parser_url_fields::UF_PATH];
if (parsed_url_pimpl_->parsed_url.field_set &
(1 << http_parser_url_fields::UF_PATH)) {
const auto& str_info = parsed_url_pimpl_->parsed_url
.field_data[http_parser_url_fields::UF_PATH];

request_->request_path_ = request_->url_.substr(str_info.off, str_info.len);
StripDuplicateStartingSlashes(request_->request_path_);
Expand Down Expand Up @@ -187,7 +196,7 @@ void HttpRequestConstructor::FinalizeImpl() {
}

try {
ParseArgs(parsed_url_);
ParseArgs(*parsed_url_pimpl_);
if (config_.parse_args_from_body) {
if (!config_.decompress_request || !request_->IsBodyCompressed())
ParseArgs(request_->request_body_.data(),
Expand Down Expand Up @@ -226,9 +235,10 @@ void HttpRequestConstructor::FinalizeImpl() {
}
}

void HttpRequestConstructor::ParseArgs(const http_parser_url& url) {
if (url.field_set & (1 << http_parser_url_fields::UF_QUERY)) {
const auto& str_info = url.field_data[http_parser_url_fields::UF_QUERY];
void HttpRequestConstructor::ParseArgs(const HttpParserUrl& url) {
if (url.parsed_url.field_set & (1 << http_parser_url_fields::UF_QUERY)) {
const auto& str_info =
url.parsed_url.field_data[http_parser_url_fields::UF_QUERY];
ParseArgs(request_->url_.data() + str_info.off, str_info.len);
LOG_TRACE() << "query="
<< request_->url_.substr(str_info.off, str_info.len);
Expand Down
10 changes: 6 additions & 4 deletions core/src/server/http/http_request_constructor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

#include <memory>

#include <http_parser.h>

#include <userver/http/parser/http_request_parse_args.hpp>
#include <userver/server/http/http_method.hpp>
#include <userver/server/request/request_config.hpp>
Expand Down Expand Up @@ -39,6 +37,8 @@ class HttpRequestConstructor final : public request::RequestConstructor {
const HandlerInfoIndex& handler_info_index,
request::ResponseDataAccounter& data_accounter);

~HttpRequestConstructor() override;

HttpRequestConstructor(HttpRequestConstructor&&) = delete;
HttpRequestConstructor& operator=(HttpRequestConstructor&&) = delete;

Expand All @@ -57,9 +57,11 @@ class HttpRequestConstructor final : public request::RequestConstructor {
std::shared_ptr<request::RequestBase> Finalize() override;

private:
struct HttpParserUrl;

void FinalizeImpl();

void ParseArgs(const http_parser_url& url);
void ParseArgs(const HttpParserUrl& url);
void ParseArgs(const char* data, size_t size);
void AddHeader();
void ParseCookies();
Expand All @@ -74,7 +76,7 @@ class HttpRequestConstructor final : public request::RequestConstructor {
Config config_;
const HandlerInfoIndex& handler_info_index_;

http_parser_url parsed_url_{};
utils::FastPimpl<HttpParserUrl, 60, 8> parsed_url_pimpl_;
std::string header_field_;
std::string header_value_;
bool header_field_flag_ = false;
Expand Down
56 changes: 28 additions & 28 deletions core/src/server/http/http_request_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace server::http {

namespace {

HttpMethod ConvertHttpMethod(http_method method) {
HttpMethod ConvertHttpMethod(llhttp_method method) {
switch (method) {
case HTTP_DELETE:
return HttpMethod::kDelete;
Expand All @@ -36,8 +36,9 @@ HttpMethod ConvertHttpMethod(http_method method) {

} // namespace

const http_parser_settings HttpRequestParser::parser_settings = []() {
http_parser_settings settings{};
const llhttp_settings_t HttpRequestParser::parser_settings = []() {
llhttp_settings_t settings{};
llhttp_settings_init(&settings);
settings.on_message_begin = HttpRequestParser::OnMessageBegin;
settings.on_url = HttpRequestParser::OnUrl;
settings.on_header_field = HttpRequestParser::OnHeaderField;
Expand All @@ -58,16 +59,17 @@ HttpRequestParser::HttpRequestParser(
on_new_request_cb_(std::move(on_new_request_cb)),
stats_(stats),
data_accounter_(data_accounter) {
http_parser_init(&parser_, HTTP_REQUEST);
llhttp_init(&parser_, HTTP_REQUEST, &parser_settings);
parser_.data = this;
}

bool HttpRequestParser::Parse(const char* data, size_t size) {
size_t parsed = http_parser_execute(&parser_, &parser_settings, data, size);
if (parsed != size) {
const auto err = llhttp_execute(&parser_, data, size);
if (err != HPE_OK) {
const auto parsed =
static_cast<size_t>(llhttp_get_error_pos(&parser_) - data + 1);
LOG_WARNING() << "parsed=" << parsed << " size=" << size
<< " error_description="
<< http_errno_description(HTTP_PARSER_ERRNO(&parser_));
<< " error_description=" << llhttp_errno_name(err);
FinalizeRequest();
return false;
}
Expand All @@ -78,62 +80,61 @@ bool HttpRequestParser::Parse(const char* data, size_t size) {
return true;
}

int HttpRequestParser::OnMessageBegin(http_parser* p) {
int HttpRequestParser::OnMessageBegin(llhttp_t* p) {
auto* http_request_parser = static_cast<HttpRequestParser*>(p->data);
UASSERT(http_request_parser != nullptr);
return http_request_parser->OnMessageBeginImpl(p);
}

int HttpRequestParser::OnHeadersComplete(http_parser* p) {
int HttpRequestParser::OnHeadersComplete(llhttp_t* p) {
auto* http_request_parser = static_cast<HttpRequestParser*>(p->data);
UASSERT(http_request_parser != nullptr);
return http_request_parser->OnHeadersCompleteImpl(p);
}

int HttpRequestParser::OnMessageComplete(http_parser* p) {
int HttpRequestParser::OnMessageComplete(llhttp_t* p) {
auto* http_request_parser = static_cast<HttpRequestParser*>(p->data);
UASSERT(http_request_parser != nullptr);
return http_request_parser->OnMessageCompleteImpl(p);
}

int HttpRequestParser::OnUrl(http_parser* p, const char* data, size_t size) {
int HttpRequestParser::OnUrl(llhttp_t* p, const char* data, size_t size) {
auto* http_request_parser = static_cast<HttpRequestParser*>(p->data);
UASSERT(http_request_parser != nullptr);
return http_request_parser->OnUrlImpl(p, data, size);
}

int HttpRequestParser::OnHeaderField(http_parser* p, const char* data,
int HttpRequestParser::OnHeaderField(llhttp_t* p, const char* data,
size_t size) {
auto* http_request_parser = static_cast<HttpRequestParser*>(p->data);
UASSERT(http_request_parser != nullptr);
return http_request_parser->OnHeaderFieldImpl(p, data, size);
}

int HttpRequestParser::OnHeaderValue(http_parser* p, const char* data,
int HttpRequestParser::OnHeaderValue(llhttp_t* p, const char* data,
size_t size) {
auto* http_request_parser = static_cast<HttpRequestParser*>(p->data);
UASSERT(http_request_parser != nullptr);
return http_request_parser->OnHeaderValueImpl(p, data, size);
}

int HttpRequestParser::OnBody(http_parser* p, const char* data, size_t size) {
int HttpRequestParser::OnBody(llhttp_t* p, const char* data, size_t size) {
auto* http_request_parser = static_cast<HttpRequestParser*>(p->data);
UASSERT(http_request_parser != nullptr);
return http_request_parser->OnBodyImpl(p, data, size);
}

int HttpRequestParser::OnMessageBeginImpl(http_parser*) {
int HttpRequestParser::OnMessageBeginImpl(llhttp_t*) {
LOG_TRACE() << "message begin";
CreateRequestConstructor();
return 0;
}

int HttpRequestParser::OnUrlImpl(http_parser* p, const char* data,
size_t size) {
int HttpRequestParser::OnUrlImpl(llhttp_t* p, const char* data, size_t size) {
UASSERT(request_constructor_);
LOG_TRACE() << "url: '" << std::string_view(data, size) << '\'';
request_constructor_->SetMethod(
ConvertHttpMethod(static_cast<http_method>(p->method)));
ConvertHttpMethod(static_cast<llhttp_method>(p->method)));
try {
request_constructor_->AppendUrl(data, size);
} catch (const std::exception& ex) {
Expand All @@ -143,7 +144,7 @@ int HttpRequestParser::OnUrlImpl(http_parser* p, const char* data,
return 0;
}

int HttpRequestParser::OnHeaderFieldImpl(http_parser* p, const char* data,
int HttpRequestParser::OnHeaderFieldImpl(llhttp_t* p, const char* data,
size_t size) {
UASSERT(request_constructor_);
LOG_TRACE() << "header field: '" << std::string_view(data, size) << "'";
Expand All @@ -157,7 +158,7 @@ int HttpRequestParser::OnHeaderFieldImpl(http_parser* p, const char* data,
return 0;
}

int HttpRequestParser::OnHeaderValueImpl(http_parser* p, const char* data,
int HttpRequestParser::OnHeaderValueImpl(llhttp_t* p, const char* data,
size_t size) {
UASSERT(request_constructor_);
if (!CheckUrlComplete(p)) return -1;
Expand All @@ -171,7 +172,7 @@ int HttpRequestParser::OnHeaderValueImpl(http_parser* p, const char* data,
return 0;
}

int HttpRequestParser::OnHeadersCompleteImpl(http_parser* p) {
int HttpRequestParser::OnHeadersCompleteImpl(llhttp_t* p) {
UASSERT(request_constructor_);
if (!CheckUrlComplete(p)) return -1;
try {
Expand All @@ -184,8 +185,7 @@ int HttpRequestParser::OnHeadersCompleteImpl(http_parser* p) {
return 0;
}

int HttpRequestParser::OnBodyImpl(http_parser* p, const char* data,
size_t size) {
int HttpRequestParser::OnBodyImpl(llhttp_t* p, const char* data, size_t size) {
UASSERT(request_constructor_);
if (!CheckUrlComplete(p)) return -1;
LOG_TRACE() << "body: '" << std::string_view(data, size) << "'";
Expand All @@ -198,12 +198,12 @@ int HttpRequestParser::OnBodyImpl(http_parser* p, const char* data,
return 0;
}

int HttpRequestParser::OnMessageCompleteImpl(http_parser* p) {
int HttpRequestParser::OnMessageCompleteImpl(llhttp_t* p) {
UASSERT(request_constructor_);
if (p->upgrade) {
return -1; // error
}
request_constructor_->SetIsFinal(!http_should_keep_alive(p));
request_constructor_->SetIsFinal(!llhttp_should_keep_alive(p));
if (!CheckUrlComplete(p)) return -1;
LOG_TRACE() << "message complete";
if (!FinalizeRequest()) return -1;
Expand All @@ -217,11 +217,11 @@ void HttpRequestParser::CreateRequestConstructor() {
url_complete_ = false;
}

bool HttpRequestParser::CheckUrlComplete(http_parser* p) {
bool HttpRequestParser::CheckUrlComplete(llhttp_t* p) {
if (url_complete_) return true;
url_complete_ = true;
request_constructor_->SetMethod(
ConvertHttpMethod(static_cast<http_method>(p->method)));
ConvertHttpMethod(static_cast<llhttp_method>(p->method)));
request_constructor_->SetHttpMajor(p->http_major);
request_constructor_->SetHttpMinor(p->http_minor);
try {
Expand Down
Loading

0 comments on commit e6b635c

Please sign in to comment.