blob: 2a37ffcc56c0ee9de5572a67caf4d734b380070b [file] [log] [blame]
#include "quiche/http2/adapter/oghttp2_adapter.h"
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "absl/strings/str_join.h"
#include "quiche/http2/adapter/http2_protocol.h"
#include "quiche/http2/adapter/http2_visitor_interface.h"
#include "quiche/http2/adapter/mock_http2_visitor.h"
#include "quiche/http2/adapter/oghttp2_util.h"
#include "quiche/http2/adapter/test_frame_sequence.h"
#include "quiche/http2/adapter/test_utils.h"
#include "quiche/common/platform/api/quiche_expect_bug.h"
#include "quiche/common/platform/api/quiche_test.h"
#include "quiche/spdy/core/http2_header_block.h"
namespace http2 {
namespace adapter {
namespace test {
namespace {
using ConnectionError = Http2VisitorInterface::ConnectionError;
using spdy::SpdyFrameType;
using testing::_;
enum FrameType {
DATA,
HEADERS,
PRIORITY,
RST_STREAM,
SETTINGS,
PUSH_PROMISE,
PING,
GOAWAY,
WINDOW_UPDATE,
CONTINUATION,
};
TEST(OgHttp2AdapterTest, ClientHandlesMetadata) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kClient;
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<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, true, 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, _,
END_STREAM_FLAG | END_HEADERS_FLAG));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _,
END_STREAM_FLAG | END_HEADERS_FLAG, 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({SpdyFrameType::SETTINGS, 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, OnBeginMetadataForStream(0, _));
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, OnBeginMetadataForStream(1, _));
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::HTTP2_NO_ERROR));
const int64_t stream_result = adapter->ProcessBytes(stream_frames);
EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result));
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, ACK_FLAG));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, ACK_FLAG, 0));
EXPECT_TRUE(adapter->want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS}));
}
TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithEmptyPayload) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kClient;
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const int32_t stream_id =
adapter->SubmitRequest(headers1, nullptr, true, nullptr);
ASSERT_GT(stream_id, 0);
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _,
END_STREAM_FLAG | END_HEADERS_FLAG));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _,
END_STREAM_FLAG | END_HEADERS_FLAG, 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({SpdyFrameType::SETTINGS, 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)
.Metadata(1, "")
.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(1, _, HEADERS, 4));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(3);
EXPECT_CALL(visitor, OnEndHeadersForStream(1));
EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4));
EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _));
EXPECT_CALL(visitor, OnMetadataEndForStream(1));
EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 1));
EXPECT_CALL(visitor, OnBeginDataForStream(1, _));
EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
EXPECT_CALL(visitor, OnEndStream(1));
EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR));
const int64_t stream_result = adapter->ProcessBytes(stream_frames);
EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result));
}
TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithPayloadError) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kClient;
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<Header> headers =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const int32_t stream_id =
adapter->SubmitRequest(headers, nullptr, true, nullptr);
ASSERT_GT(stream_id, 0);
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _,
END_STREAM_FLAG | END_HEADERS_FLAG));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _,
END_STREAM_FLAG | END_HEADERS_FLAG, 0));
int result = adapter->Send();
EXPECT_EQ(0, result);
visitor.Clear();
const std::string stream_frames =
TestFrameSequence()
.ServerPreface()
.Metadata(0, "Example connection metadata")
.Headers(stream_id,
{{":status", "200"},
{"server", "my-fake-server"},
{"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
/*fin=*/false)
.Metadata(stream_id, "Example stream metadata")
.Data(stream_id, "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, OnBeginMetadataForStream(0, _));
EXPECT_CALL(visitor, OnMetadataForStream(0, _));
EXPECT_CALL(visitor, OnMetadataEndForStream(0));
EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4));
EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id));
EXPECT_CALL(visitor, OnHeaderForStream(stream_id, _, _)).Times(3);
EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id));
EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, kMetadataFrameType, 4));
EXPECT_CALL(visitor, OnBeginMetadataForStream(stream_id, _));
EXPECT_CALL(visitor, OnMetadataForStream(stream_id, _))
.WillOnce(testing::Return(false));
// Remaining frames are not processed due to the error.
EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError));
const int64_t stream_result = adapter->ProcessBytes(stream_frames);
// Negative integer returned to indicate an error.
EXPECT_LT(stream_result, 0);
EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
EXPECT_CALL(visitor,
OnFrameSent(GOAWAY, 0, _, 0x0,
static_cast<int>(Http2ErrorCode::INTERNAL_ERROR)));
EXPECT_FALSE(adapter->want_read());
EXPECT_TRUE(adapter->want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY}));
}
TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithCompletionError) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kClient;
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<Header> headers =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const int32_t stream_id =
adapter->SubmitRequest(headers, nullptr, true, nullptr);
ASSERT_GT(stream_id, 0);
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _,
END_STREAM_FLAG | END_HEADERS_FLAG));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _,
END_STREAM_FLAG | END_HEADERS_FLAG, 0));
int result = adapter->Send();
EXPECT_EQ(0, result);
visitor.Clear();
const std::string stream_frames =
TestFrameSequence()
.ServerPreface()
.Metadata(0, "Example connection metadata")
.Headers(stream_id,
{{":status", "200"},
{"server", "my-fake-server"},
{"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
/*fin=*/false)
.Metadata(stream_id, "Example stream metadata")
.Data(stream_id, "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, OnBeginMetadataForStream(0, _));
EXPECT_CALL(visitor, OnMetadataForStream(0, _));
EXPECT_CALL(visitor, OnMetadataEndForStream(0));
EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4));
EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id));
EXPECT_CALL(visitor, OnHeaderForStream(stream_id, _, _)).Times(3);
EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id));
EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, kMetadataFrameType, 4));
EXPECT_CALL(visitor, OnBeginMetadataForStream(stream_id, _));
EXPECT_CALL(visitor, OnMetadataForStream(stream_id, _));
EXPECT_CALL(visitor, OnMetadataEndForStream(stream_id))
.WillOnce(testing::Return(false));
// Remaining frames are not processed due to the error.
EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError));
const int64_t stream_result = adapter->ProcessBytes(stream_frames);
// Negative integer returned to indicate an error.
EXPECT_LT(stream_result, 0);
EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
EXPECT_CALL(visitor,
OnFrameSent(GOAWAY, 0, _, 0x0,
static_cast<int>(Http2ErrorCode::INTERNAL_ERROR)));
EXPECT_FALSE(adapter->want_read());
EXPECT_TRUE(adapter->want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY}));
}
TEST(OgHttp2AdapterTest, ClientSendsMetadataAfterFlowControlBlock) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kClient;
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const std::string kBody = std::string(100 * 1024, 'a');
auto body1 = std::make_unique<TestDataFrameSource>(visitor, false);
body1->AppendPayload(kBody);
body1->EndData();
const int32_t stream_id1 =
adapter->SubmitRequest(headers1, std::move(body1), false, nullptr);
ASSERT_GT(stream_id1, 0);
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x4));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x4, 0));
// 4 DATA frames should saturate the default 64kB stream/connection flow
// control window.
EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id1, _, 0x0, 0)).Times(4);
int result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_FALSE(adapter->want_write());
EXPECT_EQ(0, adapter->GetSendWindowSize());
auto source = std::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders(
{{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}})));
adapter->SubmitMetadata(1, 16384u, std::move(source));
EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4));
EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0));
result = adapter->Send();
EXPECT_EQ(0, result);
}
TEST(OgHttp2AdapterTest, ClientSendsMetadataWithContinuation) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kServer;
auto adapter = OgHttp2Adapter::Create(visitor, options);
EXPECT_FALSE(adapter->want_write());
const std::string frames =
TestFrameSequence()
.ClientPreface()
.Metadata(0, "Example connection metadata in multiple frames", true)
.Headers(1,
{{":method", "GET"},
{":scheme", "https"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}},
/*fin=*/false,
/*add_continuation=*/true)
.Metadata(1,
"Some stream metadata that's also sent in multiple frames",
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());
// Metadata on stream 0
EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 0));
EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _));
EXPECT_CALL(visitor, OnMetadataForStream(0, _));
EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4));
EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _));
EXPECT_CALL(visitor, OnMetadataForStream(0, _));
EXPECT_CALL(visitor, OnMetadataEndForStream(0));
// Stream 1
EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
EXPECT_CALL(visitor, OnFrameHeader(1, _, CONTINUATION, 4));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
EXPECT_CALL(visitor, OnEndHeadersForStream(1));
// Metadata on stream 1
EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 0));
EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _));
EXPECT_CALL(visitor, OnMetadataForStream(1, _));
EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4));
EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _));
EXPECT_CALL(visitor, OnMetadataForStream(1, _));
EXPECT_CALL(visitor, OnMetadataEndForStream(1));
const int64_t result = adapter->ProcessBytes(frames);
EXPECT_EQ(frames.size(), static_cast<size_t>(result));
EXPECT_EQ("Example connection metadata in multiple frames",
absl::StrJoin(visitor.GetMetadata(0), ""));
EXPECT_EQ("Some stream metadata that's also sent in multiple frames",
absl::StrJoin(visitor.GetMetadata(1), ""));
}
TEST(OgHttp2AdapterTest, SubmitMetadata) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kServer;
auto adapter = OgHttp2Adapter::Create(visitor, options);
auto source = std::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders(
{{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}})));
adapter->SubmitMetadata(1, 16384u, std::move(source));
EXPECT_TRUE(adapter->want_write());
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4));
EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0));
int result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(),
EqualsFrames({SpdyFrameType::SETTINGS,
static_cast<SpdyFrameType>(kMetadataFrameType)}));
EXPECT_FALSE(adapter->want_write());
}
TEST(OgHttp2AdapterTest, SubmitMetadataMultipleFrames) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kServer;
auto adapter = OgHttp2Adapter::Create(visitor, options);
const auto kLargeValue = std::string(63 * 1024, 'a');
auto source = std::make_unique<TestMetadataSource>(
ToHeaderBlock(ToHeaders({{"large-value", kLargeValue}})));
adapter->SubmitMetadata(1, 16384u, std::move(source));
EXPECT_TRUE(adapter->want_write());
testing::InSequence seq;
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4));
EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0));
int result = adapter->Send();
EXPECT_EQ(0, result);
absl::string_view serialized = visitor.data();
EXPECT_THAT(serialized,
EqualsFrames({SpdyFrameType::SETTINGS,
static_cast<SpdyFrameType>(kMetadataFrameType),
static_cast<SpdyFrameType>(kMetadataFrameType),
static_cast<SpdyFrameType>(kMetadataFrameType),
static_cast<SpdyFrameType>(kMetadataFrameType)}));
EXPECT_FALSE(adapter->want_write());
}
TEST(OgHttp2AdapterTest, SubmitConnectionMetadata) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kServer;
auto adapter = OgHttp2Adapter::Create(visitor, options);
auto source = std::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders(
{{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}})));
adapter->SubmitMetadata(0, 16384u, std::move(source));
EXPECT_TRUE(adapter->want_write());
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 0, _, 0x4));
EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 0, _, 0x4, 0));
int result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(),
EqualsFrames({SpdyFrameType::SETTINGS,
static_cast<SpdyFrameType>(kMetadataFrameType)}));
EXPECT_FALSE(adapter->want_write());
}
TEST(OgHttp2AdapterTest, ServerQueuesMetadataThenTrailers) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kServer;
auto adapter = OgHttp2Adapter::Create(visitor, options);
EXPECT_FALSE(adapter->want_write());
const std::string frames = TestFrameSequence()
.ClientPreface()
.Headers(1,
{{":method", "GET"},
{":scheme", "https"},
{":authority", "example.com"},
{":path", "/"}},
/*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, _, _)).Times(4);
EXPECT_CALL(visitor, OnEndHeadersForStream(1));
EXPECT_CALL(visitor, OnEndStream(1));
const int64_t result = adapter->ProcessBytes(frames);
EXPECT_EQ(frames.size(), static_cast<size_t>(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 = std::make_unique<TestDataFrameSource>(visitor, false);
body1->AppendPayload(kBody);
body1->EndData();
int submit_result = adapter->SubmitResponse(
1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}),
std::move(body1), false);
EXPECT_EQ(submit_result, 0);
EXPECT_TRUE(adapter->want_write());
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, ACK_FLAG));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, ACK_FLAG, 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({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS,
SpdyFrameType::HEADERS, SpdyFrameType::DATA}));
EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody));
visitor.Clear();
EXPECT_FALSE(adapter->want_write());
spdy::Http2HeaderBlock block;
block["key"] = "wild value!";
adapter->SubmitMetadata(
1, 16384u, std::make_unique<TestMetadataSource>(std::move(block)));
int trailer_result =
adapter->SubmitTrailer(1, ToHeaders({{":final-status", "a-ok"}}));
ASSERT_EQ(trailer_result, 0);
EXPECT_TRUE(adapter->want_write());
EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4));
EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _,
END_STREAM_FLAG | END_HEADERS_FLAG));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _,
END_STREAM_FLAG | END_HEADERS_FLAG, 0));
EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR));
send_result = adapter->Send();
EXPECT_EQ(0, send_result);
EXPECT_THAT(visitor.data(),
EqualsFrames({static_cast<SpdyFrameType>(kMetadataFrameType),
SpdyFrameType::HEADERS}));
}
} // namespace
} // namespace test
} // namespace adapter
} // namespace http2