Adds a `OgHttp2Session` unit test exercising the case where the visitor returns false in `OnEndStream()`.
PiperOrigin-RevId: 488664335
diff --git a/quiche/http2/adapter/nghttp2_session_test.cc b/quiche/http2/adapter/nghttp2_session_test.cc
index 61500bb..f11c490 100644
--- a/quiche/http2/adapter/nghttp2_session_test.cc
+++ b/quiche/http2/adapter/nghttp2_session_test.cc
@@ -319,6 +319,55 @@
"Extension frame payload for stream 1 is null!");
}
+TEST_F(NgHttp2SessionTest, ServerSeesErrorOnEndStream) {
+ NgHttp2Session session(Perspective::kServer, CreateCallbacks(), options_,
+ &visitor_);
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "POST"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/"}},
+ /*fin=*/false)
+ .Data(1, "Request body", true)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor_, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor_, OnSettingsStart());
+ EXPECT_CALL(visitor_, OnSettingsEnd());
+ // Stream 1
+ EXPECT_CALL(visitor_, OnFrameHeader(1, _, HEADERS, 0x4));
+ EXPECT_CALL(visitor_, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor_, OnHeaderForStream(1, ":method", "POST"));
+ EXPECT_CALL(visitor_, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor_, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor_, OnHeaderForStream(1, ":path", "/"));
+ EXPECT_CALL(visitor_, OnEndHeadersForStream(1));
+
+ EXPECT_CALL(visitor_, OnFrameHeader(1, _, DATA, 0x1));
+ EXPECT_CALL(visitor_, OnBeginDataForStream(1, _));
+ EXPECT_CALL(visitor_, OnDataForStream(1, "Request body"));
+ EXPECT_CALL(visitor_, OnEndStream(1)).WillOnce(testing::Return(false));
+
+ const int64_t result = session.ProcessBytes(frames);
+ EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, result);
+
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor_, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+ ASSERT_EQ(0, nghttp2_session_send(session.raw_ptr()));
+ EXPECT_THAT(visitor_.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+ visitor_.Clear();
+
+ EXPECT_FALSE(session.want_write());
+}
+
} // namespace
} // namespace test
} // namespace adapter
diff --git a/quiche/http2/adapter/oghttp2_session_test.cc b/quiche/http2/adapter/oghttp2_session_test.cc
index 59f5c4b..f536cd7 100644
--- a/quiche/http2/adapter/oghttp2_session_test.cc
+++ b/quiche/http2/adapter/oghttp2_session_test.cc
@@ -933,6 +933,68 @@
SpdyFrameType::HEADERS}));
}
+TEST(OgHttp2SessionTest, ServerSeesErrorOnEndStream) {
+ DataSavingVisitor visitor;
+ OgHttp2Session::Options options;
+ options.perspective = Perspective::kServer;
+ OgHttp2Session session(visitor, options);
+
+ const std::string frames = TestFrameSequence()
+ .ClientPreface()
+ .Headers(1,
+ {{":method", "POST"},
+ {":scheme", "https"},
+ {":authority", "example.com"},
+ {":path", "/"}},
+ /*fin=*/false)
+ .Data(1, "Request body", true)
+ .Serialize();
+ testing::InSequence s;
+
+ // Client preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Stream 1
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0x4));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/"));
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0x1));
+ EXPECT_CALL(visitor, OnBeginDataForStream(1, _));
+ EXPECT_CALL(visitor, OnDataForStream(1, "Request body"));
+ EXPECT_CALL(visitor, OnEndStream(1)).WillOnce(testing::Return(false));
+ EXPECT_CALL(
+ visitor,
+ OnConnectionError(Http2VisitorInterface::ConnectionError::kParseError));
+
+ const int64_t result = session.ProcessBytes(frames);
+ EXPECT_EQ(/*NGHTTP2_ERR_CALLBACK_FAILURE=*/-902, result);
+
+ EXPECT_TRUE(session.want_write());
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
+ EXPECT_CALL(
+ visitor,
+ OnFrameSent(GOAWAY, 0, _, 0x0,
+ static_cast<int>(
+ Http2VisitorInterface::ConnectionError::kParseError)));
+
+ int send_result = session.Send();
+ EXPECT_EQ(0, send_result);
+ EXPECT_THAT(visitor.data(),
+ EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY}));
+ visitor.Clear();
+
+ EXPECT_FALSE(session.want_write());
+}
+
} // namespace test
} // namespace adapter
} // namespace http2