blob: 1bbb9e4901edda58aaf8b231a4535ededea7c3a1 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h"
#include <cstdint>
#include <ostream>
#include <tuple>
#include <utility>
#include <vector>
#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
#include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h"
#include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
using spdy::ERROR_CODE_PROTOCOL_ERROR;
using spdy::SETTINGS_ENABLE_PUSH;
using spdy::SETTINGS_HEADER_TABLE_SIZE;
using spdy::SETTINGS_INITIAL_WINDOW_SIZE;
using spdy::SETTINGS_MAX_CONCURRENT_STREAMS;
using spdy::SETTINGS_MAX_FRAME_SIZE;
using spdy::SETTINGS_MAX_HEADER_LIST_SIZE;
using spdy::Spdy3PriorityToHttp2Weight;
using spdy::SpdyAltSvcWireFormat;
using spdy::SpdyDataIR;
using spdy::SpdyErrorCode;
using spdy::SpdyFramer;
using spdy::SpdyFramerVisitorInterface;
using spdy::SpdyGoAwayIR;
using spdy::SpdyHeaderBlock;
using spdy::SpdyHeadersHandlerInterface;
using spdy::SpdyHeadersIR;
using spdy::SpdyKnownSettingsId;
using spdy::SpdyPingId;
using spdy::SpdyPingIR;
using spdy::SpdyPriority;
using spdy::SpdyPriorityIR;
using spdy::SpdyPushPromiseIR;
using spdy::SpdyRstStreamIR;
using spdy::SpdySerializedFrame;
using spdy::SpdySettingsId;
using spdy::SpdySettingsIR;
using spdy::SpdyStreamId;
using spdy::SpdyWindowUpdateIR;
using spdy::test::TestHeadersHandler;
using testing::_;
using testing::AtLeast;
using testing::InSequence;
using testing::Invoke;
using testing::Return;
using testing::StrictMock;
using testing::WithArgs;
namespace quic {
namespace test {
class MockQuicHpackDebugVisitor : public QuicHpackDebugVisitor {
public:
MockQuicHpackDebugVisitor() : QuicHpackDebugVisitor() {}
MockQuicHpackDebugVisitor(const MockQuicHpackDebugVisitor&) = delete;
MockQuicHpackDebugVisitor& operator=(const MockQuicHpackDebugVisitor&) =
delete;
MOCK_METHOD1(OnUseEntry, void(QuicTime::Delta elapsed));
};
namespace {
class MockVisitor : public SpdyFramerVisitorInterface {
public:
MOCK_METHOD1(OnError,
void(http2::Http2DecoderAdapter::SpdyFramerError error));
MOCK_METHOD3(OnDataFrameHeader,
void(SpdyStreamId stream_id, size_t length, bool fin));
MOCK_METHOD3(OnStreamFrameData,
void(SpdyStreamId stream_id, const char* data, size_t len));
MOCK_METHOD1(OnStreamEnd, void(SpdyStreamId stream_id));
MOCK_METHOD2(OnStreamPadding, void(SpdyStreamId stream_id, size_t len));
MOCK_METHOD1(OnHeaderFrameStart,
SpdyHeadersHandlerInterface*(SpdyStreamId stream_id));
MOCK_METHOD1(OnHeaderFrameEnd, void(SpdyStreamId stream_id));
MOCK_METHOD3(OnControlFrameHeaderData,
bool(SpdyStreamId stream_id,
const char* header_data,
size_t len));
MOCK_METHOD2(OnRstStream,
void(SpdyStreamId stream_id, SpdyErrorCode error_code));
MOCK_METHOD0(OnSettings, void());
MOCK_METHOD2(OnSetting, void(SpdySettingsId id, uint32_t value));
MOCK_METHOD0(OnSettingsAck, void());
MOCK_METHOD0(OnSettingsEnd, void());
MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
MOCK_METHOD2(OnGoAway,
void(SpdyStreamId last_accepted_stream_id,
SpdyErrorCode error_code));
MOCK_METHOD7(OnHeaders,
void(SpdyStreamId stream_id,
bool has_priority,
int weight,
SpdyStreamId parent_stream_id,
bool exclusive,
bool fin,
bool end));
MOCK_METHOD2(OnWindowUpdate,
void(SpdyStreamId stream_id, int delta_window_size));
MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id));
MOCK_METHOD3(OnPushPromise,
void(SpdyStreamId stream_id,
SpdyStreamId promised_stream_id,
bool end));
MOCK_METHOD2(OnContinuation, void(SpdyStreamId stream_id, bool end));
MOCK_METHOD3(OnAltSvc,
void(SpdyStreamId stream_id,
QuicStringPiece origin,
const SpdyAltSvcWireFormat::AlternativeServiceVector&
altsvc_vector));
MOCK_METHOD4(OnPriority,
void(SpdyStreamId stream_id,
SpdyStreamId parent_stream_id,
int weight,
bool exclusive));
MOCK_METHOD2(OnUnknownFrame,
bool(SpdyStreamId stream_id, uint8_t frame_type));
};
struct TestParams {
TestParams(const ParsedQuicVersion& version, Perspective perspective)
: version(version), perspective(perspective) {
QUIC_LOG(INFO) << "TestParams: version: "
<< ParsedQuicVersionToString(version)
<< ", perspective: " << perspective;
}
TestParams(const TestParams& other)
: version(other.version), perspective(other.perspective) {}
ParsedQuicVersion version;
Perspective perspective;
};
std::vector<TestParams> GetTestParams() {
std::vector<TestParams> params;
ParsedQuicVersionVector all_supported_versions = AllSupportedVersions();
for (size_t i = 0; i < all_supported_versions.size(); ++i) {
for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) {
params.emplace_back(all_supported_versions[i], p);
}
}
return params;
}
class QuicHeadersStreamTest : public QuicTestWithParam<TestParams> {
public:
QuicHeadersStreamTest()
: connection_(new StrictMock<MockQuicConnection>(&helper_,
&alarm_factory_,
perspective(),
GetVersion())),
session_(connection_),
body_("hello world"),
stream_frame_(
QuicUtils::GetHeadersStreamId(connection_->transport_version()),
/*fin=*/false,
/*offset=*/0,
""),
next_promised_stream_id_(2) {
session_.Initialize();
headers_stream_ = QuicSpdySessionPeer::GetHeadersStream(&session_);
headers_[":version"] = "HTTP/1.1";
headers_[":status"] = "200 Ok";
headers_["content-length"] = "11";
framer_ = std::unique_ptr<SpdyFramer>(
new SpdyFramer(SpdyFramer::ENABLE_COMPRESSION));
deframer_ = std::unique_ptr<http2::Http2DecoderAdapter>(
new http2::Http2DecoderAdapter());
deframer_->set_visitor(&visitor_);
EXPECT_EQ(transport_version(), session_.connection()->transport_version());
EXPECT_TRUE(headers_stream_ != nullptr);
connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
client_id_1_ =
QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
session_, 0);
client_id_2_ =
QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
session_, 1);
client_id_3_ =
QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
session_, 2);
next_stream_id_ = QuicSpdySessionPeer::StreamIdDelta(session_);
}
QuicStreamId GetNthClientInitiatedId(int n) {
return QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
session_, n);
}
QuicConsumedData SaveIov(size_t write_length) {
char* buf = new char[write_length];
QuicDataWriter writer(write_length, buf, NETWORK_BYTE_ORDER);
headers_stream_->WriteStreamData(headers_stream_->stream_bytes_written(),
write_length, &writer);
saved_data_.append(buf, write_length);
delete[] buf;
return QuicConsumedData(write_length, false);
}
void SavePayload(const char* data, size_t len) {
saved_payloads_.append(data, len);
}
bool SaveHeaderData(const char* data, int len) {
saved_header_data_.append(data, len);
return true;
}
void SaveHeaderDataStringPiece(QuicStringPiece data) {
saved_header_data_.append(data.data(), data.length());
}
void SavePromiseHeaderList(QuicStreamId /* stream_id */,
QuicStreamId /* promised_stream_id */,
size_t size,
const QuicHeaderList& header_list) {
SaveToHandler(size, header_list);
}
void SaveHeaderList(QuicStreamId /* stream_id */,
bool /* fin */,
size_t size,
const QuicHeaderList& header_list) {
SaveToHandler(size, header_list);
}
void SaveToHandler(size_t size, const QuicHeaderList& header_list) {
headers_handler_ = QuicMakeUnique<TestHeadersHandler>();
headers_handler_->OnHeaderBlockStart();
for (const auto& p : header_list) {
headers_handler_->OnHeader(p.first, p.second);
}
headers_handler_->OnHeaderBlockEnd(size, size);
}
void WriteAndExpectRequestHeaders(QuicStreamId stream_id,
bool fin,
SpdyPriority priority) {
WriteHeadersAndCheckData(stream_id, fin, priority, true /*is_request*/);
}
void WriteAndExpectResponseHeaders(QuicStreamId stream_id, bool fin) {
WriteHeadersAndCheckData(stream_id, fin, 0, false /*is_request*/);
}
void WriteHeadersAndCheckData(QuicStreamId stream_id,
bool fin,
SpdyPriority priority,
bool is_request) {
// Write the headers and capture the outgoing data
EXPECT_CALL(session_, WritevData(headers_stream_,
QuicUtils::GetHeadersStreamId(
connection_->transport_version()),
_, _, NO_FIN))
.WillOnce(WithArgs<2>(Invoke(this, &QuicHeadersStreamTest::SaveIov)));
QuicSpdySessionPeer::WriteHeadersImpl(
&session_, stream_id, headers_.Clone(), fin,
Spdy3PriorityToHttp2Weight(priority), 0, false, nullptr);
// Parse the outgoing data and check that it matches was was written.
if (is_request) {
EXPECT_CALL(visitor_,
OnHeaders(stream_id, kHasPriority,
Spdy3PriorityToHttp2Weight(priority),
/*parent_stream_id=*/0,
/*exclusive=*/false, fin, kFrameComplete));
} else {
EXPECT_CALL(visitor_,
OnHeaders(stream_id, !kHasPriority,
/*priority=*/0,
/*parent_stream_id=*/0,
/*exclusive=*/false, fin, kFrameComplete));
}
headers_handler_ = QuicMakeUnique<TestHeadersHandler>();
EXPECT_CALL(visitor_, OnHeaderFrameStart(stream_id))
.WillOnce(Return(headers_handler_.get()));
EXPECT_CALL(visitor_, OnHeaderFrameEnd(stream_id)).Times(1);
if (fin) {
EXPECT_CALL(visitor_, OnStreamEnd(stream_id));
}
deframer_->ProcessInput(saved_data_.data(), saved_data_.length());
EXPECT_FALSE(deframer_->HasError())
<< http2::Http2DecoderAdapter::SpdyFramerErrorToString(
deframer_->spdy_framer_error());
CheckHeaders();
saved_data_.clear();
}
void CheckHeaders() {
EXPECT_EQ(headers_, headers_handler_->decoded_block());
headers_handler_.reset();
}
Perspective perspective() const { return GetParam().perspective; }
QuicTransportVersion transport_version() const {
return GetParam().version.transport_version;
}
ParsedQuicVersionVector GetVersion() {
ParsedQuicVersionVector versions;
versions.push_back(GetParam().version);
return versions;
}
void TearDownLocalConnectionState() {
QuicConnectionPeer::TearDownLocalConnectionState(connection_);
}
QuicStreamId NextPromisedStreamId() {
return next_promised_stream_id_ += next_stream_id_;
}
static const bool kFrameComplete = true;
static const bool kHasPriority = true;
MockQuicConnectionHelper helper_;
MockAlarmFactory alarm_factory_;
StrictMock<MockQuicConnection>* connection_;
StrictMock<MockQuicSpdySession> session_;
QuicHeadersStream* headers_stream_;
SpdyHeaderBlock headers_;
std::unique_ptr<TestHeadersHandler> headers_handler_;
QuicString body_;
QuicString saved_data_;
QuicString saved_header_data_;
QuicString saved_payloads_;
std::unique_ptr<SpdyFramer> framer_;
std::unique_ptr<http2::Http2DecoderAdapter> deframer_;
StrictMock<MockVisitor> visitor_;
QuicStreamFrame stream_frame_;
QuicStreamId next_promised_stream_id_;
QuicStreamId client_id_1_;
QuicStreamId client_id_2_;
QuicStreamId client_id_3_;
QuicStreamId next_stream_id_;
};
// Run all tests with each version and perspective (client or server).
INSTANTIATE_TEST_CASE_P(Tests,
QuicHeadersStreamTest,
::testing::ValuesIn(GetTestParams()));
TEST_P(QuicHeadersStreamTest, StreamId) {
EXPECT_EQ(QuicUtils::GetHeadersStreamId(connection_->transport_version()),
headers_stream_->id());
}
TEST_P(QuicHeadersStreamTest, WriteHeaders) {
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
for (bool fin : {false, true}) {
if (perspective() == Perspective::IS_SERVER) {
WriteAndExpectResponseHeaders(stream_id, fin);
} else {
for (SpdyPriority priority = 0; priority < 7; ++priority) {
// TODO(rch): implement priorities correctly.
WriteAndExpectRequestHeaders(stream_id, fin, 0);
}
}
}
}
}
TEST_P(QuicHeadersStreamTest, WritePushPromises) {
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
QuicStreamId promised_stream_id = NextPromisedStreamId();
if (perspective() == Perspective::IS_SERVER) {
// Write the headers and capture the outgoing data
EXPECT_CALL(session_, WritevData(headers_stream_,
QuicUtils::GetHeadersStreamId(
connection_->transport_version()),
_, _, NO_FIN))
.WillOnce(WithArgs<2>(Invoke(this, &QuicHeadersStreamTest::SaveIov)));
session_.WritePushPromise(stream_id, promised_stream_id,
headers_.Clone());
// Parse the outgoing data and check that it matches was was written.
EXPECT_CALL(visitor_,
OnPushPromise(stream_id, promised_stream_id, kFrameComplete));
headers_handler_ = QuicMakeUnique<TestHeadersHandler>();
EXPECT_CALL(visitor_, OnHeaderFrameStart(stream_id))
.WillOnce(Return(headers_handler_.get()));
EXPECT_CALL(visitor_, OnHeaderFrameEnd(stream_id)).Times(1);
deframer_->ProcessInput(saved_data_.data(), saved_data_.length());
EXPECT_FALSE(deframer_->HasError())
<< http2::Http2DecoderAdapter::SpdyFramerErrorToString(
deframer_->spdy_framer_error());
CheckHeaders();
saved_data_.clear();
} else {
EXPECT_QUIC_BUG(session_.WritePushPromise(stream_id, promised_stream_id,
headers_.Clone()),
"Client shouldn't send PUSH_PROMISE");
}
}
}
TEST_P(QuicHeadersStreamTest, ProcessRawData) {
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
for (bool fin : {false, true}) {
for (SpdyPriority priority = 0; priority < 7; ++priority) {
// Replace with "WriteHeadersAndSaveData"
SpdySerializedFrame frame;
if (perspective() == Perspective::IS_SERVER) {
SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
headers_frame.set_fin(fin);
headers_frame.set_has_priority(true);
headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0));
frame = framer_->SerializeFrame(headers_frame);
EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0));
} else {
SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
headers_frame.set_fin(fin);
frame = framer_->SerializeFrame(headers_frame);
}
EXPECT_CALL(session_,
OnStreamHeaderList(stream_id, fin, frame.size(), _))
.WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList));
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
headers_stream_->OnStreamFrame(stream_frame_);
stream_frame_.offset += frame.size();
CheckHeaders();
}
}
}
}
TEST_P(QuicHeadersStreamTest, ProcessPushPromise) {
if (perspective() == Perspective::IS_SERVER) {
return;
}
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
QuicStreamId promised_stream_id = NextPromisedStreamId();
SpdyPushPromiseIR push_promise(stream_id, promised_stream_id,
headers_.Clone());
SpdySerializedFrame frame(framer_->SerializeFrame(push_promise));
if (perspective() == Perspective::IS_SERVER) {
EXPECT_CALL(*connection_,
CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
"PUSH_PROMISE not supported.", _))
.WillRepeatedly(InvokeWithoutArgs(
this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
} else {
EXPECT_CALL(session_, OnPromiseHeaderList(stream_id, promised_stream_id,
frame.size(), _))
.WillOnce(
Invoke(this, &QuicHeadersStreamTest::SavePromiseHeaderList));
}
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
headers_stream_->OnStreamFrame(stream_frame_);
if (perspective() == Perspective::IS_CLIENT) {
stream_frame_.offset += frame.size();
CheckHeaders();
}
}
}
TEST_P(QuicHeadersStreamTest, ProcessPriorityFrame) {
QuicStreamId parent_stream_id = 0;
for (SpdyPriority priority = 0; priority < 7; ++priority) {
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
int weight = Spdy3PriorityToHttp2Weight(priority);
SpdyPriorityIR priority_frame(stream_id, parent_stream_id, weight, true);
SpdySerializedFrame frame(framer_->SerializeFrame(priority_frame));
parent_stream_id = stream_id;
if (transport_version() <= QUIC_VERSION_39) {
EXPECT_CALL(*connection_,
CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
"SPDY PRIORITY frame received.", _))
.WillRepeatedly(InvokeWithoutArgs(
this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
} else if (perspective() == Perspective::IS_CLIENT) {
EXPECT_CALL(*connection_,
CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
"Server must not send PRIORITY frames.", _))
.WillRepeatedly(InvokeWithoutArgs(
this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
} else {
EXPECT_CALL(session_, OnPriorityFrame(stream_id, priority)).Times(1);
}
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
headers_stream_->OnStreamFrame(stream_frame_);
stream_frame_.offset += frame.size();
}
}
}
TEST_P(QuicHeadersStreamTest, ProcessPushPromiseDisabledSetting) {
session_.OnConfigNegotiated();
SpdySettingsIR data;
// Respect supported settings frames SETTINGS_ENABLE_PUSH.
data.AddSetting(SETTINGS_ENABLE_PUSH, 0);
SpdySerializedFrame frame(framer_->SerializeFrame(data));
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
if (perspective() == Perspective::IS_CLIENT) {
EXPECT_CALL(
*connection_,
CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
"Unsupported field of HTTP/2 SETTINGS frame: 2", _));
}
headers_stream_->OnStreamFrame(stream_frame_);
EXPECT_EQ(session_.server_push_enabled(),
perspective() == Perspective::IS_CLIENT);
}
TEST_P(QuicHeadersStreamTest, ProcessLargeRawData) {
QuicSpdySessionPeer::SetMaxUncompressedHeaderBytes(&session_, 256 * 1024);
// We want to create a frame that is more than the SPDY Framer's max control
// frame size, which is 16K, but less than the HPACK decoders max decode
// buffer size, which is 32K.
headers_["key0"] = QuicString(1 << 13, '.');
headers_["key1"] = QuicString(1 << 13, '.');
headers_["key2"] = QuicString(1 << 13, '.');
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
for (bool fin : {false, true}) {
for (SpdyPriority priority = 0; priority < 7; ++priority) {
// Replace with "WriteHeadersAndSaveData"
SpdySerializedFrame frame;
if (perspective() == Perspective::IS_SERVER) {
SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
headers_frame.set_fin(fin);
headers_frame.set_has_priority(true);
headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0));
frame = framer_->SerializeFrame(headers_frame);
EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0));
} else {
SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
headers_frame.set_fin(fin);
frame = framer_->SerializeFrame(headers_frame);
}
EXPECT_CALL(session_,
OnStreamHeaderList(stream_id, fin, frame.size(), _))
.WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList));
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
headers_stream_->OnStreamFrame(stream_frame_);
stream_frame_.offset += frame.size();
CheckHeaders();
}
}
}
}
TEST_P(QuicHeadersStreamTest, ProcessBadData) {
const char kBadData[] = "blah blah blah";
EXPECT_CALL(*connection_,
CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _))
.Times(::testing::AnyNumber());
stream_frame_.data_buffer = kBadData;
stream_frame_.data_length = strlen(kBadData);
headers_stream_->OnStreamFrame(stream_frame_);
}
TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrame) {
SpdyDataIR data(/* stream_id = */ 2, "ping");
SpdySerializedFrame frame(framer_->SerializeFrame(data));
EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
"SPDY DATA frame received.", _))
.WillOnce(InvokeWithoutArgs(
this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
headers_stream_->OnStreamFrame(stream_frame_);
}
TEST_P(QuicHeadersStreamTest, ProcessSpdyRstStreamFrame) {
SpdyRstStreamIR data(/* stream_id = */ 2, ERROR_CODE_PROTOCOL_ERROR);
SpdySerializedFrame frame(framer_->SerializeFrame(data));
EXPECT_CALL(*connection_,
CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
"SPDY RST_STREAM frame received.", _))
.WillOnce(InvokeWithoutArgs(
this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
headers_stream_->OnStreamFrame(stream_frame_);
}
TEST_P(QuicHeadersStreamTest, RespectHttp2SettingsFrameSupportedFields) {
const uint32_t kTestHeaderTableSize = 1000;
SpdySettingsIR data;
// Respect supported settings frames SETTINGS_HEADER_TABLE_SIZE,
// SETTINGS_MAX_HEADER_LIST_SIZE.
data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, kTestHeaderTableSize);
data.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, 2000);
SpdySerializedFrame frame(framer_->SerializeFrame(data));
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
headers_stream_->OnStreamFrame(stream_frame_);
EXPECT_EQ(kTestHeaderTableSize, QuicSpdySessionPeer::GetSpdyFramer(&session_)
.header_encoder_table_size());
}
TEST_P(QuicHeadersStreamTest, RespectHttp2SettingsFrameUnsupportedFields) {
SpdySettingsIR data;
// Does not support SETTINGS_MAX_CONCURRENT_STREAMS,
// SETTINGS_INITIAL_WINDOW_SIZE, SETTINGS_ENABLE_PUSH and
// SETTINGS_MAX_FRAME_SIZE.
data.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 100);
data.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 100);
data.AddSetting(SETTINGS_ENABLE_PUSH, 1);
data.AddSetting(SETTINGS_MAX_FRAME_SIZE, 1250);
SpdySerializedFrame frame(framer_->SerializeFrame(data));
EXPECT_CALL(
*connection_,
CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ",
SETTINGS_MAX_CONCURRENT_STREAMS),
_));
EXPECT_CALL(
*connection_,
CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ",
SETTINGS_INITIAL_WINDOW_SIZE),
_));
if (session_.perspective() == Perspective::IS_CLIENT) {
EXPECT_CALL(*connection_,
CloseConnection(
QUIC_INVALID_HEADERS_STREAM_DATA,
QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ",
SETTINGS_ENABLE_PUSH),
_));
}
EXPECT_CALL(
*connection_,
CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ",
SETTINGS_MAX_FRAME_SIZE),
_));
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
headers_stream_->OnStreamFrame(stream_frame_);
}
TEST_P(QuicHeadersStreamTest, ProcessSpdyPingFrame) {
SpdyPingIR data(1);
SpdySerializedFrame frame(framer_->SerializeFrame(data));
EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
"SPDY PING frame received.", _))
.WillOnce(InvokeWithoutArgs(
this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
headers_stream_->OnStreamFrame(stream_frame_);
}
TEST_P(QuicHeadersStreamTest, ProcessSpdyGoAwayFrame) {
SpdyGoAwayIR data(/* last_good_stream_id = */ 1, ERROR_CODE_PROTOCOL_ERROR,
"go away");
SpdySerializedFrame frame(framer_->SerializeFrame(data));
EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
"SPDY GOAWAY frame received.", _))
.WillOnce(InvokeWithoutArgs(
this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
headers_stream_->OnStreamFrame(stream_frame_);
}
TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) {
SpdyWindowUpdateIR data(/* stream_id = */ 1, /* delta = */ 1);
SpdySerializedFrame frame(framer_->SerializeFrame(data));
EXPECT_CALL(*connection_,
CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
"SPDY WINDOW_UPDATE frame received.", _))
.WillOnce(InvokeWithoutArgs(
this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
headers_stream_->OnStreamFrame(stream_frame_);
}
TEST_P(QuicHeadersStreamTest, NoConnectionLevelFlowControl) {
EXPECT_FALSE(QuicStreamPeer::StreamContributesToConnectionFlowControl(
headers_stream_));
}
TEST_P(QuicHeadersStreamTest, HpackDecoderDebugVisitor) {
auto hpack_decoder_visitor =
QuicMakeUnique<StrictMock<MockQuicHpackDebugVisitor>>();
{
InSequence seq;
// Number of indexed representations generated in headers below.
for (int i = 1; i < 28; i++) {
EXPECT_CALL(*hpack_decoder_visitor,
OnUseEntry(QuicTime::Delta::FromMilliseconds(i)))
.Times(4);
}
}
QuicSpdySessionPeer::SetHpackDecoderDebugVisitor(
&session_, std::move(hpack_decoder_visitor));
// Create some headers we expect to generate entries in HPACK's
// dynamic table, in addition to content-length.
headers_["key0"] = QuicString(1 << 1, '.');
headers_["key1"] = QuicString(1 << 2, '.');
headers_["key2"] = QuicString(1 << 3, '.');
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
for (bool fin : {false, true}) {
for (SpdyPriority priority = 0; priority < 7; ++priority) {
// Replace with "WriteHeadersAndSaveData"
SpdySerializedFrame frame;
if (perspective() == Perspective::IS_SERVER) {
SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
headers_frame.set_fin(fin);
headers_frame.set_has_priority(true);
headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0));
frame = framer_->SerializeFrame(headers_frame);
EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0));
} else {
SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
headers_frame.set_fin(fin);
frame = framer_->SerializeFrame(headers_frame);
}
EXPECT_CALL(session_,
OnStreamHeaderList(stream_id, fin, frame.size(), _))
.WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList));
stream_frame_.data_buffer = frame.data();
stream_frame_.data_length = frame.size();
connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
headers_stream_->OnStreamFrame(stream_frame_);
stream_frame_.offset += frame.size();
CheckHeaders();
}
}
}
}
TEST_P(QuicHeadersStreamTest, HpackEncoderDebugVisitor) {
auto hpack_encoder_visitor =
QuicMakeUnique<StrictMock<MockQuicHpackDebugVisitor>>();
if (perspective() == Perspective::IS_SERVER) {
InSequence seq;
for (int i = 1; i < 4; i++) {
EXPECT_CALL(*hpack_encoder_visitor,
OnUseEntry(QuicTime::Delta::FromMilliseconds(i)));
}
} else {
InSequence seq;
for (int i = 1; i < 28; i++) {
EXPECT_CALL(*hpack_encoder_visitor,
OnUseEntry(QuicTime::Delta::FromMilliseconds(i)));
}
}
QuicSpdySessionPeer::SetHpackEncoderDebugVisitor(
&session_, std::move(hpack_encoder_visitor));
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
for (bool fin : {false, true}) {
if (perspective() == Perspective::IS_SERVER) {
WriteAndExpectResponseHeaders(stream_id, fin);
connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
} else {
for (SpdyPriority priority = 0; priority < 7; ++priority) {
// TODO(rch): implement priorities correctly.
WriteAndExpectRequestHeaders(stream_id, fin, 0);
connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
}
}
}
}
}
TEST_P(QuicHeadersStreamTest, AckSentData) {
EXPECT_CALL(session_, WritevData(headers_stream_,
QuicUtils::GetHeadersStreamId(
connection_->transport_version()),
_, _, NO_FIN))
.WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
InSequence s;
QuicReferenceCountedPointer<MockAckListener> ack_listener1(
new MockAckListener());
QuicReferenceCountedPointer<MockAckListener> ack_listener2(
new MockAckListener());
QuicReferenceCountedPointer<MockAckListener> ack_listener3(
new MockAckListener());
// Packet 1.
headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
// Packet 2.
headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
// Packet 3.
headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
// Packet 2 gets retransmitted.
EXPECT_CALL(*ack_listener3, OnPacketRetransmitted(7)).Times(1);
EXPECT_CALL(*ack_listener2, OnPacketRetransmitted(7)).Times(1);
headers_stream_->OnStreamFrameRetransmitted(21, 7, false);
headers_stream_->OnStreamFrameRetransmitted(28, 7, false);
// Packets are acked in order: 2, 3, 1.
EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(21, 7, false,
QuicTime::Delta::Zero()));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(28, 7, false,
QuicTime::Delta::Zero()));
EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(35, 7, false,
QuicTime::Delta::Zero()));
EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _));
EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(0, 7, false,
QuicTime::Delta::Zero()));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(7, 7, false,
QuicTime::Delta::Zero()));
// Unsent data is acked.
EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(14, 10, false,
QuicTime::Delta::Zero()));
}
TEST_P(QuicHeadersStreamTest, FrameContainsMultipleHeaders) {
// In this test, a stream frame can contain multiple headers.
EXPECT_CALL(session_, WritevData(headers_stream_,
QuicUtils::GetHeadersStreamId(
connection_->transport_version()),
_, _, NO_FIN))
.WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
InSequence s;
QuicReferenceCountedPointer<MockAckListener> ack_listener1(
new MockAckListener());
QuicReferenceCountedPointer<MockAckListener> ack_listener2(
new MockAckListener());
QuicReferenceCountedPointer<MockAckListener> ack_listener3(
new MockAckListener());
headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
// Frame 1 is retransmitted.
EXPECT_CALL(*ack_listener1, OnPacketRetransmitted(14));
EXPECT_CALL(*ack_listener2, OnPacketRetransmitted(3));
headers_stream_->OnStreamFrameRetransmitted(0, 17, false);
// Frames are acked in order: 2, 3, 1.
EXPECT_CALL(*ack_listener2, OnPacketAcked(4, _));
EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
EXPECT_CALL(*ack_listener2, OnPacketAcked(2, _));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(17, 13, false,
QuicTime::Delta::Zero()));
EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _));
EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(30, 12, false,
QuicTime::Delta::Zero()));
EXPECT_CALL(*ack_listener1, OnPacketAcked(14, _));
EXPECT_CALL(*ack_listener2, OnPacketAcked(3, _));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(0, 17, false,
QuicTime::Delta::Zero()));
}
TEST_P(QuicHeadersStreamTest, HeadersGetAckedMultipleTimes) {
EXPECT_CALL(session_, WritevData(headers_stream_,
QuicUtils::GetHeadersStreamId(
connection_->transport_version()),
_, _, NO_FIN))
.WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
InSequence s;
QuicReferenceCountedPointer<MockAckListener> ack_listener1(
new MockAckListener());
QuicReferenceCountedPointer<MockAckListener> ack_listener2(
new MockAckListener());
QuicReferenceCountedPointer<MockAckListener> ack_listener3(
new MockAckListener());
// Send [0, 42).
headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
// Ack [15, 20), [5, 25), [10, 17), [0, 12) and [22, 42).
EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(15, 5, false,
QuicTime::Delta::Zero()));
EXPECT_CALL(*ack_listener1, OnPacketAcked(9, _));
EXPECT_CALL(*ack_listener2, OnPacketAcked(1, _));
EXPECT_CALL(*ack_listener2, OnPacketAcked(1, _));
EXPECT_CALL(*ack_listener3, OnPacketAcked(4, _));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(5, 20, false,
QuicTime::Delta::Zero()));
// Duplicate ack.
EXPECT_FALSE(headers_stream_->OnStreamFrameAcked(10, 7, false,
QuicTime::Delta::Zero()));
EXPECT_CALL(*ack_listener1, OnPacketAcked(5, _));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(0, 12, false,
QuicTime::Delta::Zero()));
EXPECT_CALL(*ack_listener3, OnPacketAcked(3, _));
EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(22, 20, false,
QuicTime::Delta::Zero()));
}
} // namespace
} // namespace test
} // namespace quic