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

http1 encode trailers in chunk encoding #8667

Merged
merged 69 commits into from
Dec 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
08ae97c
http1 encode trailers in chunk encoding
Chuongv Oct 18, 2019
5de8533
Add integration test for trailers http1<--->Envoy<--->http2
Chuongv Oct 24, 2019
9c550cb
Use tcp client for integration test for trailers
Chuongv Oct 24, 2019
f0d53cd
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Oct 25, 2019
f9c9f48
Update tests for trailers http1
Chuongv Oct 25, 2019
e002b4b
Undo changes in codec_impl
Chuongv Oct 25, 2019
10a9089
Implementation of trailers support for http1
Chuongv Oct 28, 2019
de29acb
Trailer code clean up
Chuongv Oct 28, 2019
96913f9
Remove unnecessary check for trailers
Chuongv Oct 28, 2019
77ec2ac
Keep the trailer processing on MessageComplete
Chuongv Oct 28, 2019
8b03163
Remove trailer logic in onHeaderComplete
Chuongv Oct 28, 2019
6955a1a
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Oct 28, 2019
92421a8
Accomodate trailers in grpc reverse bridge filter
Chuongv Oct 31, 2019
e7cb695
Reuse encodeFormattedHeaders in encodeTrailers
Chuongv Oct 31, 2019
592e315
Fix the formatting
Chuongv Oct 31, 2019
9e2a48a
Refactor and clean up redundant parser fields
Chuongv Oct 31, 2019
cd56fd8
Add empty space to match the creation of trailers
Chuongv Oct 31, 2019
e9c27e4
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Oct 31, 2019
c70e7b0
Update tests to account for trailers in H1
Chuongv Oct 31, 2019
557e027
Fix formatting
Chuongv Oct 31, 2019
614ff9b
Refactor trailers based on feedback
Chuongv Nov 1, 2019
4205198
Move trailer tests to appropriate test file
Chuongv Nov 1, 2019
b63375d
Fix typo
Chuongv Nov 3, 2019
92d4efc
Add flag to disable support of trailers
Chuongv Nov 8, 2019
dae9233
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Nov 8, 2019
07a0e79
Update docs to include information about encoding h1 trailers
Chuongv Nov 8, 2019
e124271
Update encode trailer docs to include ref link
Chuongv Nov 8, 2019
962afd2
Add next free field to pass formatter
Chuongv Nov 8, 2019
fce0ddb
Correct the label in the version history docs
Chuongv Nov 8, 2019
9f990a5
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Nov 8, 2019
de672b8
Correct the label in the version history docs
Chuongv Nov 11, 2019
b41c492
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Nov 11, 2019
ee426ea
Set the enable_trailers so it defaults to false
Chuongv Nov 13, 2019
1296eff
Switch flag to enable_trailers_ for unit test
Chuongv Nov 13, 2019
ce552b8
Fix spelling
Chuongv Nov 13, 2019
1b87726
Refactor and clean up integration tests
Chuongv Nov 13, 2019
fd5c7de
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Nov 13, 2019
4ed8b97
Move decoding trailers to be guarded by configuration flag
Chuongv Nov 16, 2019
2f18c1c
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Nov 16, 2019
dfbd03e
Don't check for trailers in H2/H1 tests
Chuongv Nov 16, 2019
76a0a6d
Clear out trailers in grpc-web
Chuongv Nov 21, 2019
3041acb
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Nov 21, 2019
e59101d
Address comments and move tests
Chuongv Nov 24, 2019
c784ce4
Use bitfield for enable_trailers
Chuongv Nov 25, 2019
f28af8f
Add more unit and integration tests and address PR feedback
Chuongv Nov 26, 2019
86fed8a
Allow configuration of incomplete streams for Autonomous Upstream
Chuongv Nov 26, 2019
cee19aa
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Nov 26, 2019
32e0c7a
Use bitfields for processing_trailers
Chuongv Nov 26, 2019
4388cf4
Update todo for clearing trailers
Chuongv Nov 26, 2019
8b13bdf
Add some helpful comments on codec_impl.h
Chuongv Nov 27, 2019
4aadf64
Merge branch 'AutonomousStreamIncompleteStream' into http1-trailers-c…
Chuongv Nov 27, 2019
14ac61b
Allow incomplete streams in PipelineTrailers test
Chuongv Nov 27, 2019
41135aa
Move enable_trailers_ for optimal packing
Chuongv Dec 4, 2019
678222d
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Dec 4, 2019
dd6844a
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Dec 4, 2019
b90f535
Address PR comments
Chuongv Dec 6, 2019
fbdf83d
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Dec 6, 2019
9e8be74
Docs not liking the identations
Chuongv Dec 6, 2019
c179840
Fix the identation in the api docs for v3alpha
Chuongv Dec 6, 2019
915fb06
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Dec 9, 2019
ce5cb82
Use set trailers
Chuongv Dec 10, 2019
b406f71
Update docs with attention for enable_trailers
Chuongv Dec 10, 2019
5447d69
Use the new trailers.clear() method
Chuongv Dec 10, 2019
be14d7e
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Dec 10, 2019
0c717f8
Address PR comments
Chuongv Dec 12, 2019
a347b6f
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Dec 13, 2019
fb321d8
Add tests for reverse bridge filter with trailers
Chuongv Dec 13, 2019
d1c66e8
Move trailers/headers to constant strings. Use Consistent Http1 naming
Chuongv Dec 16, 2019
15b6715
Merge remote-tracking branch 'envoy/master' into http1-trailers-chunk…
Chuongv Dec 16, 2019
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
12 changes: 12 additions & 0 deletions api/envoy/api/v2/core/protocol.proto
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ message HttpProtocolOptions {
google.protobuf.UInt32Value max_headers_count = 2 [(validate.rules).uint32 = {gte: 1}];
}

