Skip to content

Commit

Permalink
http/2: Fix crash when remote sends invalid frame with in flight stre…
Browse files Browse the repository at this point in the history
…am (#124)
  • Loading branch information
mattklein123 authored Oct 6, 2016
1 parent 6aaf128 commit f59e261
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 8 deletions.
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

0 comments on commit f59e261

Please sign in to comment.