Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[http] add llhttp parser implementation #15814

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1e9d3be
add llhttp
asraa Apr 1, 2021
204e519
add integration test support
asraa Apr 2, 2021
d458ac4
fix deps script weird URL
asraa Apr 2, 2021
591811f
bump llhttp
asraa Apr 5, 2021
9398f11
fix initial comments
asraa Apr 6, 2021
e4f6b56
Merge remote-tracking branch 'upstream/main' into add-llhttp
asraa Apr 10, 2021
397d027
make changes
asraa Apr 11, 2021
f342c0e
fix release date once again
asraa Apr 11, 2021
53232a4
explicitly test both runtime vals
asraa Apr 13, 2021
9365663
Merge remote-tracking branch 'upstream/main' into add-llhttp
asraa Apr 18, 2021
9289b6b
update llhttp
asraa Apr 18, 2021
43e29dc
Merge remote-tracking branch 'upstream/main' into add-llhttp
asraa Apr 22, 2021
d243dda
update llhttp
asraa Apr 22, 2021
dcd8752
Merge remote-tracking branch 'upstream/main' into add-llhttp
asraa Apr 26, 2021
a206ccc
update llhttp version
asraa Apr 26, 2021
2e9421b
add coverage
asraa Apr 28, 2021
8cba94e
Merge remote-tracking branch 'upstream/main' into add-llhttp
asraa Apr 28, 2021
f37f3e4
fix rel notes
asraa Apr 28, 2021
a080df9
fix cov
asraa Apr 28, 2021
f023654
update coverage
asraa Apr 29, 2021
526c73b
remove unused lines
asraa Apr 29, 2021
4882b6a
why format pre doesnt get run in fix_format
asraa Apr 29, 2021
f5fcfaf
fix fuzz coverage
asraa Apr 30, 2021
ddc6d3b
fix fuzz coverage
asraa Apr 30, 2021
6592db0
Merge remote-tracking branch 'upstream/main' into add-llhttp
asraa Apr 30, 2021
0d23c11
Merge remote-tracking branch 'upstream/main' into add-llhttp
asraa May 4, 2021
20c8935
Merge remote-tracking branch 'upstream/main' into add-llhttp
asraa May 10, 2021
5fea125
fix cov
asraa May 10, 2021
6599974
Merge remote-tracking branch 'upstream/main' into add-llhttp
asraa May 19, 2021
b5d59d9
bump llhttp
asraa May 20, 2021
c77400d
Merge remote-tracking branch 'upstream/main' into add-llhttp
asraa Jun 17, 2021
8f84cf4
bump to 6.0.3
asraa Jun 17, 2021
7e480fe
fix bad doc merge
asraa Jun 21, 2021
2a41f11
fix codec fuzzer merge
asraa Jun 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bazel/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,11 @@ config_setting(
define_values = {"FUZZING_ENGINE": "oss-fuzz"},
)

config_setting(
name = "enable_new_http1_parser_in_integration_tests",
values = {"define": "use_new_http1_parser_in_integration_tests=true"},
)

