Let OgHttp2Session avoid visitor callbacks for data on unknown streams. This CL performs a stream map check in OgHttp2Session::OnDataFrameHeader() and OgHttp2Session::OnStreamFrameData(). If the stream is not in the map, then OgHttp2Session will avoid invoking visitor_.OnBeginDataForStream() and visitor_.OnDataForStream(), respectively. This behavior is in line with Http2Dispatcher behavior [1, 2] and brings oghttp2 more in line with nghttp2. Similarly, this CL also moves visitor_.OnEndStream() into an already-existing stream map check. This behavior also allows Http2FloodMitigationTest.UpstreamRstStreamStormOnDownstreamCloseRegressionTest to pass, as hypothesized in http://b/201799377#comment8: blaze test //third_party/envoy/src/test/integration:http2_flood_integration_test --test_filter=*/Http2FloodMitigationTest.UpstreamRstStreamStormOnDownstreamCloseRegressionTest/* --test_arg=--vmodule=oghttp2_session=2 --test_arg="--" --test_arg="-l trace" --test_env="ENVOY_NGHTTP2_TRACE=" Before: http://sponge2/3ea792e7-d946-413e-83f3-d20e1f92741f After: http://sponge2/f43bb7d8-4374-46a4-8804-3e2be1719442 [1] http://google3/gfe/gfe2/http2/http2_dispatcher.cc;l=974-987;rcl=406920946 [2] http://google3/gfe/gfe2/http2/http2_dispatcher.cc;l=1024-1032;rcl=406920946 PiperOrigin-RevId: 410129890
diff --git a/http2/adapter/oghttp2_adapter_test.cc b/http2/adapter/oghttp2_adapter_test.cc index f5a8cb9..efe0327 100644 --- a/http2/adapter/oghttp2_adapter_test.cc +++ b/http2/adapter/oghttp2_adapter_test.cc
@@ -1251,16 +1251,9 @@ .Serialize(); // The visitor gets notified about the HEADERS frame and DATA frame for the - // closed stream with further processing on the DATA frame. + // closed stream with no further processing on either frame. EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 0x4)); EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, DATA, 0x1)); - EXPECT_CALL(visitor, OnBeginDataForStream(stream_id, _)); - EXPECT_CALL(visitor, - OnDataForStream(stream_id, "This is the response body.")); - EXPECT_CALL(visitor, OnEndStream(stream_id)); - // This final OnCloseStream() is avoided because of a stream map check in - // OnEndStream(). - EXPECT_CALL(visitor, OnCloseStream(stream_id, _)).Times(0); const int64_t response_result = adapter->ProcessBytes(response_frames); EXPECT_EQ(response_frames.size(), static_cast<size_t>(response_result));
diff --git a/http2/adapter/oghttp2_session.cc b/http2/adapter/oghttp2_session.cc index a48dc47..c46f338 100644 --- a/http2/adapter/oghttp2_session.cc +++ b/http2/adapter/oghttp2_session.cc
@@ -746,12 +746,17 @@ void OgHttp2Session::OnDataFrameHeader(spdy::SpdyStreamId stream_id, size_t length, bool /*fin*/) { - if (static_cast<Http2StreamId>(stream_id) > highest_processed_stream_id_) { - // Receiving DATA before HEADERS is a connection error. - LatchErrorAndNotify(Http2ErrorCode::PROTOCOL_ERROR, - ConnectionError::kWrongFrameSequence); + if (!stream_map_.contains(stream_id)) { + // The stream does not exist; it could be an error or a benign close, e.g., + // getting data for a stream this connection recently closed. + if (static_cast<Http2StreamId>(stream_id) > highest_processed_stream_id_) { + // Receiving DATA before HEADERS is a connection error. + LatchErrorAndNotify(Http2ErrorCode::PROTOCOL_ERROR, + ConnectionError::kWrongFrameSequence); + } return; } + const bool result = visitor_.OnBeginDataForStream(stream_id, length); if (!result) { decoder_.StopProcessing(); @@ -764,8 +769,8 @@ // Count the data against flow control, even if the stream is unknown. MarkDataBuffered(stream_id, len); - if (static_cast<Http2StreamId>(stream_id) > highest_processed_stream_id_) { - // Receiving DATA before HEADERS is a connection error; the visitor was + if (!stream_map_.contains(stream_id)) { + // If the stream was unknown due to a protocol error, the visitor was // informed in OnDataFrameHeader(). return; } @@ -781,8 +786,8 @@ auto iter = stream_map_.find(stream_id); if (iter != stream_map_.end()) { iter->second.half_closed_remote = true; + visitor_.OnEndStream(stream_id); } - visitor_.OnEndStream(stream_id); if (iter != stream_map_.end() && iter->second.half_closed_local && options_.perspective == Perspective::kClient) { // From the client's perspective, the stream can be closed if it's already