// [#next-free-field: 6]
message Http1ProtocolOptions {
message HeaderKeyFormat {
message ProperCaseWords {
Expand Down Expand Up @@ -83,6 +84,17 @@ message Http1ProtocolOptions {
// Describes how the keys for response headers should be formatted. By default, all header keys
// are lower cased.
HeaderKeyFormat header_key_format = 4;

// Enables trailers for HTTP/1. By default the HTTP/1 codec drops proxied trailers.
//
// .. attention::
//
// Note that this only happens when Envoy is chunk encoding which occurs when:
// - The request is HTTP/1.1.
// - Is neither a HEAD only request nor a HTTP Upgrade.
// - Not a response to a HEAD request.
// - The content length header is not present.
bool enable_trailers = 5;
mattklein123 marked this conversation as resolved.
Show resolved Hide resolved
}

// [#next-free-field: 13]
Expand Down
12 changes: 12 additions & 0 deletions api/envoy/api/v3alpha/core/protocol.proto
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ message HttpProtocolOptions {
google.protobuf.UInt32Value max_headers_count = 2 [(validate.rules).uint32 = {gte: 1}];
}

// [#next-free-field: 6]
message Http1ProtocolOptions {
option (udpa.annotations.versioning).previous_message_type =
"envoy.api.v2.core.Http1ProtocolOptions";
Expand Down Expand Up @@ -98,6 +99,17 @@ message Http1ProtocolOptions {
// Describes how the keys for response headers should be formatted. By default, all header keys
// are lower cased.
HeaderKeyFormat header_key_format = 4;

// Enables trailers for HTTP/1. By default the HTTP/1 codec drops proxied trailers.
//
// .. attention::
//
// Note that this only happens when Envoy is chunk encoding which occurs when:
// - The request is HTTP/1.1.
// - Is neither a HEAD only request nor a HTTP Upgrade.
// - Not a response to a HEAD request.
// - The content length header is not present.
bool enable_trailers = 5;
}

// [#next-free-field: 13]
Expand Down
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Version history
* decompressor: remove decompressor hard assert failure and replace with an error flag.
* ext_authz: added :ref:`configurable ability<envoy_api_field_config.filter.http.ext_authz.v2.ExtAuthz.include_peer_certificate>` to send the :ref:`certificate<envoy_api_field_service.auth.v2.AttributeContext.Peer.certificate>` to the `ext_authz` service.
* health check: gRPC health checker sets the gRPC deadline to the configured timeout duration.
* http: added support for http1 trailers. To enable use :ref:`enable_trailers <envoy_api_field_core.Http1ProtocolOptions.enable_trailers>`.
* http: added the ability to sanitize headers nominated by the Connection header. This new behavior is guarded by envoy.reloadable_features.connection_header_sanitization which defaults to true.
* http: blocks unsupported transfer-encodings. Can be reverted temporarily by setting runtime feature `envoy.reloadable_features.reject_unsupported_transfer_encodings` to false.
* http: support :ref:`auto_host_rewrite_header<envoy_api_field_config.filter.http.dynamic_forward_proxy.v2alpha.PerRouteConfig.auto_host_rewrite_header>` in the dynamic forward proxy.
Expand Down
6 changes: 6 additions & 0 deletions include/envoy/http/codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ struct Http1Settings {
bool accept_http_10_{false};
// Set a default host if no Host: header is present for HTTP/1.0 requests.`
std::string default_host_for_http_10_;
// Encode trailers in Http. By default the HTTP/1 codec drops proxied trailers.
// Note that this only happens when Envoy is chunk encoding which occurs when:
// - The request is HTTP/1.1
// - Is neither a HEAD only request nor a HTTP Upgrade
// - Not a HEAD request
bool enable_trailers_{false};

enum class HeaderKeyFormat {
// By default no formatting is performed, presenting all headers in lowercase (as Envoy
Expand Down
110 changes: 88 additions & 22 deletions source/common/http/http1/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ struct Http1ResponseCodeDetailValues {
const absl::string_view InvalidUrl = "http1.invalid_url";
};

struct Http1HeaderTypesValues {
const absl::string_view Headers = "headers";
const absl::string_view Trailers = "trailers";
};

using Http1ResponseCodeDetails = ConstSingleton<Http1ResponseCodeDetailValues>;
using Http1HeaderTypes = ConstSingleton<Http1HeaderTypesValues>;

const StringUtil::CaseUnorderedSet& caseUnorderdSetContainingUpgradeAndHttp2Settings() {
CONSTRUCT_ON_FIRST_USE(StringUtil::CaseUnorderedSet,
Expand All @@ -51,7 +57,8 @@ HeaderKeyFormatterPtr formatter(const Http::Http1Settings& settings) {
} // namespace

const std::string StreamEncoderImpl::CRLF = "\r\n";
const std::string StreamEncoderImpl::LAST_CHUNK = "0\r\n\r\n";
// Last chunk as defined here https://tools.ietf.org/html/rfc7230#section-4.1
const std::string StreamEncoderImpl::LAST_CHUNK = "0\r\n";

StreamEncoderImpl::StreamEncoderImpl(ConnectionImpl& connection,
HeaderKeyFormatter* header_key_formatter)
Expand Down Expand Up @@ -200,7 +207,31 @@ void StreamEncoderImpl::encodeData(Buffer::Instance& data, bool end_stream) {
}
}

void StreamEncoderImpl::encodeTrailers(const HeaderMap&) { endEncode(); }
void StreamEncoderImpl::encodeTrailers(const HeaderMap& trailers) {
if (!connection_.enableTrailers()) {
return endEncode();
}
// Trailers only matter if it is a chunk transfer encoding
// https://tools.ietf.org/html/rfc7230#section-4.4
if (chunk_encoding_) {
mattklein123 marked this conversation as resolved.
Show resolved Hide resolved
mattklein123 marked this conversation as resolved.
Show resolved Hide resolved
// Finalize the body
connection_.buffer().add(LAST_CHUNK);

trailers.iterate(
mattklein123 marked this conversation as resolved.
Show resolved Hide resolved
[](const HeaderEntry& header, void* context) -> HeaderMap::Iterate {
static_cast<StreamEncoderImpl*>(context)->encodeFormattedHeader(
header.key().getStringView(), header.value().getStringView());
return HeaderMap::Iterate::Continue;
},
this);

connection_.flushOutput();
Chuongv marked this conversation as resolved.
Show resolved Hide resolved
connection_.buffer().add(CRLF);
}

connection_.flushOutput();
connection_.onEncodeComplete();
}

void StreamEncoderImpl::encodeMetadata(const MetadataMapVector&) {
connection_.stats().metadata_not_supported_error_.inc();
Expand All @@ -209,6 +240,7 @@ void StreamEncoderImpl::encodeMetadata(const MetadataMapVector&) {
void StreamEncoderImpl::endEncode() {
if (chunk_encoding_) {
connection_.buffer().add(LAST_CHUNK);
connection_.buffer().add(CRLF);
mattklein123 marked this conversation as resolved.
Show resolved Hide resolved
}

connection_.flushOutput();
Expand Down Expand Up @@ -366,13 +398,15 @@ const ToLowerTable& ConnectionImpl::toLowerTable() {
ConnectionImpl::ConnectionImpl(Network::Connection& connection, Stats::Scope& stats,
http_parser_type type, uint32_t max_headers_kb,
const uint32_t max_headers_count,
HeaderKeyFormatterPtr&& header_key_formatter)
HeaderKeyFormatterPtr&& header_key_formatter, bool enable_trailers)
: connection_(connection), stats_{ALL_HTTP1_CODEC_STATS(POOL_COUNTER_PREFIX(stats, "http1."))},
header_key_formatter_(std::move(header_key_formatter)), handling_upgrade_(false),
reset_stream_called_(false), strict_header_validation_(Runtime::runtimeFeatureEnabled(
"envoy.reloadable_features.strict_header_validation")),
header_key_formatter_(std::move(header_key_formatter)), processing_trailers_(false),
handling_upgrade_(false), reset_stream_called_(false),
strict_header_validation_(
Runtime::runtimeFeatureEnabled("envoy.reloadable_features.strict_header_validation")),
connection_header_sanitization_(Runtime::runtimeFeatureEnabled(
"envoy.reloadable_features.connection_header_sanitization")),
enable_trailers_(enable_trailers),
output_buffer_([&]() -> void { this->onBelowLowWatermark(); },
[&]() -> void { this->onAboveHighWatermark(); }),
max_headers_kb_(max_headers_kb), max_headers_count_(max_headers_count) {
Expand All @@ -384,16 +418,20 @@ ConnectionImpl::ConnectionImpl(Network::Connection& connection, Stats::Scope& st
void ConnectionImpl::completeLastHeader() {
ENVOY_CONN_LOG(trace, "completed header: key={} value={}", connection_,
current_header_field_.getStringView(), current_header_value_.getStringView());

if (!current_header_field_.empty()) {
toLowerTable().toLowerCase(current_header_field_.buffer(), current_header_field_.size());
current_header_map_->addViaMove(std::move(current_header_field_),
std::move(current_header_value_));
}

// Check if the number of headers exceeds the limit.
if (current_header_map_->size() > max_headers_count_) {
error_code_ = Http::Code::RequestHeaderFieldsTooLarge;
sendProtocolError(Http1ResponseCodeDetails::get().TooManyHeaders);
throw CodecProtocolException("headers size exceeds limit");
const absl::string_view header_type =
processing_trailers_ ? Http1HeaderTypes::get().Trailers : Http1HeaderTypes::get().Headers;
throw CodecProtocolException(absl::StrCat(header_type, " size exceeds limit"));
}

header_parsing_state_ = HeaderParsingState::Field;
Expand Down Expand Up @@ -462,11 +500,16 @@ size_t ConnectionImpl::dispatchSlice(const char* slice, size_t len) {
}

void ConnectionImpl::onHeaderField(const char* data, size_t length) {
// We previously already finished up the headers, these headers are
// now trailers.
if (header_parsing_state_ == HeaderParsingState::Done) {
// Ignore trailers.
return;
if (!enable_trailers_) {
// Ignore trailers.
return;
}
processing_trailers_ = true;
header_parsing_state_ = HeaderParsingState::Field;
Chuongv marked this conversation as resolved.
Show resolved Hide resolved
}

if (header_parsing_state_ == HeaderParsingState::Value) {
completeLastHeader();
}
Expand All @@ -475,11 +518,14 @@ void ConnectionImpl::onHeaderField(const char* data, size_t length) {
}

void ConnectionImpl::onHeaderValue(const char* data, size_t length) {
if (header_parsing_state_ == HeaderParsingState::Done) {
if (header_parsing_state_ == HeaderParsingState::Done && !enable_trailers_) {
mattklein123 marked this conversation as resolved.
Show resolved Hide resolved
// Ignore trailers.
return;
}

if (!current_header_map_) {
current_header_map_ = std::make_unique<HeaderMapImpl>();
}
// Work around a bug in http_parser where trailing whitespace is not trimmed
// as the spec requires: https://tools.ietf.org/html/rfc7230#section-3.2.4
const absl::string_view header_value = StringUtil::trim(absl::string_view(data, length));
Expand All @@ -505,16 +551,20 @@ void ConnectionImpl::onHeaderValue(const char* data, size_t length) {
const uint32_t total =
current_header_field_.size() + current_header_value_.size() + current_header_map_->byteSize();
if (total > (max_headers_kb_ * 1024)) {

const absl::string_view header_type =
processing_trailers_ ? Http1HeaderTypes::get().Trailers : Http1HeaderTypes::get().Headers;
error_code_ = Http::Code::RequestHeaderFieldsTooLarge;
sendProtocolError(Http1ResponseCodeDetails::get().HeadersTooLarge);
throw CodecProtocolException("headers size exceeds limit");
throw CodecProtocolException(absl::StrCat(header_type, " size exceeds limit"));
}
}

int ConnectionImpl::onHeadersCompleteBase() {
ENVOY_CONN_LOG(trace, "headers complete", connection_);
const absl::string_view header_type =
processing_trailers_ ? Http1HeaderTypes::get().Trailers : Http1HeaderTypes::get().Headers;
ENVOY_CONN_LOG(trace, "onHeadersCompleteBase complete for {}", connection_, header_type);
completeLastHeader();

if (!(parser_.http_major == 1 && parser_.http_minor == 1)) {
// This is not necessarily true, but it's good enough since higher layers only care if this is
// HTTP/1.1 or not.
Expand Down Expand Up @@ -571,6 +621,7 @@ int ConnectionImpl::onHeadersCompleteBase() {

int rc = onHeadersComplete(std::move(current_header_map_));
current_header_map_.reset();

header_parsing_state_ = HeaderParsingState::Done;

// Returning 2 informs http_parser to not expect a body or further data on this connection.
Expand All @@ -579,6 +630,7 @@ int ConnectionImpl::onHeadersCompleteBase() {

void ConnectionImpl::onMessageCompleteBase() {
ENVOY_CONN_LOG(trace, "message complete", connection_);

if (handling_upgrade_) {
// If this is an upgrade request, swallow the onMessageComplete. The
// upgrade payload will be treated as stream body.
Expand All @@ -587,7 +639,14 @@ void ConnectionImpl::onMessageCompleteBase() {
http_parser_pause(&parser_, 1);
return;
}
onMessageComplete();

// If true, this indicates we were processing trailers and must
// move the last header into current_header_map_
if (header_parsing_state_ == HeaderParsingState::Value) {
Chuongv marked this conversation as resolved.
Show resolved Hide resolved
completeLastHeader();
}

onMessageComplete(std::move(current_header_map_));
}

void ConnectionImpl::onMessageBeginBase() {
Expand All @@ -610,10 +669,11 @@ void ConnectionImpl::onResetStreamBase(StreamResetReason reason) {

ServerConnectionImpl::ServerConnectionImpl(Network::Connection& connection, Stats::Scope& stats,
ServerConnectionCallbacks& callbacks,
Http1Settings settings, uint32_t max_request_headers_kb,
const Http1Settings& settings,
uint32_t max_request_headers_kb,
const uint32_t max_request_headers_count)
: ConnectionImpl(connection, stats, HTTP_REQUEST, max_request_headers_kb,
max_request_headers_count, formatter(settings)),
max_request_headers_count, formatter(settings), settings.enable_trailers_),
callbacks_(callbacks), codec_settings_(settings) {}

void ServerConnectionImpl::onEncodeComplete() {
Expand Down Expand Up @@ -670,6 +730,8 @@ void ServerConnectionImpl::handlePath(HeaderMapImpl& headers, unsigned int metho
}

int ServerConnectionImpl::onHeadersComplete(HeaderMapImplPtr&& headers) {
ENVOY_CONN_LOG(trace, "Server: onHeadersComplete size={}", connection_, headers->size());

// Handle the case where response happens prior to request complete. It's up to upper layer code
// to disconnect the connection but we shouldn't fire any more events since it doesn't make
// sense.
Expand Down Expand Up @@ -734,16 +796,17 @@ void ServerConnectionImpl::onBody(const char* data, size_t length) {
}
}

void ServerConnectionImpl::onMessageComplete() {
void ServerConnectionImpl::onMessageComplete(HeaderMapImplPtr&& trailers) {
if (active_request_) {
Buffer::OwnedImpl buffer;
active_request_->remote_complete_ = true;

if (deferred_end_stream_headers_) {
active_request_->request_decoder_->decodeHeaders(std::move(deferred_end_stream_headers_),
true);
deferred_end_stream_headers_.reset();
} else if (processing_trailers_) {
active_request_->request_decoder_->decodeTrailers(std::move(trailers));
} else {
Buffer::OwnedImpl buffer;
active_request_->request_decoder_->decodeData(buffer, true);
}
}
Expand Down Expand Up @@ -792,7 +855,7 @@ ClientConnectionImpl::ClientConnectionImpl(Network::Connection& connection, Stat
ConnectionCallbacks&, const Http1Settings& settings,
const uint32_t max_response_headers_count)
: ConnectionImpl(connection, stats, HTTP_RESPONSE, MAX_RESPONSE_HEADERS_KB,
max_response_headers_count, formatter(settings)) {}
max_response_headers_count, formatter(settings), settings.enable_trailers_) {}

bool ClientConnectionImpl::cannotHaveBody() {
if ((!pending_responses_.empty() && pending_responses_.front().head_request_) ||
Expand Down Expand Up @@ -825,6 +888,7 @@ void ClientConnectionImpl::onEncodeHeaders(const HeaderMap& headers) {
}

int ClientConnectionImpl::onHeadersComplete(HeaderMapImplPtr&& headers) {
ENVOY_CONN_LOG(trace, "Client: onHeadersComplete size={}", connection_, headers->size());
headers->setStatus(parser_.status_code);

// Handle the case where the client is closing a kept alive connection (by sending a 408
Expand Down Expand Up @@ -859,7 +923,7 @@ void ClientConnectionImpl::onBody(const char* data, size_t length) {
}
}

void ClientConnectionImpl::onMessageComplete() {
void ClientConnectionImpl::onMessageComplete(HeaderMapImplPtr&& trailers) {
ENVOY_CONN_LOG(trace, "message complete", connection_);
if (ignore_message_complete_for_100_continue_) {
ignore_message_complete_for_100_continue_ = false;
Expand All @@ -885,6 +949,8 @@ void ClientConnectionImpl::onMessageComplete() {
if (deferred_end_stream_headers_) {
response.decoder_->decodeHeaders(std::move(deferred_end_stream_headers_), true);
deferred_end_stream_headers_.reset();
} else if (processing_trailers_) {
response.decoder_->decodeTrailers(std::move(trailers));
} else {
Buffer::OwnedImpl buffer;
response.decoder_->decodeData(buffer, true);
Expand Down
Loading