alias(
name = "fuzzing_engine",
actual = select({
Expand Down
2 changes: 2 additions & 0 deletions bazel/envoy_build_system.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ load(
_envoy_select_enable_http3 = "envoy_select_enable_http3",
_envoy_select_google_grpc = "envoy_select_google_grpc",
_envoy_select_hot_restart = "envoy_select_hot_restart",
_envoy_select_new_http1_parser_in_integration_tests = "envoy_select_new_http1_parser_in_integration_tests",
_envoy_select_wasm_cpp_tests = "envoy_select_wasm_cpp_tests",
_envoy_select_wasm_rust_tests = "envoy_select_wasm_rust_tests",
_envoy_select_wasm_v8 = "envoy_select_wasm_v8",
Expand Down Expand Up @@ -205,6 +206,7 @@ def envoy_google_grpc_external_deps():
envoy_select_boringssl = _envoy_select_boringssl
envoy_select_google_grpc = _envoy_select_google_grpc
envoy_select_enable_http3 = _envoy_select_enable_http3
envoy_select_new_http1_parser_in_integration_tests = _envoy_select_new_http1_parser_in_integration_tests
envoy_select_hot_restart = _envoy_select_hot_restart
envoy_select_wasm_cpp_tests = _envoy_select_wasm_cpp_tests
envoy_select_wasm_rust_tests = _envoy_select_wasm_rust_tests
Expand Down
6 changes: 6 additions & 0 deletions bazel/envoy_select.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,9 @@ def envoy_select_wasm_wasmtime(xs):
"@envoy//bazel:wasm_wasmtime": xs,
"//conditions:default": [],
})

def envoy_select_new_http1_parser_in_integration_tests(xs):
return select({
"@envoy//bazel:enable_new_http1_parser_in_integration_tests": xs,
"//conditions:default": [],
})
14 changes: 14 additions & 0 deletions bazel/external/llhttp.BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
licenses(["notice"]) # Apache 2

cc_library(
name = "llhttp",
srcs = [
"src/api.c",
"src/http.c",
"src/llhttp.c",
],
hdrs = ["include/llhttp.h"],
defines = ["LLHTTP_STRICT_MODE"],
includes = ["include"],
visibility = ["//visibility:public"],
)
11 changes: 11 additions & 0 deletions bazel/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ def envoy_dependencies(skip_targets = []):
_com_github_nghttp2_nghttp2()
_com_github_skyapm_cpp2sky()
_com_github_nodejs_http_parser()
_com_github_nodejs_llhttp()
_com_github_alibaba_hessian2_codec()
_com_github_tencent_rapidjson()
_com_github_nlohmann_json()
Expand Down Expand Up @@ -479,6 +480,16 @@ def _com_github_nodejs_http_parser():
actual = "@com_github_nodejs_http_parser//:http_parser",
)

def _com_github_nodejs_llhttp():
external_http_archive(
name = "com_github_nodejs_llhttp",
build_file = "@envoy//bazel/external:llhttp.BUILD",
)
native.bind(
name = "llhttp",
actual = "@com_github_nodejs_llhttp//:llhttp",
)

def _com_github_alibaba_hessian2_codec():
external_http_archive("com_github_alibaba_hessian2_codec")
native.bind(
Expand Down
12 changes: 12 additions & 0 deletions bazel/repository_locations.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,18 @@ REPOSITORY_LOCATIONS_SPEC = dict(
release_date = "2020-07-10",
cpe = "cpe:2.3:a:nodejs:node.js:*",
),
com_github_nodejs_llhttp = dict(
project_name = "llhttp",
project_desc = "Parser for HTTP messages written in C",
project_url = "https://github.com/nodejs/llhttp",
version = "5.1.0",
sha256 = "7140bbafa30f9cad4d20a0065953bd19bcc11203ab9a901ce7dea1b8182c8c78",
strip_prefix = "llhttp-release-v{version}",
urls = ["https://github.com/nodejs/llhttp/archive/release/v{version}.tar.gz"],
use_category = ["dataplane_core"],
release_date = "2021-04-06",
cpe = "cpe:2.3:a:nodejs:node.js:*",
),
asraa marked this conversation as resolved.
Show resolved Hide resolved
com_github_alibaba_hessian2_codec = dict(
project_name = "hessian2-codec",
project_desc = "hessian2-codec is a C++ library for hessian2 codec",
Expand Down
1 change: 1 addition & 0 deletions ci/do_ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ elif [[ "$CI_TARGET" == "bazel.compile_time_options" ]]; then
"--define" "deprecated_features=disabled"
"--define" "tcmalloc=gperftools"
"--define" "zlib=ng"
"--define" "use_new_http1_parser_in_integration_tests=true"
"--@envoy//bazel:http3=False"
"--@envoy//source/extensions/filters/http/kill_request:enabled"
"--test_env=ENVOY_HAS_EXTRA_EXTENSIONS=true")
Expand Down
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ New Features
* ext_authz: added :ref:`allowed_client_headers_on_success <envoy_v3_api_field_extensions.filters.http.ext_authz.v3.AuthorizationResponse.allowed_client_headers_on_success>` to support sending response headers to downstream clients on OK external authorization checks via HTTP.
* grpc_json_transcoder: added :ref:`request_validation_options <envoy_v3_api_field_extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder.request_validation_options>` to reject invalid requests early.
* grpc_json_transcoder: filter can now be configured on per-route/per-vhost level as well. Leaving empty list of services in the filter configuration disables transcoding on the specific route.
* http: added new HTTP/1.1 parser llhttp. This is off by default and can be enabled by setting the `envoy.reloadable_features.enable_new_http1_parser` runtime feature to true.
* http: added support for `Envoy::ScopeTrackedObject` for HTTP/1 and HTTP/2 dispatching. Crashes while inside the dispatching loop should dump debug information. Furthermore, HTTP/1 and HTTP/2 clients now dumps the originating request whose response from the upstream caused Envoy to crash.
* http: added support for :ref:`preconnecting <envoy_v3_api_msg_config.cluster.v3.Cluster.PreconnectPolicy>`. Preconnecting is off by default, but recommended for clusters serving latency-sensitive traffic, especially if using HTTP/1.1.
* http: added support for stream filters to mutate the cached route set by HCM route resolution. Useful for filters in a filter chain that want to override specific methods/properties of a route. See :ref:`http route mutation <arch_overview_http_filters_route_mutation>` docs for more information.
Expand Down
12 changes: 12 additions & 0 deletions source/common/http/http1/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ envoy_cc_library(
":header_formatter_lib",
":legacy_parser_lib",
":parser_interface",
":parser_lib",
"//include/envoy/buffer:buffer_interface",
"//include/envoy/common:scope_tracker_interface",
"//include/envoy/http:codec_interface",
Expand Down Expand Up @@ -116,3 +117,14 @@ envoy_cc_library(
"//source/common/common:assert_lib",
],
)

envoy_cc_library(
name = "parser_lib",
srcs = ["parser_impl.cc"],
hdrs = ["parser_impl.h"],
external_deps = ["llhttp"],
deps = [
":parser_interface",
"//source/common/common:assert_lib",
],
)
10 changes: 9 additions & 1 deletion source/common/http/http1/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "common/http/headers.h"
#include "common/http/http1/header_formatter.h"
#include "common/http/http1/legacy_parser_impl.h"
#include "common/http/http1/parser_impl.h"
#include "common/http/utility.h"
#include "common/runtime/runtime_features.h"

Expand Down Expand Up @@ -466,7 +467,11 @@ ConnectionImpl::ConnectionImpl(Network::Connection& connection, CodecStats& stat
[]() -> void { /* TODO(adisuissa): Handle overflow watermark */ })),
max_headers_kb_(max_headers_kb), max_headers_count_(max_headers_count) {
output_buffer_->setWatermarks(connection.bufferLimit());
parser_ = std::make_unique<LegacyHttpParserImpl>(type, this);
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.enable_new_http1_parser")) {
parser_ = std::make_unique<HttpParserImpl>(type, this);
} else {
parser_ = std::make_unique<LegacyHttpParserImpl>(type, this);
}
}

