Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 15 additions & 3 deletions source/common/http/http1/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,7 @@ bool ClientConnectionImpl::cannotHaveBody() {
if ((pending_response_.has_value() && pending_response_.value().encoder_.headRequest()) ||
parser_.status_code == 204 || parser_.status_code == 304 ||
(parser_.status_code >= 200 && parser_.content_length == 0)) {
ASSERT(!pending_response_done_);
return true;
} else {
return false;
Expand All @@ -909,7 +910,9 @@ RequestEncoder& ClientConnectionImpl::newStream(ResponseDecoder& response_decode
ASSERT(connection_.readEnabled());

ASSERT(!pending_response_.has_value());
ASSERT(pending_response_done_);
pending_response_.emplace(*this, header_key_formatter_.get(), &response_decoder);
pending_response_done_ = false;
return pending_response_.value().encoder_;
}

Expand All @@ -920,6 +923,7 @@ int ClientConnectionImpl::onHeadersComplete() {
if (!pending_response_.has_value() && !resetStreamCalled()) {
throw PrematureResponseException(static_cast<Http::Code>(parser_.status_code));
} else if (pending_response_.has_value()) {
ASSERT(!pending_response_done_);
auto& headers = absl::get<ResponseHeaderMapPtr>(headers_or_trailers_);
ENVOY_CONN_LOG(trace, "Client: onHeadersComplete size={}", connection_, headers->size());
headers->setStatus(parser_.status_code);
Expand Down Expand Up @@ -948,6 +952,7 @@ int ClientConnectionImpl::onHeadersComplete() {
void ClientConnectionImpl::onBody(const char* data, size_t length) {
ASSERT(!deferred_end_stream_headers_);
if (pending_response_.has_value()) {
ASSERT(!pending_response_done_);
Buffer::OwnedImpl buffer;
buffer.add(data, length);
pending_response_.value().decoder_->decodeData(buffer, false);
Expand All @@ -961,9 +966,12 @@ void ClientConnectionImpl::onMessageComplete() {
return;
}
if (pending_response_.has_value()) {
ASSERT(!pending_response_done_);
// After calling decodeData() with end stream set to true, we should no longer be able to reset.
PendingResponse response = std::move(pending_response_.value());
pending_response_.reset();
PendingResponse& response = pending_response_.value();
// Encoder is used as part of decode* calls later in this function so pending_response_ can not
// be reset just yet. Preserve the state in pending_response_done_ instead.
pending_response_done_ = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't thrill me due to extra logic complexity, but I think this is an OK solution for now. Can you do me a favor and put an additional ASSERT(!pending_response_done_); every time we load successfully check that pending_response has a value in the other functions above? I think there shouldn't be too many.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


// Streams are responsible for unwinding any outstanding readDisable(true)
// calls done on the underlying connection as they are destroyed. As this is
Expand All @@ -989,20 +997,23 @@ void ClientConnectionImpl::onMessageComplete() {
}

// Reset to ensure no information from one requests persists to the next.
pending_response_.reset();
headers_or_trailers_.emplace<ResponseHeaderMapPtr>(nullptr);
}
}

void ClientConnectionImpl::onResetStream(StreamResetReason reason) {
// Only raise reset if we did not already dispatch a complete response.
if (pending_response_.has_value()) {
if (pending_response_.has_value() && !pending_response_done_) {
pending_response_.value().encoder_.runResetCallbacks(reason);
pending_response_done_ = true;
pending_response_.reset();
}
}

void ClientConnectionImpl::sendProtocolError(absl::string_view details) {
if (pending_response_.has_value()) {
ASSERT(!pending_response_done_);
pending_response_.value().encoder_.setDetails(details);
}
}
Expand All @@ -1017,6 +1028,7 @@ void ClientConnectionImpl::onBelowLowWatermark() {
// such as sending multiple responses to the same request, causing us to close the connection, but
// in doing so go below low watermark.
if (pending_response_.has_value()) {
ASSERT(!pending_response_done_);
pending_response_.value().encoder_.runLowWatermarkCallbacks();
}
}
Expand Down
1 change: 1 addition & 0 deletions source/common/http/http1/codec_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ class ClientConnectionImpl : public ClientConnection, public ConnectionImpl {
}

absl::optional<PendingResponse> pending_response_;
bool pending_response_done_{true};
// Set true between receiving 100-Continue headers and receiving the spurious onMessageComplete.
bool ignore_message_complete_for_100_continue_{};
// TODO(mattklein123): This should be a member of PendingResponse but this change needs dedicated
Expand Down