| #include "quiche/http2/adapter/callback_visitor.h" | 
 |  | 
 | #include <string> | 
 |  | 
 | #include "absl/container/flat_hash_map.h" | 
 | #include "quiche/http2/adapter/http2_protocol.h" | 
 | #include "quiche/http2/adapter/mock_nghttp2_callbacks.h" | 
 | #include "quiche/http2/adapter/nghttp2_adapter.h" | 
 | #include "quiche/http2/adapter/nghttp2_test_utils.h" | 
 | #include "quiche/http2/adapter/test_frame_sequence.h" | 
 | #include "quiche/http2/adapter/test_utils.h" | 
 | #include "quiche/common/platform/api/quiche_test.h" | 
 |  | 
 | namespace http2 { | 
 | namespace adapter { | 
 | namespace test { | 
 | namespace { | 
 |  | 
 | using testing::_; | 
 | using testing::IsEmpty; | 
 | using testing::Pair; | 
 | using testing::UnorderedElementsAre; | 
 |  | 
 | enum FrameType { | 
 |   DATA, | 
 |   HEADERS, | 
 |   PRIORITY, | 
 |   RST_STREAM, | 
 |   SETTINGS, | 
 |   PUSH_PROMISE, | 
 |   PING, | 
 |   GOAWAY, | 
 |   WINDOW_UPDATE, | 
 |   CONTINUATION, | 
 | }; | 
 |  | 
 | // Tests connection-level events. | 
 | TEST(ClientCallbackVisitorUnitTest, ConnectionFrames) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kClient, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |  | 
 |   testing::InSequence seq; | 
 |  | 
 |   // SETTINGS | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, SETTINGS, _))); | 
 |   visitor.OnFrameHeader(0, 0, SETTINGS, 0); | 
 |  | 
 |   visitor.OnSettingsStart(); | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsSettings(testing::IsEmpty()))); | 
 |   visitor.OnSettingsEnd(); | 
 |  | 
 |   // PING | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, PING, _))); | 
 |   visitor.OnFrameHeader(0, 8, PING, 0); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsPing(42))); | 
 |   visitor.OnPing(42, false); | 
 |  | 
 |   // WINDOW_UPDATE | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, WINDOW_UPDATE, _))); | 
 |   visitor.OnFrameHeader(0, 4, WINDOW_UPDATE, 0); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsWindowUpdate(1000))); | 
 |   visitor.OnWindowUpdate(0, 1000); | 
 |  | 
 |   // PING ack | 
 |   EXPECT_CALL(callbacks, | 
 |               OnBeginFrame(HasFrameHeader(0, PING, NGHTTP2_FLAG_ACK))); | 
 |   visitor.OnFrameHeader(0, 8, PING, 1); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsPingAck(247))); | 
 |   visitor.OnPing(247, true); | 
 |  | 
 |   // GOAWAY | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, GOAWAY, 0))); | 
 |   visitor.OnFrameHeader(0, 19, GOAWAY, 0); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsGoAway(5, NGHTTP2_ENHANCE_YOUR_CALM, | 
 |                                               "calm down!!"))); | 
 |   visitor.OnGoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, "calm down!!"); | 
 |  | 
 |   EXPECT_EQ(visitor.stream_map_size(), 0); | 
 | } | 
 |  | 
 | TEST(ClientCallbackVisitorUnitTest, StreamFrames) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kClient, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |   absl::flat_hash_map<Http2StreamId, int> stream_close_counts; | 
 |   visitor.set_stream_close_listener( | 
 |       [&stream_close_counts](Http2StreamId stream_id) { | 
 |         ++stream_close_counts[stream_id]; | 
 |       }); | 
 |  | 
 |   testing::InSequence seq; | 
 |  | 
 |   EXPECT_EQ(visitor.stream_map_size(), 0); | 
 |  | 
 |   // HEADERS on stream 1 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, HEADERS, _))); | 
 |   visitor.OnFrameHeader(1, 23, HEADERS, 4); | 
 |  | 
 |   EXPECT_CALL(callbacks, | 
 |               OnBeginHeaders(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE))); | 
 |   visitor.OnBeginHeadersForStream(1); | 
 |  | 
 |   EXPECT_EQ(visitor.stream_map_size(), 1); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, ":status", "200", _)); | 
 |   visitor.OnHeaderForStream(1, ":status", "200"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, "server", "my-fake-server", _)); | 
 |   visitor.OnHeaderForStream(1, "server", "my-fake-server"); | 
 |  | 
 |   EXPECT_CALL(callbacks, | 
 |               OnHeader(_, "date", "Tue, 6 Apr 2021 12:54:01 GMT", _)); | 
 |   visitor.OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, "trailer", "x-server-status", _)); | 
 |   visitor.OnHeaderForStream(1, "trailer", "x-server-status"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE))); | 
 |   visitor.OnEndHeadersForStream(1); | 
 |  | 
 |   // DATA for stream 1 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, DATA, 0))); | 
 |   visitor.OnFrameHeader(1, 26, DATA, 0); | 
 |  | 
 |   visitor.OnBeginDataForStream(1, 26); | 
 |   EXPECT_CALL(callbacks, OnDataChunkRecv(0, 1, "This is the response body.")); | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsData(1, _, 0))); | 
 |   visitor.OnDataForStream(1, "This is the response body."); | 
 |  | 
 |   // Trailers for stream 1, with a different nghttp2 "category". | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, HEADERS, _))); | 
 |   visitor.OnFrameHeader(1, 23, HEADERS, 4); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnBeginHeaders(IsHeaders(1, _, NGHTTP2_HCAT_HEADERS))); | 
 |   visitor.OnBeginHeadersForStream(1); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, "x-server-status", "OK", _)); | 
 |   visitor.OnHeaderForStream(1, "x-server-status", "OK"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsHeaders(1, _, NGHTTP2_HCAT_HEADERS))); | 
 |   visitor.OnEndHeadersForStream(1); | 
 |  | 
 |   EXPECT_THAT(stream_close_counts, IsEmpty()); | 
 |  | 
 |   // RST_STREAM on stream 3 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(3, RST_STREAM, 0))); | 
 |   visitor.OnFrameHeader(3, 4, RST_STREAM, 0); | 
 |  | 
 |   // No change in stream map size. | 
 |   EXPECT_EQ(visitor.stream_map_size(), 1); | 
 |   EXPECT_THAT(stream_close_counts, IsEmpty()); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsRstStream(3, NGHTTP2_INTERNAL_ERROR))); | 
 |   visitor.OnRstStream(3, Http2ErrorCode::INTERNAL_ERROR); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnStreamClose(3, NGHTTP2_INTERNAL_ERROR)); | 
 |   visitor.OnCloseStream(3, Http2ErrorCode::INTERNAL_ERROR); | 
 |  | 
 |   EXPECT_THAT(stream_close_counts, UnorderedElementsAre(Pair(3, 1))); | 
 |  | 
 |   // More stream close events | 
 |   EXPECT_CALL(callbacks, | 
 |               OnBeginFrame(HasFrameHeader(1, DATA, NGHTTP2_FLAG_END_STREAM))); | 
 |   visitor.OnFrameHeader(1, 0, DATA, 1); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsData(1, _, NGHTTP2_FLAG_END_STREAM))); | 
 |   visitor.OnBeginDataForStream(1, 0); | 
 |   EXPECT_TRUE(visitor.OnEndStream(1)); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnStreamClose(1, NGHTTP2_NO_ERROR)); | 
 |   visitor.OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR); | 
 |  | 
 |   // Stream map is empty again after both streams were closed. | 
 |   EXPECT_EQ(visitor.stream_map_size(), 0); | 
 |   EXPECT_THAT(stream_close_counts, | 
 |               UnorderedElementsAre(Pair(3, 1), Pair(1, 1))); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(5, RST_STREAM, _))); | 
 |   visitor.OnFrameHeader(5, 4, RST_STREAM, 0); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsRstStream(5, NGHTTP2_REFUSED_STREAM))); | 
 |   visitor.OnRstStream(5, Http2ErrorCode::REFUSED_STREAM); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnStreamClose(5, NGHTTP2_REFUSED_STREAM)); | 
 |   visitor.OnCloseStream(5, Http2ErrorCode::REFUSED_STREAM); | 
 |  | 
 |   EXPECT_EQ(visitor.stream_map_size(), 0); | 
 |   EXPECT_THAT(stream_close_counts, | 
 |               UnorderedElementsAre(Pair(3, 1), Pair(1, 1), Pair(5, 1))); | 
 | } | 
 |  | 
 | TEST(ClientCallbackVisitorUnitTest, HeadersWithContinuation) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kClient, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |  | 
 |   testing::InSequence seq; | 
 |  | 
 |   // HEADERS on stream 1 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, HEADERS, 0x0))); | 
 |   ASSERT_TRUE(visitor.OnFrameHeader(1, 23, HEADERS, 0x0)); | 
 |  | 
 |   EXPECT_CALL(callbacks, | 
 |               OnBeginHeaders(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE))); | 
 |   visitor.OnBeginHeadersForStream(1); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, ":status", "200", _)); | 
 |   visitor.OnHeaderForStream(1, ":status", "200"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, "server", "my-fake-server", _)); | 
 |   visitor.OnHeaderForStream(1, "server", "my-fake-server"); | 
 |  | 
 |   EXPECT_CALL(callbacks, | 
 |               OnBeginFrame(HasFrameHeader(1, CONTINUATION, END_HEADERS_FLAG))); | 
 |   ASSERT_TRUE(visitor.OnFrameHeader(1, 23, CONTINUATION, END_HEADERS_FLAG)); | 
 |  | 
 |   EXPECT_CALL(callbacks, | 
 |               OnHeader(_, "date", "Tue, 6 Apr 2021 12:54:01 GMT", _)); | 
 |   visitor.OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, "trailer", "x-server-status", _)); | 
 |   visitor.OnHeaderForStream(1, "trailer", "x-server-status"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE))); | 
 |   visitor.OnEndHeadersForStream(1); | 
 | } | 
 |  | 
 | TEST(ClientCallbackVisitorUnitTest, ContinuationNoHeaders) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kClient, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |   // Because no stream precedes the CONTINUATION frame, the stream ID does not | 
 |   // match, and the method returns false. | 
 |   EXPECT_FALSE(visitor.OnFrameHeader(1, 23, CONTINUATION, END_HEADERS_FLAG)); | 
 | } | 
 |  | 
 | TEST(ClientCallbackVisitorUnitTest, ContinuationWrongPrecedingType) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kClient, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, WINDOW_UPDATE, _))); | 
 |   visitor.OnFrameHeader(1, 4, WINDOW_UPDATE, 0); | 
 |  | 
 |   // Because the CONTINUATION frame does not follow HEADERS, the method returns | 
 |   // false. | 
 |   EXPECT_FALSE(visitor.OnFrameHeader(1, 23, CONTINUATION, END_HEADERS_FLAG)); | 
 | } | 
 |  | 
 | TEST(ClientCallbackVisitorUnitTest, ContinuationWrongStream) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kClient, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |   // HEADERS on stream 1 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, HEADERS, 0x0))); | 
 |   ASSERT_TRUE(visitor.OnFrameHeader(1, 23, HEADERS, 0x0)); | 
 |  | 
 |   EXPECT_CALL(callbacks, | 
 |               OnBeginHeaders(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE))); | 
 |   visitor.OnBeginHeadersForStream(1); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, ":status", "200", _)); | 
 |   visitor.OnHeaderForStream(1, ":status", "200"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, "server", "my-fake-server", _)); | 
 |   visitor.OnHeaderForStream(1, "server", "my-fake-server"); | 
 |  | 
 |   // The CONTINUATION stream ID does not match the one from the HEADERS. | 
 |   EXPECT_FALSE(visitor.OnFrameHeader(3, 23, CONTINUATION, END_HEADERS_FLAG)); | 
 | } | 
 |  | 
 | TEST(ClientCallbackVisitorUnitTest, ResetAndGoaway) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kClient, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |  | 
 |   testing::InSequence seq; | 
 |  | 
 |   // RST_STREAM on stream 1 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, RST_STREAM, 0x0))); | 
 |   EXPECT_TRUE(visitor.OnFrameHeader(1, 13, RST_STREAM, 0x0)); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsRstStream(1, NGHTTP2_INTERNAL_ERROR))); | 
 |   visitor.OnRstStream(1, Http2ErrorCode::INTERNAL_ERROR); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnStreamClose(1, NGHTTP2_INTERNAL_ERROR)); | 
 |   EXPECT_TRUE(visitor.OnCloseStream(1, Http2ErrorCode::INTERNAL_ERROR)); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, GOAWAY, 0x0))); | 
 |   EXPECT_TRUE(visitor.OnFrameHeader(0, 13, GOAWAY, 0x0)); | 
 |  | 
 |   EXPECT_CALL(callbacks, | 
 |               OnFrameRecv(IsGoAway(3, NGHTTP2_ENHANCE_YOUR_CALM, "calma te"))); | 
 |   EXPECT_TRUE( | 
 |       visitor.OnGoAway(3, Http2ErrorCode::ENHANCE_YOUR_CALM, "calma te")); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnStreamClose(5, NGHTTP2_STREAM_CLOSED)) | 
 |       .WillOnce(testing::Return(NGHTTP2_ERR_CALLBACK_FAILURE)); | 
 |   EXPECT_FALSE(visitor.OnCloseStream(5, Http2ErrorCode::STREAM_CLOSED)); | 
 | } | 
 |  | 
 | TEST(ServerCallbackVisitorUnitTest, ConnectionFrames) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kServer, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |  | 
 |   testing::InSequence seq; | 
 |  | 
 |   // SETTINGS | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, SETTINGS, _))); | 
 |   visitor.OnFrameHeader(0, 0, SETTINGS, 0); | 
 |  | 
 |   visitor.OnSettingsStart(); | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsSettings(testing::IsEmpty()))); | 
 |   visitor.OnSettingsEnd(); | 
 |  | 
 |   // PING | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, PING, _))); | 
 |   visitor.OnFrameHeader(0, 8, PING, 0); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsPing(42))); | 
 |   visitor.OnPing(42, false); | 
 |  | 
 |   // WINDOW_UPDATE | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, WINDOW_UPDATE, _))); | 
 |   visitor.OnFrameHeader(0, 4, WINDOW_UPDATE, 0); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsWindowUpdate(1000))); | 
 |   visitor.OnWindowUpdate(0, 1000); | 
 |  | 
 |   // PING ack | 
 |   EXPECT_CALL(callbacks, | 
 |               OnBeginFrame(HasFrameHeader(0, PING, NGHTTP2_FLAG_ACK))); | 
 |   visitor.OnFrameHeader(0, 8, PING, 1); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsPingAck(247))); | 
 |   visitor.OnPing(247, true); | 
 |  | 
 |   EXPECT_EQ(visitor.stream_map_size(), 0); | 
 | } | 
 |  | 
 | TEST(ServerCallbackVisitorUnitTest, StreamFrames) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kServer, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |  | 
 |   testing::InSequence seq; | 
 |  | 
 |   // HEADERS on stream 1 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader( | 
 |                              1, HEADERS, NGHTTP2_FLAG_END_HEADERS))); | 
 |   visitor.OnFrameHeader(1, 23, HEADERS, 4); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnBeginHeaders(IsHeaders(1, NGHTTP2_FLAG_END_HEADERS, | 
 |                                                   NGHTTP2_HCAT_REQUEST))); | 
 |   visitor.OnBeginHeadersForStream(1); | 
 |  | 
 |   EXPECT_EQ(visitor.stream_map_size(), 1); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, ":method", "POST", _)); | 
 |   visitor.OnHeaderForStream(1, ":method", "POST"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, ":path", "/example/path", _)); | 
 |   visitor.OnHeaderForStream(1, ":path", "/example/path"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, ":scheme", "https", _)); | 
 |   visitor.OnHeaderForStream(1, ":scheme", "https"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, ":authority", "example.com", _)); | 
 |   visitor.OnHeaderForStream(1, ":authority", "example.com"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, "accept", "text/html", _)); | 
 |   visitor.OnHeaderForStream(1, "accept", "text/html"); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsHeaders(1, NGHTTP2_FLAG_END_HEADERS, | 
 |                                                NGHTTP2_HCAT_REQUEST))); | 
 |   visitor.OnEndHeadersForStream(1); | 
 |  | 
 |   // DATA on stream 1 | 
 |   EXPECT_CALL(callbacks, | 
 |               OnBeginFrame(HasFrameHeader(1, DATA, NGHTTP2_FLAG_END_STREAM))); | 
 |   visitor.OnFrameHeader(1, 25, DATA, NGHTTP2_FLAG_END_STREAM); | 
 |  | 
 |   visitor.OnBeginDataForStream(1, 25); | 
 |   EXPECT_CALL(callbacks, OnDataChunkRecv(NGHTTP2_FLAG_END_STREAM, 1, | 
 |                                          "This is the request body.")); | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsData(1, _, NGHTTP2_FLAG_END_STREAM))); | 
 |   visitor.OnDataForStream(1, "This is the request body."); | 
 |   EXPECT_TRUE(visitor.OnEndStream(1)); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnStreamClose(1, NGHTTP2_NO_ERROR)); | 
 |   visitor.OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR); | 
 |  | 
 |   EXPECT_EQ(visitor.stream_map_size(), 0); | 
 |  | 
 |   // RST_STREAM on stream 3 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(3, RST_STREAM, 0))); | 
 |   visitor.OnFrameHeader(3, 4, RST_STREAM, 0); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsRstStream(3, NGHTTP2_INTERNAL_ERROR))); | 
 |   visitor.OnRstStream(3, Http2ErrorCode::INTERNAL_ERROR); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnStreamClose(3, NGHTTP2_INTERNAL_ERROR)); | 
 |   visitor.OnCloseStream(3, Http2ErrorCode::INTERNAL_ERROR); | 
 |  | 
 |   EXPECT_EQ(visitor.stream_map_size(), 0); | 
 | } | 
 |  | 
 | TEST(ServerCallbackVisitorUnitTest, DataWithPadding) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kServer, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |  | 
 |   const size_t kPaddingLength = 39; | 
 |   const uint8_t kFlags = NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_END_STREAM; | 
 |  | 
 |   testing::InSequence seq; | 
 |  | 
 |   // DATA on stream 1 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, DATA, kFlags))); | 
 |   EXPECT_TRUE(visitor.OnFrameHeader(1, 25 + kPaddingLength, DATA, kFlags)); | 
 |  | 
 |   EXPECT_TRUE(visitor.OnBeginDataForStream(1, 25 + kPaddingLength)); | 
 |  | 
 |   // Padding before data. | 
 |   EXPECT_TRUE(visitor.OnDataPaddingLength(1, kPaddingLength)); | 
 |  | 
 |   EXPECT_CALL(callbacks, | 
 |               OnDataChunkRecv(kFlags, 1, "This is the request body.")); | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsData(1, _, kFlags, kPaddingLength))); | 
 |   EXPECT_TRUE(visitor.OnDataForStream(1, "This is the request body.")); | 
 |   EXPECT_TRUE(visitor.OnEndStream(1)); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnStreamClose(1, NGHTTP2_NO_ERROR)); | 
 |   visitor.OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR); | 
 |  | 
 |   // DATA on stream 3 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(3, DATA, kFlags))); | 
 |   EXPECT_TRUE(visitor.OnFrameHeader(3, 25 + kPaddingLength, DATA, kFlags)); | 
 |  | 
 |   EXPECT_TRUE(visitor.OnBeginDataForStream(3, 25 + kPaddingLength)); | 
 |  | 
 |   // Data before padding. | 
 |   EXPECT_CALL(callbacks, | 
 |               OnDataChunkRecv(kFlags, 3, "This is the request body.")); | 
 |   EXPECT_TRUE(visitor.OnDataForStream(3, "This is the request body.")); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsData(3, _, kFlags, kPaddingLength))); | 
 |   EXPECT_TRUE(visitor.OnDataPaddingLength(3, kPaddingLength)); | 
 |   EXPECT_TRUE(visitor.OnEndStream(3)); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnStreamClose(3, NGHTTP2_NO_ERROR)); | 
 |   visitor.OnCloseStream(3, Http2ErrorCode::HTTP2_NO_ERROR); | 
 |  | 
 |   // DATA on stream 5 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(5, DATA, kFlags))); | 
 |   EXPECT_TRUE(visitor.OnFrameHeader(5, 25 + kPaddingLength, DATA, kFlags)); | 
 |  | 
 |   EXPECT_TRUE(visitor.OnBeginDataForStream(5, 25 + kPaddingLength)); | 
 |  | 
 |   // Error during padding. | 
 |   EXPECT_CALL(callbacks, | 
 |               OnDataChunkRecv(kFlags, 5, "This is the request body.")); | 
 |   EXPECT_TRUE(visitor.OnDataForStream(5, "This is the request body.")); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsData(5, _, kFlags, kPaddingLength))) | 
 |       .WillOnce(testing::Return(NGHTTP2_ERR_CALLBACK_FAILURE)); | 
 |   EXPECT_TRUE(visitor.OnDataPaddingLength(5, kPaddingLength)); | 
 |   EXPECT_FALSE(visitor.OnEndStream(3)); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnStreamClose(5, NGHTTP2_NO_ERROR)); | 
 |   visitor.OnCloseStream(5, Http2ErrorCode::HTTP2_NO_ERROR); | 
 | } | 
 |  | 
 | // In the case of a Content-Length mismatch where the header value is larger | 
 | // than the actual data for the stream, nghttp2 will call | 
 | // `on_begin_frame_callback` and `on_data_chunk_recv_callback`, but not the | 
 | // `on_frame_recv_callback`. | 
 | TEST(ServerCallbackVisitorUnitTest, MismatchedContentLengthCallbacks) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kServer, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |   auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
 |  | 
 |   const std::string frames = TestFrameSequence() | 
 |                                  .ClientPreface() | 
 |                                  .Headers(1, | 
 |                                           {{":method", "POST"}, | 
 |                                            {":scheme", "https"}, | 
 |                                            {":authority", "example.com"}, | 
 |                                            {":path", "/"}, | 
 |                                            {"content-length", "50"}}, | 
 |                                           /*fin=*/false) | 
 |                                  .Data(1, "Less than 50 bytes.", true) | 
 |                                  .Serialize(); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, SETTINGS, _))); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsSettings(testing::IsEmpty()))); | 
 |  | 
 |   // HEADERS on stream 1 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader( | 
 |                              1, HEADERS, NGHTTP2_FLAG_END_HEADERS))); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnBeginHeaders(IsHeaders(1, NGHTTP2_FLAG_END_HEADERS, | 
 |                                                   NGHTTP2_HCAT_REQUEST))); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader(_, ":method", "POST", _)); | 
 |   EXPECT_CALL(callbacks, OnHeader(_, ":path", "/", _)); | 
 |   EXPECT_CALL(callbacks, OnHeader(_, ":scheme", "https", _)); | 
 |   EXPECT_CALL(callbacks, OnHeader(_, ":authority", "example.com", _)); | 
 |   EXPECT_CALL(callbacks, OnHeader(_, "content-length", "50", _)); | 
 |   EXPECT_CALL(callbacks, OnFrameRecv(IsHeaders(1, NGHTTP2_FLAG_END_HEADERS, | 
 |                                                NGHTTP2_HCAT_REQUEST))); | 
 |  | 
 |   // DATA on stream 1 | 
 |   EXPECT_CALL(callbacks, | 
 |               OnBeginFrame(HasFrameHeader(1, DATA, NGHTTP2_FLAG_END_STREAM))); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnDataChunkRecv(NGHTTP2_FLAG_END_STREAM, 1, | 
 |                                          "Less than 50 bytes.")); | 
 |  | 
 |   // Like nghttp2, CallbackVisitor does not pass on a call to OnFrameRecv in the | 
 |   // case of Content-Length mismatch. | 
 |  | 
 |   int64_t result = adapter->ProcessBytes(frames); | 
 |   EXPECT_EQ(frames.size(), result); | 
 | } | 
 |  | 
 | TEST(ServerCallbackVisitorUnitTest, HeadersAfterFin) { | 
 |   testing::StrictMock<MockNghttp2Callbacks> callbacks; | 
 |   CallbackVisitor visitor(Perspective::kServer, | 
 |                           *MockNghttp2Callbacks::GetCallbacks(), &callbacks); | 
 |  | 
 |   testing::InSequence seq; | 
 |  | 
 |   // HEADERS on stream 1 | 
 |   EXPECT_CALL( | 
 |       callbacks, | 
 |       OnBeginFrame(HasFrameHeader( | 
 |           1, HEADERS, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM))); | 
 |   visitor.OnFrameHeader(1, 23, HEADERS, 5); | 
 |  | 
 |   EXPECT_CALL(callbacks, | 
 |               OnBeginHeaders(IsHeaders( | 
 |                   1, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, | 
 |                   NGHTTP2_HCAT_REQUEST))); | 
 |   EXPECT_TRUE(visitor.OnBeginHeadersForStream(1)); | 
 |  | 
 |   EXPECT_EQ(visitor.stream_map_size(), 1); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnHeader).Times(5); | 
 |   visitor.OnHeaderForStream(1, ":method", "POST"); | 
 |   visitor.OnHeaderForStream(1, ":path", "/example/path"); | 
 |   visitor.OnHeaderForStream(1, ":scheme", "https"); | 
 |   visitor.OnHeaderForStream(1, ":authority", "example.com"); | 
 |   visitor.OnHeaderForStream(1, "accept", "text/html"); | 
 |  | 
 |   EXPECT_CALL(callbacks, | 
 |               OnFrameRecv(IsHeaders( | 
 |                   1, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, | 
 |                   NGHTTP2_HCAT_REQUEST))); | 
 |   visitor.OnEndHeadersForStream(1); | 
 |  | 
 |   EXPECT_TRUE(visitor.OnEndStream(1)); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnStreamClose(1, NGHTTP2_NO_ERROR)); | 
 |   visitor.OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR); | 
 |  | 
 |   EXPECT_EQ(visitor.stream_map_size(), 0); | 
 |  | 
 |   // Invalid repeat HEADERS on closed stream 1 | 
 |   EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader( | 
 |                              1, HEADERS, NGHTTP2_FLAG_END_HEADERS))); | 
 |   visitor.OnFrameHeader(1, 23, HEADERS, 4); | 
 |  | 
 |   EXPECT_CALL(callbacks, OnBeginHeaders(IsHeaders(1, NGHTTP2_FLAG_END_HEADERS, | 
 |                                                   NGHTTP2_HCAT_HEADERS))); | 
 |   EXPECT_TRUE(visitor.OnBeginHeadersForStream(1)); | 
 |  | 
 |   // The visitor should not revive streams that have already been closed. | 
 |   EXPECT_EQ(visitor.stream_map_size(), 0); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace test | 
 | }  // namespace adapter | 
 | }  // namespace http2 |