Status ConnectionImpl::completeLastHeader() {
Expand Down Expand Up @@ -659,6 +664,8 @@ Status ConnectionImpl::onHeaderValue(const char* data, size_t length) {
}

absl::string_view header_value{data, length};
// TODO(5155): This can be removed when http_parser is removed. llhttp enables strict validation
// of HTTP header values.
if (!Http::HeaderUtility::headerValueIsValid(header_value)) {
ENVOY_CONN_LOG(debug, "invalid header value: {}", connection_, header_value);
error_code_ = Http::Code::BadRequest;
Expand Down Expand Up @@ -767,6 +774,7 @@ StatusOr<ParserStatus> ConnectionImpl::onHeadersComplete() {
return codecProtocolError("http/1.1 protocol error: unsupported transfer encoding");
}
}
parser_->setHasContentLength(request_or_response_headers.ContentLength() != nullptr);

auto statusor = onHeadersCompleteBase();
if (!statusor.ok()) {
Expand Down
1 change: 1 addition & 0 deletions source/common/http/http1/legacy_parser_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class LegacyHttpParserImpl : public Parser {
int httpMajor() const override;
int httpMinor() const override;
absl::optional<uint64_t> contentLength() const override;
void setHasContentLength(bool) override{};
bool isChunked() const override;
absl::string_view methodName() const override;
absl::string_view errnoName(int rc) const override;
Expand Down
4 changes: 4 additions & 0 deletions source/common/http/http1/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ class Parser {
// Returns the number of bytes in the body. absl::nullopt if no Content-Length header
virtual absl::optional<uint64_t> contentLength() const PURE;

// Indicated that Content-Length header is present. This is used to differentiate between an unset
// Content-Length and a 0 value.
virtual void setHasContentLength(bool val) PURE;

// Returns whether headers are chunked.
virtual bool isChunked() const PURE;

Expand Down
Loading