blob: 4a4307b2439e0383e2ed33e565c9f83884119833 [file] [log] [blame]
#include "http2/adapter/oghttp2_adapter.h"
#include "http2/adapter/mock_http2_visitor.h"
#include "http2/adapter/test_frame_sequence.h"
#include "http2/adapter/test_utils.h"
#include "common/platform/api/quiche_test.h"
#include "common/platform/api/quiche_test_helpers.h"
namespace http2 {
namespace adapter {
namespace test {
namespace {
using testing::_;
enum FrameType {
DATA,
HEADERS,
PRIORITY,
RST_STREAM,
SETTINGS,
PUSH_PROMISE,
PING,
GOAWAY,
WINDOW_UPDATE,
};
using spdy::SpdyFrameType;
class OgHttp2AdapterTest : public testing::Test {
protected:
void SetUp() override {
OgHttp2Adapter::Options options{.perspective = Perspective::kServer};
adapter_ = OgHttp2Adapter::Create(http2_visitor_, options);
}
DataSavingVisitor http2_visitor_;
std::unique_ptr<OgHttp2Adapter> adapter_;
};
TEST_F(OgHttp2AdapterTest, IsServerSession) {
EXPECT_TRUE(adapter_->IsServerSession());
}
TEST_F(OgHttp2AdapterTest, ProcessBytes) {
testing::InSequence seq;
EXPECT_CALL(http2_visitor_, OnFrameHeader(0, 0, 4, 0));
EXPECT_CALL(http2_visitor_, OnSettingsStart());
EXPECT_CALL(http2_visitor_, OnSettingsEnd());
EXPECT_CALL(http2_visitor_, OnFrameHeader(0, 8, 6, 0));
EXPECT_CALL(http2_visitor_, OnPing(17, false));
adapter_->ProcessBytes(
TestFrameSequence().ClientPreface().Ping(17).Serialize());
}
TEST(OgHttp2AdapterClientTest, ClientHandlesTrailers) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<const Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const char* kSentinel1 = "arbitrary pointer 1";
const int32_t stream_id1 =
adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
ASSERT_GT(stream_id1, 0);
QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
int result = adapter->Send();
EXPECT_EQ(0, result);
absl::string_view data = visitor.data();
EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
spdy::SpdyFrameType::HEADERS}));
visitor.Clear();
const std::string stream_frames =
TestFrameSequence()
.ServerPreface()
.Headers(1,
{{":status", "200"},
{"server", "my-fake-server"},
{"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
/*fin=*/false)
.Data(1, "This is the response body.")
.Headers(1, {{"final-status", "A-OK"}},
/*fin=*/true)
.Serialize();
// Server preface (empty SETTINGS)
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSettingsEnd());
EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
EXPECT_CALL(visitor,
OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"));
EXPECT_CALL(visitor, OnEndHeadersForStream(1));
EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 0));
EXPECT_CALL(visitor, OnBeginDataForStream(1, 26));
EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
EXPECT_CALL(visitor, OnHeaderForStream(1, "final-status", "A-OK"));
EXPECT_CALL(visitor, OnEndHeadersForStream(1));
EXPECT_CALL(visitor, OnEndStream(1));
EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
EXPECT_EQ(stream_frames.size(), stream_result);
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
EXPECT_TRUE(adapter->session().want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
}
TEST(OgHttp2AdapterClientTest, ClientHandlesMetadata) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<const Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const char* kSentinel1 = "arbitrary pointer 1";
const int32_t stream_id1 =
adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
ASSERT_GT(stream_id1, 0);
QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
int result = adapter->Send();
EXPECT_EQ(0, result);
absl::string_view data = visitor.data();
EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
spdy::SpdyFrameType::HEADERS}));
visitor.Clear();
const std::string stream_frames =
TestFrameSequence()
.ServerPreface()
.Metadata(0, "Example connection metadata")
.Headers(1,
{{":status", "200"},
{"server", "my-fake-server"},
{"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
/*fin=*/false)
.Metadata(1, "Example stream metadata")
.Data(1, "This is the response body.", true)
.Serialize();
// Server preface (empty SETTINGS)
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSettingsEnd());
EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4));
// EXPECT_CALL(visitor, OnMetadataForStream(0, _));
// EXPECT_CALL(visitor, OnMetadataEndForStream(0));
EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
EXPECT_CALL(visitor,
OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"));
EXPECT_CALL(visitor, OnEndHeadersForStream(1));
EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4));
// EXPECT_CALL(visitor, OnMetadataForStream(1, _));
// EXPECT_CALL(visitor, OnMetadataEndForStream(1));
EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 1));
EXPECT_CALL(visitor, OnBeginDataForStream(1, 26));
EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
EXPECT_CALL(visitor, OnEndStream(1));
EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
EXPECT_EQ(stream_frames.size(), stream_result);
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
EXPECT_TRUE(adapter->session().want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
}
TEST(OgHttp2AdapterClientTest, ClientRstStreamWhileHandlingHeaders) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<const Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const char* kSentinel1 = "arbitrary pointer 1";
const int32_t stream_id1 =
adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
ASSERT_GT(stream_id1, 0);
QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
int result = adapter->Send();
EXPECT_EQ(0, result);
absl::string_view data = visitor.data();
EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
spdy::SpdyFrameType::HEADERS}));
visitor.Clear();
const std::string stream_frames =
TestFrameSequence()
.ServerPreface()
.Headers(1,
{{":status", "200"},
{"server", "my-fake-server"},
{"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
/*fin=*/false)
.Data(1, "This is the response body.")
.Serialize();
// Server preface (empty SETTINGS)
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSettingsEnd());
EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
EXPECT_CALL(visitor,
OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"))
.WillOnce(testing::DoAll(
testing::InvokeWithoutArgs([&adapter]() {
adapter->SubmitRst(1, Http2ErrorCode::REFUSED_STREAM);
}),
testing::Return(Http2VisitorInterface::HEADER_RST_STREAM)));
EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0));
EXPECT_CALL(visitor, OnBeginDataForStream(1, _));
EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
EXPECT_EQ(stream_frames.size(), stream_result);
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id1, 4, 0x0));
EXPECT_CALL(visitor,
OnFrameSent(RST_STREAM, stream_id1, 4, 0x0,
static_cast<int>(Http2ErrorCode::REFUSED_STREAM)));
EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
EXPECT_TRUE(adapter->session().want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
spdy::SpdyFrameType::RST_STREAM}));
}
TEST(NgHttp2AdapterTest, ClientConnectionErrorWhileHandlingHeaders) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<const Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const char* kSentinel1 = "arbitrary pointer 1";
const int32_t stream_id1 =
adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
ASSERT_GT(stream_id1, 0);
QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
int result = adapter->Send();
EXPECT_EQ(0, result);
absl::string_view data = visitor.data();
EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
spdy::SpdyFrameType::HEADERS}));
visitor.Clear();
const std::string stream_frames =
TestFrameSequence()
.ServerPreface()
.Headers(1,
{{":status", "200"},
{"server", "my-fake-server"},
{"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
/*fin=*/false)
.Data(1, "This is the response body.")
.Serialize();
// Server preface (empty SETTINGS)
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSettingsEnd());
EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
EXPECT_CALL(visitor,
OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"))
.WillOnce(
testing::Return(Http2VisitorInterface::HEADER_CONNECTION_ERROR));
EXPECT_CALL(visitor, OnConnectionError());
// Note: OgHttp2Adapter continues processing bytes until the input is
// complete.
EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0));
EXPECT_CALL(visitor, OnBeginDataForStream(1, _));
EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
EXPECT_EQ(stream_frames.size(), stream_result);
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
EXPECT_TRUE(adapter->session().want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
}
// TODO(birenroy): Validate headers and re-enable this test. The library should
// invoke OnErrorDebug() with an error message for the invalid header. The
// library should also invoke OnInvalidFrame() for the invalid HEADERS frame.
TEST(OgHttp2AdapterClientTest, DISABLED_ClientHandlesInvalidTrailers) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<const Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const char* kSentinel1 = "arbitrary pointer 1";
const int32_t stream_id1 =
adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
ASSERT_GT(stream_id1, 0);
QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
int result = adapter->Send();
EXPECT_EQ(0, result);
absl::string_view data = visitor.data();
EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
spdy::SpdyFrameType::HEADERS}));
visitor.Clear();
const std::string stream_frames =
TestFrameSequence()
.ServerPreface()
.Headers(1,
{{":status", "200"},
{"server", "my-fake-server"},
{"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
/*fin=*/false)
.Data(1, "This is the response body.")
.Headers(1, {{":bad-status", "9000"}},
/*fin=*/true)
.Serialize();
// Server preface (empty SETTINGS)
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSettingsEnd());
EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200"));
EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server"));
EXPECT_CALL(visitor,
OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"));
EXPECT_CALL(visitor, OnEndHeadersForStream(1));
EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 0));
EXPECT_CALL(visitor, OnBeginDataForStream(1, 26));
EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
// Bad status trailer will cause a PROTOCOL_ERROR. The header is never
// delivered in an OnHeaderForStream callback.
const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
EXPECT_EQ(stream_frames.size(), stream_result);
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id1, 4, 0x0));
EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, stream_id1, 4, 0x0, 1));
EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::PROTOCOL_ERROR));
EXPECT_TRUE(adapter->session().want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
spdy::SpdyFrameType::RST_STREAM}));
}
TEST_F(OgHttp2AdapterTest, SubmitMetadata) {
EXPECT_QUICHE_BUG(adapter_->SubmitMetadata(3, true), "Not implemented");
}
TEST_F(OgHttp2AdapterTest, GetSendWindowSize) {
const int peer_window = adapter_->GetSendWindowSize();
EXPECT_EQ(peer_window, kInitialFlowControlWindowSize);
}
TEST_F(OgHttp2AdapterTest, MarkDataConsumedForStream) {
EXPECT_QUICHE_BUG(adapter_->MarkDataConsumedForStream(1, 11),
"Stream 1 not found");
}
TEST_F(OgHttp2AdapterTest, TestSerialize) {
EXPECT_TRUE(adapter_->session().want_read());
EXPECT_FALSE(adapter_->session().want_write());
adapter_->SubmitSettings(
{{HEADER_TABLE_SIZE, 128}, {MAX_FRAME_SIZE, 128 << 10}});
EXPECT_TRUE(adapter_->session().want_write());
adapter_->SubmitPriorityForStream(3, 1, 255, true);
adapter_->SubmitRst(3, Http2ErrorCode::CANCEL);
adapter_->SubmitPing(42);
adapter_->SubmitGoAway(13, Http2ErrorCode::NO_ERROR, "");
adapter_->SubmitWindowUpdate(3, 127);
EXPECT_TRUE(adapter_->session().want_write());
EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(http2_visitor_, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(PRIORITY, 3, _, 0x0));
EXPECT_CALL(http2_visitor_, OnFrameSent(PRIORITY, 3, _, 0x0, 0));
EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(RST_STREAM, 3, _, 0x0));
EXPECT_CALL(http2_visitor_, OnFrameSent(RST_STREAM, 3, _, 0x0, 0x8));
EXPECT_CALL(http2_visitor_, OnCloseStream(3, Http2ErrorCode::NO_ERROR));
EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(PING, 0, _, 0x0));
EXPECT_CALL(http2_visitor_, OnFrameSent(PING, 0, _, 0x0, 0));
EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
EXPECT_CALL(http2_visitor_, OnFrameSent(GOAWAY, 0, _, 0x0, 0));
EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(WINDOW_UPDATE, 3, _, 0x0));
EXPECT_CALL(http2_visitor_, OnFrameSent(WINDOW_UPDATE, 3, _, 0x0, 0));
int result = adapter_->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(
http2_visitor_.data(),
EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::PRIORITY,
SpdyFrameType::RST_STREAM, SpdyFrameType::PING,
SpdyFrameType::GOAWAY, SpdyFrameType::WINDOW_UPDATE}));
EXPECT_FALSE(adapter_->session().want_write());
}
TEST_F(OgHttp2AdapterTest, TestPartialSerialize) {
EXPECT_FALSE(adapter_->session().want_write());
adapter_->SubmitSettings(
{{HEADER_TABLE_SIZE, 128}, {MAX_FRAME_SIZE, 128 << 10}});
adapter_->SubmitGoAway(13, Http2ErrorCode::NO_ERROR, "And don't come back!");
adapter_->SubmitPing(42);
EXPECT_TRUE(adapter_->session().want_write());
http2_visitor_.set_send_limit(20);
EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(http2_visitor_, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
int result = adapter_->Send();
EXPECT_EQ(0, result);
EXPECT_TRUE(adapter_->session().want_write());
EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
EXPECT_CALL(http2_visitor_, OnFrameSent(GOAWAY, 0, _, 0x0, 0));
result = adapter_->Send();
EXPECT_EQ(0, result);
EXPECT_TRUE(adapter_->session().want_write());
EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(PING, 0, _, 0x0));
EXPECT_CALL(http2_visitor_, OnFrameSent(PING, 0, _, 0x0, 0));
result = adapter_->Send();
EXPECT_EQ(0, result);
EXPECT_FALSE(adapter_->session().want_write());
EXPECT_THAT(http2_visitor_.data(),
EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY,
SpdyFrameType::PING}));
}
TEST(OgHttp2AdapterServerTest, ServerSendsInvalidTrailers) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options{.perspective = Perspective::kServer};
auto adapter = OgHttp2Adapter::Create(visitor, options);
EXPECT_FALSE(adapter->session().want_write());
const std::string frames = TestFrameSequence()
.ClientPreface()
.Headers(1,
{{":method", "GET"},
{":scheme", "https"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}},
/*fin=*/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, 5));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
EXPECT_CALL(visitor, OnEndHeadersForStream(1));
EXPECT_CALL(visitor, OnEndStream(1));
const ssize_t result = adapter->ProcessBytes(frames);
EXPECT_EQ(frames.size(), result);
const absl::string_view kBody = "This is an example response body.";
// The body source must indicate that the end of the body is not the end of
// the stream.
auto body1 =
absl::make_unique<TestDataFrameSource>(visitor, kBody, /*has_fin=*/false);
int submit_result = adapter->SubmitResponse(
1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}),
std::move(body1));
EXPECT_EQ(submit_result, 0);
EXPECT_TRUE(adapter->session().want_write());
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0));
int send_result = adapter->Send();
EXPECT_EQ(0, send_result);
EXPECT_THAT(visitor.data(),
EqualsFrames(
{spdy::SpdyFrameType::SETTINGS, spdy::SpdyFrameType::SETTINGS,
spdy::SpdyFrameType::HEADERS, spdy::SpdyFrameType::DATA}));
EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody));
visitor.Clear();
EXPECT_FALSE(adapter->session().want_write());
// The body source has been exhausted by the call to Send() above.
EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
int trailer_result =
adapter->SubmitTrailer(1, ToHeaders({{":final-status", "a-ok"}}));
ASSERT_EQ(trailer_result, 0);
EXPECT_TRUE(adapter->session().want_write());
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0));
send_result = adapter->Send();
EXPECT_EQ(0, send_result);
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS}));
}
TEST(OgHttp2AdapterServerTest, ServerErrorWhileHandlingHeaders) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options{.perspective = Perspective::kServer};
auto adapter = OgHttp2Adapter::Create(visitor, options);
const std::string frames = TestFrameSequence()
.ClientPreface()
.Headers(1,
{{":method", "POST"},
{":scheme", "https"},
{":authority", "example.com"},
{":path", "/this/is/request/one"},
{"accept", "some bogus value!"}},
/*fin=*/false)
.WindowUpdate(1, 2000)
.Data(1, "This is the request body.")
.WindowUpdate(0, 2000)
.Serialize();
testing::InSequence s;
// Client preface (empty SETTINGS)
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSettingsEnd());
EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
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", "/this/is/request/one"));
EXPECT_CALL(visitor, OnHeaderForStream(1, "accept", "some bogus value!"))
.WillOnce(testing::Return(Http2VisitorInterface::HEADER_RST_STREAM));
EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0));
EXPECT_CALL(visitor, OnWindowUpdate(1, 2000));
EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0));
EXPECT_CALL(visitor, OnBeginDataForStream(1, _));
EXPECT_CALL(visitor, OnDataForStream(1, "This is the request body."));
EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0));
EXPECT_CALL(visitor, OnWindowUpdate(0, 2000));
const ssize_t result = adapter->ProcessBytes(frames);
EXPECT_EQ(frames.size(), result);
EXPECT_TRUE(adapter->session().want_write());
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, 4, 0x0));
EXPECT_CALL(visitor,
OnFrameSent(RST_STREAM, 1, 4, 0x0,
static_cast<int>(Http2ErrorCode::INTERNAL_ERROR)));
EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
int send_result = adapter->Send();
// Some bytes should have been serialized.
EXPECT_EQ(0, send_result);
// SETTINGS ack
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
spdy::SpdyFrameType::SETTINGS,
spdy::SpdyFrameType::RST_STREAM}));
}
} // namespace
} // namespace test
} // namespace adapter
} // namespace http2