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/2: Fix crash when remote sends invalid frame with in flight stream #124

Merged
merged 1 commit into from
Oct 6, 2016
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
22 changes: 16 additions & 6 deletions source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,16 @@ Network::FilterStatus ConnectionManagerImpl::onData(Buffer::Instance& data) {
try {
codec_->dispatch(data);
} catch (const CodecProtocolException& e) {
// HTTP/1.1 codec has already sent a 400 response if possible.
// HTTP/1.1 codec has already sent a 400 response if possible. HTTP/2 codec has already sent
// GOAWAY.
conn_log_debug("dispatch error: {}", read_callbacks_->connection(), e.what());
config_.stats().named_.downstream_cx_protocol_error_.inc();

// In the protocol error case, we need to reset all streams now. Since we do a flush write,
// the connection might stick around long enough for a pending stream to come back and try
// to encode.
resetAllStreams();

read_callbacks_->connection().close(Network::ConnectionCloseType::FlushWrite);
return Network::FilterStatus::StopIteration;
}
Expand Down Expand Up @@ -192,6 +199,13 @@ void ConnectionManagerImpl::onBufferChange(Network::ConnectionBufferType type, u
config_.stats().named_.downstream_cx_tx_bytes_buffered_);
}

void ConnectionManagerImpl::resetAllStreams() {
while (!streams_.empty()) {
// Mimic a downstream reset in this case.
streams_.front()->onResetStream(StreamResetReason::ConnectionTermination);
}
}

void ConnectionManagerImpl::onEvent(uint32_t events) {
if (events & Network::ConnectionEvent::LocalClose) {
config_.stats().named_.downstream_cx_destroy_local_.inc();
Expand Down Expand Up @@ -224,11 +238,7 @@ void ConnectionManagerImpl::onEvent(uint32_t events) {

config_.stats().named_.downstream_cx_destroy_active_rq_.inc();
user_agent_.onConnectionDestroy(events, true);

while (!streams_.empty()) {
// Mimic a downstream reset in this case.
streams_.front()->onResetStream(StreamResetReason::ConnectionTermination);
}
resetAllStreams();
}
}

Expand Down
1 change: 1 addition & 0 deletions source/common/http/conn_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
*/
void destroyStream(ActiveStream& stream);

void resetAllStreams();
void onIdleTimeout();
void onDrainTimeout();
void startDrainSequence();
Expand Down
4 changes: 2 additions & 2 deletions test/common/http/conn_manager_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,11 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamProtocolError) {
callbacks.addStreamDecoderFilter(Http::StreamDecoderFilterPtr{filter});
}));

// A protocol exception should result in a local close followed by reset of the streams.
// A protocol exception should result in reset of the streams followed by a local close.
Sequence s;
EXPECT_CALL(filter->reset_stream_called_, ready()).InSequence(s);
EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite))
.InSequence(s);
EXPECT_CALL(filter->reset_stream_called_, ready()).InSequence(s);

NiceMock<Http::MockStreamEncoder> encoder;
EXPECT_CALL(*codec_, dispatch(_))
Expand Down