This source file includes following definitions.
- DecompressFrame
- finished_
- ResetBuffer
- OnError
- OnDataFrameHeader
- OnStreamFrameData
- OnControlFrameHeaderData
- OnSynStream
- OnSynReply
- OnRstStream
- OnSetting
- OnPing
- OnSettingsEnd
- OnGoAway
- OnHeaders
- OnWindowUpdate
- OnPushPromise
- OnContinuation
- ReleaseBuffer
- OnWindowUpdate
- header_buffer_valid_
- OnError
- OnDataFrameHeader
- OnStreamFrameData
- OnControlFrameHeaderData
- OnSynStream
- OnSynReply
- OnRstStream
- OnRstStreamFrameData
- OnSetting
- OnSettingsAck
- OnSettingsEnd
- OnPing
- OnGoAway
- OnHeaders
- OnWindowUpdate
- OnPushPromise
- OnContinuation
- OnSendCompressedFrame
- OnReceiveCompressedFrame
- SimulateInFramer
- InitHeaderStreaming
- set_header_buffer_size
- header_data_chunk_max_size
- GetSerializedHeaders
- SetUp
- CompareFrame
- CompareFrames
- CompareHeaderBlocks
- IsSpdy2
- IsSpdy3
- IsSpdy4
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
- TEST_P
#include <algorithm>
#include <iostream>
#include <limits>
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "net/spdy/hpack_output_stream.h"
#include "net/spdy/mock_spdy_framer_visitor.h"
#include "net/spdy/spdy_frame_builder.h"
#include "net/spdy/spdy_framer.h"
#include "net/spdy/spdy_protocol.h"
#include "net/spdy/spdy_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/platform_test.h"
using base::StringPiece;
using std::string;
using std::max;
using std::min;
using std::numeric_limits;
using testing::ElementsAre;
using testing::Pair;
using testing::_;
namespace net {
namespace test {
static const size_t kMaxDecompressedSize = 1024;
class MockDebugVisitor : public SpdyFramerDebugVisitorInterface {
public:
MOCK_METHOD4(OnSendCompressedFrame, void(SpdyStreamId stream_id,
SpdyFrameType type,
size_t payload_len,
size_t frame_len));
MOCK_METHOD3(OnReceiveCompressedFrame, void(SpdyStreamId stream_id,
SpdyFrameType type,
size_t frame_len));
};
class SpdyFramerTestUtil {
public:
template<class SpdyFrameType> static SpdyFrame* DecompressFrame(
SpdyFramer* framer, const SpdyFrameType& frame) {
DecompressionVisitor visitor(framer->protocol_version());
framer->set_visitor(&visitor);
CHECK_EQ(frame.size(), framer->ProcessInput(frame.data(), frame.size()));
CHECK_EQ(SpdyFramer::SPDY_RESET, framer->state());
framer->set_visitor(NULL);
char* buffer = visitor.ReleaseBuffer();
CHECK(buffer != NULL);
SpdyFrame* decompressed_frame = new SpdyFrame(buffer, visitor.size(), true);
SetFrameLength(decompressed_frame,
visitor.size() - framer->GetControlFrameHeaderSize(),
framer->protocol_version());
return decompressed_frame;
}
class DecompressionVisitor : public SpdyFramerVisitorInterface {
public:
explicit DecompressionVisitor(SpdyMajorVersion version)
: version_(version), size_(0), finished_(false) {}
void ResetBuffer() {
CHECK(buffer_.get() == NULL);
CHECK_EQ(0u, size_);
CHECK(!finished_);
buffer_.reset(new char[kMaxDecompressedSize]);
}
virtual void OnError(SpdyFramer* framer) OVERRIDE { LOG(FATAL); }
virtual void OnDataFrameHeader(SpdyStreamId stream_id,
size_t length,
bool fin) OVERRIDE {
LOG(FATAL) << "Unexpected data frame header";
}
virtual void OnStreamFrameData(SpdyStreamId stream_id,
const char* data,
size_t len,
bool fin) OVERRIDE {
LOG(FATAL);
}
virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id,
const char* header_data,
size_t len) OVERRIDE {
CHECK(buffer_.get() != NULL);
CHECK_GE(kMaxDecompressedSize, size_ + len);
CHECK(!finished_);
if (len != 0) {
memcpy(buffer_.get() + size_, header_data, len);
size_ += len;
} else {
finished_ = true;
}
return true;
}
virtual void OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
bool fin,
bool unidirectional) OVERRIDE {
SpdyFramer framer(version_);
framer.set_enable_compression(false);
SpdySynStreamIR syn_stream(stream_id);
syn_stream.set_associated_to_stream_id(associated_stream_id);
syn_stream.set_priority(priority);
syn_stream.set_fin(fin);
syn_stream.set_unidirectional(unidirectional);
scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
ResetBuffer();
memcpy(buffer_.get(), frame->data(), framer.GetSynStreamMinimumSize());
size_ += framer.GetSynStreamMinimumSize();
}
virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {
SpdyFramer framer(version_);
framer.set_enable_compression(false);
SpdyHeadersIR headers(stream_id);
headers.set_fin(fin);
scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers));
ResetBuffer();
memcpy(buffer_.get(), frame->data(), framer.GetHeadersMinimumSize());
size_ += framer.GetSynStreamMinimumSize();
}
virtual void OnRstStream(SpdyStreamId stream_id,
SpdyRstStreamStatus status) OVERRIDE {
LOG(FATAL);
}
virtual void OnSetting(SpdySettingsIds id,
uint8 flags,
uint32 value) OVERRIDE {
LOG(FATAL);
}
virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {
LOG(FATAL);
}
virtual void OnSettingsEnd() OVERRIDE { LOG(FATAL); }
virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
SpdyGoAwayStatus status) OVERRIDE {
LOG(FATAL);
}
virtual void OnHeaders(SpdyStreamId stream_id,
bool fin,
bool end) OVERRIDE {
SpdyFramer framer(version_);
framer.set_enable_compression(false);
SpdyHeadersIR headers(stream_id);
headers.set_fin(fin);
scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers));
ResetBuffer();
memcpy(buffer_.get(), frame->data(), framer.GetHeadersMinimumSize());
size_ += framer.GetHeadersMinimumSize();
}
virtual void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) {
LOG(FATAL);
}
virtual void OnPushPromise(SpdyStreamId stream_id,
SpdyStreamId promised_stream_id,
bool end) OVERRIDE {
SpdyFramer framer(version_);
framer.set_enable_compression(false);
SpdyPushPromiseIR push_promise(stream_id, promised_stream_id);
scoped_ptr<SpdyFrame> frame(framer.SerializePushPromise(push_promise));
ResetBuffer();
memcpy(buffer_.get(), frame->data(), framer.GetPushPromiseMinimumSize());
size_ += framer.GetPushPromiseMinimumSize();
}
virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE {
LOG(FATAL);
}
char* ReleaseBuffer() {
CHECK(finished_);
return buffer_.release();
}
virtual void OnWindowUpdate(SpdyStreamId stream_id,
uint32 delta_window_size) OVERRIDE {
LOG(FATAL);
}
size_t size() const {
CHECK(finished_);
return size_;
}
private:
SpdyMajorVersion version_;
scoped_ptr<char[]> buffer_;
size_t size_;
bool finished_;
DISALLOW_COPY_AND_ASSIGN(DecompressionVisitor);
};
private:
DISALLOW_COPY_AND_ASSIGN(SpdyFramerTestUtil);
};
class TestSpdyVisitor : public SpdyFramerVisitorInterface,
public SpdyFramerDebugVisitorInterface {
public:
static const size_t kDefaultHeaderBufferSize = 16 * 1024 * 1024;
explicit TestSpdyVisitor(SpdyMajorVersion version)
: framer_(version),
use_compression_(false),
error_count_(0),
syn_frame_count_(0),
syn_reply_frame_count_(0),
headers_frame_count_(0),
goaway_count_(0),
setting_count_(0),
settings_ack_sent_(0),
settings_ack_received_(0),
continuation_count_(0),
last_window_update_stream_(0),
last_window_update_delta_(0),
last_push_promise_stream_(0),
last_push_promise_promised_stream_(0),
data_bytes_(0),
fin_frame_count_(0),
fin_opaque_data_(),
fin_flag_count_(0),
zero_length_data_frame_count_(0),
control_frame_header_data_count_(0),
zero_length_control_frame_header_data_count_(0),
data_frame_count_(0),
last_payload_len_(0),
last_frame_len_(0),
header_buffer_(new char[kDefaultHeaderBufferSize]),
header_buffer_length_(0),
header_buffer_size_(kDefaultHeaderBufferSize),
header_stream_id_(-1),
header_control_type_(DATA),
header_buffer_valid_(false) {
}
virtual void OnError(SpdyFramer* f) OVERRIDE {
LOG(INFO) << "SpdyFramer Error: "
<< SpdyFramer::ErrorCodeToString(f->error_code());
error_count_++;
}
virtual void OnDataFrameHeader(SpdyStreamId stream_id,
size_t length,
bool fin) OVERRIDE {
data_frame_count_++;
header_stream_id_ = stream_id;
}
virtual void OnStreamFrameData(SpdyStreamId stream_id,
const char* data,
size_t len,
bool fin) OVERRIDE {
EXPECT_EQ(header_stream_id_, stream_id);
if (len == 0)
++zero_length_data_frame_count_;
data_bytes_ += len;
std::cerr << "OnStreamFrameData(" << stream_id << ", \"";
if (len > 0) {
for (size_t i = 0 ; i < len; ++i) {
std::cerr << std::hex << (0xFF & (unsigned int)data[i]) << std::dec;
}
}
std::cerr << "\", " << len << ")\n";
}
virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id,
const char* header_data,
size_t len) OVERRIDE {
++control_frame_header_data_count_;
CHECK_EQ(header_stream_id_, stream_id);
if (len == 0) {
++zero_length_control_frame_header_data_count_;
headers_.clear();
CHECK(header_buffer_valid_);
size_t parsed_length = framer_.ParseHeaderBlockInBuffer(
header_buffer_.get(), header_buffer_length_, &headers_);
DCHECK_EQ(header_buffer_length_, parsed_length);
return true;
}
const size_t available = header_buffer_size_ - header_buffer_length_;
if (len > available) {
header_buffer_valid_ = false;
return false;
}
memcpy(header_buffer_.get() + header_buffer_length_, header_data, len);
header_buffer_length_ += len;
return true;
}
virtual void OnSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
SpdyPriority priority,
bool fin,
bool unidirectional) OVERRIDE {
syn_frame_count_++;
InitHeaderStreaming(SYN_STREAM, stream_id);
if (fin) {
fin_flag_count_++;
}
}
virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {
syn_reply_frame_count_++;
InitHeaderStreaming(SYN_REPLY, stream_id);
if (fin) {
fin_flag_count_++;
}
}
virtual void OnRstStream(SpdyStreamId stream_id,
SpdyRstStreamStatus status) OVERRIDE {
fin_frame_count_++;
}
virtual bool OnRstStreamFrameData(const char* rst_stream_data,
size_t len) OVERRIDE {
if ((rst_stream_data != NULL) && (len > 0)) {
fin_opaque_data_ += std::string(rst_stream_data, len);
}
return true;
}
virtual void OnSetting(SpdySettingsIds id,
uint8 flags,
uint32 value) OVERRIDE {
setting_count_++;
}
virtual void OnSettingsAck() OVERRIDE {
DCHECK_GE(4, framer_.protocol_version());
settings_ack_received_++;
}
virtual void OnSettingsEnd() OVERRIDE {
if (framer_.protocol_version() < 4) { return; }
settings_ack_sent_++;
}
virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {
DLOG(FATAL);
}
virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
SpdyGoAwayStatus status) OVERRIDE {
goaway_count_++;
}
virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) OVERRIDE {
headers_frame_count_++;
InitHeaderStreaming(HEADERS, stream_id);
if (fin) {
fin_flag_count_++;
}
}
virtual void OnWindowUpdate(SpdyStreamId stream_id,
uint32 delta_window_size) OVERRIDE {
last_window_update_stream_ = stream_id;
last_window_update_delta_ = delta_window_size;
}
virtual void OnPushPromise(SpdyStreamId stream_id,
SpdyStreamId promised_stream_id,
bool end) OVERRIDE {
InitHeaderStreaming(PUSH_PROMISE, stream_id);
last_push_promise_stream_ = stream_id;
last_push_promise_promised_stream_ = promised_stream_id;
}
virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE {
continuation_count_++;
}
virtual void OnSendCompressedFrame(SpdyStreamId stream_id,
SpdyFrameType type,
size_t payload_len,
size_t frame_len) OVERRIDE {
last_payload_len_ = payload_len;
last_frame_len_ = frame_len;
}
virtual void OnReceiveCompressedFrame(SpdyStreamId stream_id,
SpdyFrameType type,
size_t frame_len) OVERRIDE {
last_frame_len_ = frame_len;
}
void SimulateInFramer(const unsigned char* input, size_t size) {
framer_.set_enable_compression(use_compression_);
framer_.set_visitor(this);
size_t input_remaining = size;
const char* input_ptr = reinterpret_cast<const char*>(input);
while (input_remaining > 0 &&
framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) {
const size_t kMaxReadSize = 32;
size_t bytes_read =
(rand() % min(input_remaining, kMaxReadSize)) + 1;
size_t bytes_processed = framer_.ProcessInput(input_ptr, bytes_read);
input_remaining -= bytes_processed;
input_ptr += bytes_processed;
}
}
void InitHeaderStreaming(SpdyFrameType header_control_type,
SpdyStreamId stream_id) {
DCHECK_GE(header_control_type, FIRST_CONTROL_TYPE);
DCHECK_LE(header_control_type, LAST_CONTROL_TYPE);
memset(header_buffer_.get(), 0, header_buffer_size_);
header_buffer_length_ = 0;
header_stream_id_ = stream_id;
header_control_type_ = header_control_type;
header_buffer_valid_ = true;
DCHECK_NE(header_stream_id_, SpdyFramer::kInvalidStream);
}
void set_header_buffer_size(size_t header_buffer_size) {
header_buffer_size_ = header_buffer_size;
header_buffer_.reset(new char[header_buffer_size]);
}
static size_t header_data_chunk_max_size() {
return SpdyFramer::kHeaderDataChunkMaxSize;
}
SpdyFramer framer_;
bool use_compression_;
int error_count_;
int syn_frame_count_;
int syn_reply_frame_count_;
int headers_frame_count_;
int goaway_count_;
int setting_count_;
int settings_ack_sent_;
int settings_ack_received_;
int continuation_count_;
SpdyStreamId last_window_update_stream_;
uint32 last_window_update_delta_;
SpdyStreamId last_push_promise_stream_;
SpdyStreamId last_push_promise_promised_stream_;
int data_bytes_;
int fin_frame_count_;
std::string fin_opaque_data_;
int fin_flag_count_;
int zero_length_data_frame_count_;
int control_frame_header_data_count_;
int zero_length_control_frame_header_data_count_;
int data_frame_count_;
size_t last_payload_len_;
size_t last_frame_len_;
scoped_ptr<char[]> header_buffer_;
size_t header_buffer_length_;
size_t header_buffer_size_;
SpdyStreamId header_stream_id_;
SpdyFrameType header_control_type_;
bool header_buffer_valid_;
SpdyHeaderBlock headers_;
};
base::StringPiece GetSerializedHeaders(const SpdyFrame* frame,
const SpdyFramer& framer) {
return base::StringPiece(frame->data() + framer.GetSynStreamMinimumSize(),
frame->size() - framer.GetSynStreamMinimumSize());
}
}
}
using net::test::SetFrameLength;
using net::test::SetFrameFlags;
using net::test::CompareCharArraysWithHexError;
using net::test::SpdyFramerTestUtil;
using net::test::TestSpdyVisitor;
using net::test::GetSerializedHeaders;
namespace net {
class SpdyFramerTest : public ::testing::TestWithParam<SpdyMajorVersion> {
protected:
virtual void SetUp() {
spdy_version_ = GetParam();
spdy_version_ch_ = static_cast<unsigned char>(spdy_version_);
}
void CompareFrame(const string& description,
const SpdyFrame& actual_frame,
const unsigned char* expected,
const int expected_len) {
const unsigned char* actual =
reinterpret_cast<const unsigned char*>(actual_frame.data());
CompareCharArraysWithHexError(
description, actual, actual_frame.size(), expected, expected_len);
}
void CompareFrames(const string& description,
const SpdyFrame& expected_frame,
const SpdyFrame& actual_frame) {
CompareCharArraysWithHexError(
description,
reinterpret_cast<const unsigned char*>(expected_frame.data()),
expected_frame.size(),
reinterpret_cast<const unsigned char*>(actual_frame.data()),
actual_frame.size());
}
bool CompareHeaderBlocks(const SpdyHeaderBlock* expected,
const SpdyHeaderBlock* actual) {
if (expected->size() != actual->size()) {
LOG(ERROR) << "Expected " << expected->size() << " headers; actually got "
<< actual->size() << ".";
return false;
}
for (SpdyHeaderBlock::const_iterator it = expected->begin();
it != expected->end();
++it) {
SpdyHeaderBlock::const_iterator it2 = actual->find(it->first);
if (it2 == actual->end()) {
LOG(ERROR) << "Expected header name '" << it->first << "'.";
return false;
}
if (it->second.compare(it2->second) != 0) {
LOG(ERROR) << "Expected header named '" << it->first
<< "' to have a value of '" << it->second
<< "'. The actual value received was '" << it2->second
<< "'.";
return false;
}
}
return true;
}
bool IsSpdy2() { return spdy_version_ == SPDY2; }
bool IsSpdy3() { return spdy_version_ == SPDY3; }
bool IsSpdy4() { return spdy_version_ == SPDY4; }
SpdyMajorVersion spdy_version_;
unsigned char spdy_version_ch_;
};
INSTANTIATE_TEST_CASE_P(SpdyFramerTests,
SpdyFramerTest,
::testing::Values(SPDY2, SPDY3, SPDY4));
TEST_P(SpdyFramerTest, HeaderBlockInBuffer) {
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
SpdySynStreamIR syn_stream(1);
syn_stream.set_priority(1);
syn_stream.SetHeader("alpha", "beta");
syn_stream.SetHeader("gamma", "charlie");
syn_stream.SetHeader("cookie", "key1=value1; key2=value2");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(frame->data()),
frame->size());
EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
EXPECT_TRUE(CompareHeaderBlocks(&syn_stream.name_value_block(),
&visitor.headers_));
}
TEST_P(SpdyFramerTest, UndersizedHeaderBlockInBuffer) {
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
SpdySynStreamIR syn_stream(1);
syn_stream.set_priority(1);
syn_stream.SetHeader("alpha", "beta");
syn_stream.SetHeader("gamma", "charlie");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(frame->data()),
frame->size() - 2);
EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_);
EXPECT_EQ(0u, visitor.headers_.size());
}
TEST_P(SpdyFramerTest, SynReplyWithStreamIdZero) {
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
SpdySynReplyIR syn_reply(0);
syn_reply.SetHeader("alpha", "beta");
scoped_ptr<SpdySerializedFrame> frame(framer.SerializeSynReply(syn_reply));
ASSERT_TRUE(frame.get() != NULL);
EXPECT_CALL(visitor, OnError(testing::Eq(&framer)));
EXPECT_GT(frame->size(), framer.ProcessInput(frame->data(), frame->size()));
EXPECT_TRUE(framer.HasError());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
TEST_P(SpdyFramerTest, HeadersWithStreamIdZero) {
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
SpdyHeadersIR headers_ir(0);
headers_ir.SetHeader("alpha", "beta");
scoped_ptr<SpdySerializedFrame> frame(framer.SerializeHeaders(headers_ir));
ASSERT_TRUE(frame.get() != NULL);
EXPECT_CALL(visitor, OnError(testing::Eq(&framer)));
EXPECT_GT(frame->size(), framer.ProcessInput(frame->data(), frame->size()));
EXPECT_TRUE(framer.HasError());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
TEST_P(SpdyFramerTest, PushPromiseWithStreamIdZero) {
if (spdy_version_ < SPDY4) {
return;
}
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
SpdyPushPromiseIR push_promise(0, 4);
push_promise.SetHeader("alpha", "beta");
scoped_ptr<SpdySerializedFrame> frame(
framer.SerializePushPromise(push_promise));
ASSERT_TRUE(frame.get() != NULL);
EXPECT_CALL(visitor, OnError(testing::Eq(&framer)));
EXPECT_GT(frame->size(), framer.ProcessInput(frame->data(), frame->size()));
EXPECT_TRUE(framer.HasError());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) {
if (spdy_version_ < SPDY4) {
return;
}
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
SpdyPushPromiseIR push_promise(3, 0);
push_promise.SetHeader("alpha", "beta");
scoped_ptr<SpdySerializedFrame> frame(
framer.SerializePushPromise(push_promise));
ASSERT_TRUE(frame.get() != NULL);
EXPECT_CALL(visitor, OnError(testing::Eq(&framer)));
EXPECT_GT(frame->size(), framer.ProcessInput(frame->data(), frame->size()));
EXPECT_TRUE(framer.HasError());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
TEST_P(SpdyFramerTest, DuplicateHeader) {
if (spdy_version_ >= 4) {
return;
}
SpdyFramer framer(spdy_version_);
SpdyFrameBuilder frame(1024);
if (spdy_version_ < 4) {
frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE);
frame.WriteUInt32(3);
frame.WriteUInt32(0);
frame.WriteUInt16(0);
} else {
frame.WriteFramePrefix(framer, HEADERS, HEADERS_FLAG_PRIORITY, 3);
frame.WriteUInt32(framer.GetHighestPriority());
}
if (IsSpdy2()) {
frame.WriteUInt16(2);
frame.WriteString("name");
frame.WriteString("value1");
frame.WriteString("name");
frame.WriteString("value2");
} else {
frame.WriteUInt32(2);
frame.WriteStringPiece32("name");
frame.WriteStringPiece32("value1");
frame.WriteStringPiece32("name");
frame.WriteStringPiece32("value2");
}
frame.RewriteLength(framer);
SpdyHeaderBlock new_headers;
framer.set_enable_compression(false);
scoped_ptr<SpdyFrame> control_frame(frame.take());
base::StringPiece serialized_headers =
GetSerializedHeaders(control_frame.get(), framer);
EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(),
serialized_headers.size(),
&new_headers));
}
TEST_P(SpdyFramerTest, MultiValueHeader) {
SpdyFramer framer(spdy_version_);
SpdyFrameBuilder frame(1024);
if (spdy_version_ < 4) {
frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE);
frame.WriteUInt32(3);
frame.WriteUInt32(0);
frame.WriteUInt16(0);
} else {
frame.WriteFramePrefix(framer,
HEADERS,
HEADERS_FLAG_PRIORITY | HEADERS_FLAG_END_HEADERS,
3);
frame.WriteUInt32(framer.GetHighestPriority());
}
string value("value1\0value2", 13);
if (IsSpdy2()) {
frame.WriteUInt16(1);
frame.WriteString("name");
frame.WriteString(value);
} else if (spdy_version_ >= 4) {
HpackOutputStream output_stream(1024);
output_stream.AppendLiteralHeaderNoIndexingWithName("name", value);
string buffer;
output_stream.TakeString(&buffer);
frame.WriteBytes(&buffer[0], buffer.size());
} else {
frame.WriteUInt32(1);
frame.WriteStringPiece32("name");
frame.WriteStringPiece32(value);
}
frame.RewriteLength(framer);
framer.set_enable_compression(false);
scoped_ptr<SpdyFrame> control_frame(frame.take());
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
EXPECT_THAT(visitor.headers_, ElementsAre(
Pair("name", value)));
}
TEST_P(SpdyFramerTest, BasicCompression) {
if (spdy_version_ >= 4) {
return;
}
scoped_ptr<TestSpdyVisitor> visitor(new TestSpdyVisitor(spdy_version_));
SpdyFramer framer(spdy_version_);
framer.set_debug_visitor(visitor.get());
SpdySynStreamIR syn_stream(1);
syn_stream.set_priority(1);
syn_stream.SetHeader("server", "SpdyServer 1.0");
syn_stream.SetHeader("date", "Mon 12 Jan 2009 12:12:12 PST");
syn_stream.SetHeader("status", "200");
syn_stream.SetHeader("version", "HTTP/1.1");
syn_stream.SetHeader("content-type", "text/html");
syn_stream.SetHeader("content-length", "12");
scoped_ptr<SpdyFrame> frame1(framer.SerializeSynStream(syn_stream));
size_t uncompressed_size1 = visitor->last_payload_len_;
size_t compressed_size1 =
visitor->last_frame_len_ - framer.GetSynStreamMinimumSize();
if (IsSpdy2()) {
EXPECT_EQ(139u, uncompressed_size1);
#if defined(USE_SYSTEM_ZLIB)
EXPECT_EQ(155u, compressed_size1);
#else
EXPECT_EQ(135u, compressed_size1);
#endif
} else {
EXPECT_EQ(165u, uncompressed_size1);
#if defined(USE_SYSTEM_ZLIB)
EXPECT_EQ(181u, compressed_size1);
#else
EXPECT_EQ(117u, compressed_size1);
#endif
}
scoped_ptr<SpdyFrame> frame2(framer.SerializeSynStream(syn_stream));
size_t uncompressed_size2 = visitor->last_payload_len_;
size_t compressed_size2 =
visitor->last_frame_len_ - framer.GetSynStreamMinimumSize();
EXPECT_LE(frame2->size(), frame1->size());
scoped_ptr<SpdyFrame> frame3(SpdyFramerTestUtil::DecompressFrame(
&framer, *frame1.get()));
visitor.reset(new TestSpdyVisitor(spdy_version_));
framer.set_debug_visitor(visitor.get());
scoped_ptr<SpdyFrame> frame4(SpdyFramerTestUtil::DecompressFrame(
&framer, *frame2.get()));
size_t uncompressed_size4 =
frame4->size() - framer.GetSynStreamMinimumSize();
size_t compressed_size4 =
visitor->last_frame_len_ - framer.GetSynStreamMinimumSize();
if (IsSpdy2()) {
EXPECT_EQ(139u, uncompressed_size4);
#if defined(USE_SYSTEM_ZLIB)
EXPECT_EQ(149u, compressed_size4);
#else
EXPECT_EQ(101u, compressed_size4);
#endif
} else {
EXPECT_EQ(165u, uncompressed_size4);
#if defined(USE_SYSTEM_ZLIB)
EXPECT_EQ(175u, compressed_size4);
#else
EXPECT_EQ(102u, compressed_size4);
#endif
}
EXPECT_EQ(uncompressed_size1, uncompressed_size2);
EXPECT_EQ(uncompressed_size1, uncompressed_size4);
EXPECT_EQ(compressed_size2, compressed_size4);
CompareFrames("Uncompressed SYN_STREAM", *frame3, *frame4);
framer.set_enable_compression(false);
scoped_ptr<SpdyFrame> uncompressed_frame(
framer.SerializeSynStream(syn_stream));
CompareFrames("Uncompressed SYN_STREAM", *frame3, *uncompressed_frame);
}
TEST_P(SpdyFramerTest, CompressEmptyHeaders) {
SpdySynStreamIR syn_stream(1);
syn_stream.SetHeader("server", "SpdyServer 1.0");
syn_stream.SetHeader("date", "Mon 12 Jan 2009 12:12:12 PST");
syn_stream.SetHeader("status", "200");
syn_stream.SetHeader("version", "HTTP/1.1");
syn_stream.SetHeader("content-type", "text/html");
syn_stream.SetHeader("content-length", "12");
syn_stream.SetHeader("x-empty-header", "");
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(true);
scoped_ptr<SpdyFrame> frame1(framer.SerializeSynStream(syn_stream));
}
TEST_P(SpdyFramerTest, Basic) {
const unsigned char kV2Input[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x02, 'h', 'h',
0x00, 0x02, 'v', 'v',
0x80, spdy_version_ch_, 0x00, 0x08,
0x00, 0x00, 0x00, 0x18,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
0x00, 0x02, 'h', '2',
0x00, 0x02, 'v', '2',
0x00, 0x02, 'h', '3',
0x00, 0x02, 'v', '3',
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0c,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0c,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x04,
0xde, 0xad, 0xbe, 0xef,
0x80, spdy_version_ch_, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00,
0x80, spdy_version_ch_, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00,
};
const unsigned char kV3Input[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x1a,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x00, 0x02, 'h', 'h',
0x00, 0x00, 0x00, 0x02,
'v', 'v',
0x80, spdy_version_ch_, 0x00, 0x08,
0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x02,
'h', '2',
0x00, 0x00, 0x00, 0x02,
'v', '2', 0x00, 0x00,
0x00, 0x02, 'h', '3',
0x00, 0x00, 0x00, 0x02,
'v', '3',
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0c,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0e,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x04,
0xde, 0xad, 0xbe, 0xef,
0x80, spdy_version_ch_, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00,
0x80, spdy_version_ch_, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00,
};
const unsigned char kV4Input[] = {
0x00, 0x05, 0x01, 0x0c,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x82,
0x00, 0x01, 0x01, 0x04,
0x00, 0x00, 0x00, 0x01,
0x8c,
0x00, 0x0c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0x00, 0x05, 0x01, 0x0c,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00,
0x82,
0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xde, 0xad, 0xbe, 0xef,
0x00, 0x04, 0x03, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
0x00, 0x0f, 0x03, 0x00,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00,
0x52, 0x45, 0x53, 0x45,
0x54, 0x53, 0x54, 0x52,
0x45, 0x41, 0x4d,
};
TestSpdyVisitor visitor(spdy_version_);
if (IsSpdy2()) {
visitor.SimulateInFramer(kV2Input, sizeof(kV2Input));
} else if (IsSpdy3()) {
visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
} else {
visitor.SimulateInFramer(kV4Input, sizeof(kV4Input));
}
EXPECT_EQ(2, visitor.syn_frame_count_);
EXPECT_EQ(0, visitor.syn_reply_frame_count_);
EXPECT_EQ(1, visitor.headers_frame_count_);
EXPECT_EQ(24, visitor.data_bytes_);
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(2, visitor.fin_frame_count_);
if (IsSpdy4()) {
base::StringPiece reset_stream = "RESETSTREAM";
EXPECT_EQ(reset_stream, visitor.fin_opaque_data_);
} else {
EXPECT_TRUE(visitor.fin_opaque_data_.empty());
}
EXPECT_EQ(0, visitor.fin_flag_count_);
EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
EXPECT_EQ(4, visitor.data_frame_count_);
visitor.fin_opaque_data_.clear();
}
TEST_P(SpdyFramerTest, FinOnDataFrame) {
const unsigned char kV2Input[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x02, 'h', 'h',
0x00, 0x02, 'v', 'v',
0x80, spdy_version_ch_, 0x00, 0x02,
0x00, 0x00, 0x00, 0x10,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
0x00, 0x02, 'a', 'a',
0x00, 0x02, 'b', 'b',
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0c,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x04,
0xde, 0xad, 0xbe, 0xef,
};
const unsigned char kV3Input[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x1a,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x00, 0x02, 'h', 'h',
0x00, 0x00, 0x00, 0x02,
'v', 'v',
0x80, spdy_version_ch_, 0x00, 0x02,
0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
'a', 'a', 0x00, 0x00,
0x00, 0x02, 'b', 'b',
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0c,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x04,
0xde, 0xad, 0xbe, 0xef,
};
const unsigned char kV4Input[] = {
0x00, 0x05, 0x01, 0x0c,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x82,
0x00, 0x01, 0x01, 0x04,
0x00, 0x00, 0x00, 0x01,
0x8c,
0x00, 0x0c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef,
0x00, 0x04, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
0xde, 0xad, 0xbe, 0xef,
};
TestSpdyVisitor visitor(spdy_version_);
if (IsSpdy2()) {
visitor.SimulateInFramer(kV2Input, sizeof(kV2Input));
} else if (IsSpdy3()) {
visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
} else {
visitor.SimulateInFramer(kV4Input, sizeof(kV4Input));
}
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.syn_frame_count_);
if (IsSpdy4()) {
EXPECT_EQ(0, visitor.syn_reply_frame_count_);
EXPECT_EQ(1, visitor.headers_frame_count_);
} else {
EXPECT_EQ(1, visitor.syn_reply_frame_count_);
EXPECT_EQ(0, visitor.headers_frame_count_);
}
EXPECT_EQ(16, visitor.data_bytes_);
EXPECT_EQ(0, visitor.fin_frame_count_);
EXPECT_EQ(0, visitor.fin_flag_count_);
EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
EXPECT_EQ(2, visitor.data_frame_count_);
}
TEST_P(SpdyFramerTest, FinOnSynReplyFrame) {
const unsigned char kV2Input[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x02, 'h', 'h',
0x00, 0x02, 'v', 'v',
0x80, spdy_version_ch_, 0x00, 0x02,
0x01, 0x00, 0x00, 0x10,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
0x00, 0x02, 'a', 'a',
0x00, 0x02, 'b', 'b',
};
const unsigned char kV3Input[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x1a,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x00, 0x02, 'h', 'h',
0x00, 0x00, 0x00, 0x02,
'v', 'v',
0x80, spdy_version_ch_, 0x00, 0x02,
0x01, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
'a', 'a', 0x00, 0x00,
0x00, 0x02, 'b', 'b',
};
const unsigned char kV4Input[] = {
0x00, 0x05, 0x01, 0x0c,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x82,
0x00, 0x01, 0x01, 0x05,
0x00, 0x00, 0x00, 0x01,
0x8c,
};
TestSpdyVisitor visitor(spdy_version_);
if (IsSpdy2()) {
visitor.SimulateInFramer(kV2Input, sizeof(kV2Input));
} else if (IsSpdy3()) {
visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
} else {
visitor.SimulateInFramer(kV4Input, sizeof(kV4Input));
}
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.syn_frame_count_);
if (IsSpdy4()) {
EXPECT_EQ(0, visitor.syn_reply_frame_count_);
EXPECT_EQ(1, visitor.headers_frame_count_);
} else {
EXPECT_EQ(1, visitor.syn_reply_frame_count_);
EXPECT_EQ(0, visitor.headers_frame_count_);
}
EXPECT_EQ(0, visitor.data_bytes_);
EXPECT_EQ(0, visitor.fin_frame_count_);
EXPECT_EQ(1, visitor.fin_flag_count_);
EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
EXPECT_EQ(0, visitor.data_frame_count_);
}
TEST_P(SpdyFramerTest, HeaderCompression) {
if (spdy_version_ >= 4) {
return;
}
SpdyFramer send_framer(spdy_version_);
SpdyFramer recv_framer(spdy_version_);
send_framer.set_enable_compression(true);
recv_framer.set_enable_compression(true);
const char kHeader1[] = "header1";
const char kHeader2[] = "header2";
const char kHeader3[] = "header3";
const char kValue1[] = "value1";
const char kValue2[] = "value2";
const char kValue3[] = "value3";
SpdyHeaderBlock block;
block[kHeader1] = kValue1;
block[kHeader2] = kValue2;
SpdySynStreamIR syn_ir_1(1);
syn_ir_1.set_name_value_block(block);
scoped_ptr<SpdyFrame> syn_frame_1(send_framer.SerializeFrame(syn_ir_1));
EXPECT_TRUE(syn_frame_1.get() != NULL);
block[kHeader3] = kValue3;
SpdySynStreamIR syn_stream(3);
syn_stream.set_name_value_block(block);
scoped_ptr<SpdyFrame> syn_frame_2(send_framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(syn_frame_2.get() != NULL);
scoped_ptr<SpdyFrame> decompressed;
scoped_ptr<SpdyFrame> uncompressed;
base::StringPiece serialized_headers;
SpdyHeaderBlock decompressed_headers;
decompressed.reset(SpdyFramerTestUtil::DecompressFrame(
&recv_framer, *syn_frame_1.get()));
EXPECT_TRUE(decompressed.get() != NULL);
serialized_headers = GetSerializedHeaders(decompressed.get(), send_framer);
EXPECT_TRUE(recv_framer.ParseHeaderBlockInBuffer(serialized_headers.data(),
serialized_headers.size(),
&decompressed_headers));
EXPECT_EQ(2u, decompressed_headers.size());
EXPECT_EQ(kValue1, decompressed_headers[kHeader1]);
EXPECT_EQ(kValue2, decompressed_headers[kHeader2]);
decompressed.reset(SpdyFramerTestUtil::DecompressFrame(
&recv_framer, *syn_frame_2.get()));
EXPECT_TRUE(decompressed.get() != NULL);
serialized_headers = GetSerializedHeaders(decompressed.get(), send_framer);
decompressed_headers.clear();
EXPECT_TRUE(recv_framer.ParseHeaderBlockInBuffer(serialized_headers.data(),
serialized_headers.size(),
&decompressed_headers));
EXPECT_EQ(3u, decompressed_headers.size());
EXPECT_EQ(kValue1, decompressed_headers[kHeader1]);
EXPECT_EQ(kValue2, decompressed_headers[kHeader2]);
EXPECT_EQ(kValue3, decompressed_headers[kHeader3]);
}
TEST_P(SpdyFramerTest, UnclosedStreamDataCompressors) {
SpdyFramer send_framer(spdy_version_);
send_framer.set_enable_compression(true);
const char kHeader1[] = "header1";
const char kHeader2[] = "header2";
const char kValue1[] = "value1";
const char kValue2[] = "value2";
SpdySynStreamIR syn_stream(1);
syn_stream.SetHeader(kHeader1, kValue1);
syn_stream.SetHeader(kHeader2, kValue2);
scoped_ptr<SpdyFrame> syn_frame(send_framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(syn_frame.get() != NULL);
StringPiece bytes = "this is a test test test test test!";
net::SpdyDataIR data_ir(1, bytes);
data_ir.set_fin(true);
scoped_ptr<SpdyFrame> send_frame(send_framer.SerializeData(data_ir));
EXPECT_TRUE(send_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
const unsigned char* data;
data = reinterpret_cast<const unsigned char*>(syn_frame->data());
visitor.SimulateInFramer(data, syn_frame->size());
data = reinterpret_cast<const unsigned char*>(send_frame->data());
visitor.SimulateInFramer(data, send_frame->size());
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.syn_frame_count_);
EXPECT_EQ(0, visitor.syn_reply_frame_count_);
EXPECT_EQ(0, visitor.headers_frame_count_);
EXPECT_EQ(bytes.size(), static_cast<unsigned>(visitor.data_bytes_));
EXPECT_EQ(0, visitor.fin_frame_count_);
EXPECT_EQ(0, visitor.fin_flag_count_);
EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
EXPECT_EQ(1, visitor.data_frame_count_);
}
TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) {
SpdyFramer send_framer(spdy_version_);
send_framer.set_enable_compression(true);
const char kHeader1[] = "header1";
const char kHeader2[] = "header2";
const char kValue1[] = "value1";
const char kValue2[] = "value2";
SpdySynStreamIR syn_stream(1);
syn_stream.SetHeader(kHeader1, kValue1);
syn_stream.SetHeader(kHeader2, kValue2);
scoped_ptr<SpdyFrame> syn_frame(send_framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(syn_frame.get() != NULL);
const char bytes[] = "this is a test test test test test!";
net::SpdyDataIR data_ir(1, StringPiece(bytes, arraysize(bytes)));
data_ir.set_fin(true);
scoped_ptr<SpdyFrame> send_frame(send_framer.SerializeData(data_ir));
EXPECT_TRUE(send_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
const unsigned char* data;
data = reinterpret_cast<const unsigned char*>(syn_frame->data());
for (size_t idx = 0; idx < syn_frame->size(); ++idx) {
visitor.SimulateInFramer(data + idx, 1);
ASSERT_EQ(0, visitor.error_count_);
}
data = reinterpret_cast<const unsigned char*>(send_frame->data());
for (size_t idx = 0; idx < send_frame->size(); ++idx) {
visitor.SimulateInFramer(data + idx, 1);
ASSERT_EQ(0, visitor.error_count_);
}
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.syn_frame_count_);
EXPECT_EQ(0, visitor.syn_reply_frame_count_);
EXPECT_EQ(0, visitor.headers_frame_count_);
EXPECT_EQ(arraysize(bytes), static_cast<unsigned>(visitor.data_bytes_));
EXPECT_EQ(0, visitor.fin_frame_count_);
EXPECT_EQ(0, visitor.fin_flag_count_);
EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
EXPECT_EQ(1, visitor.data_frame_count_);
}
TEST_P(SpdyFramerTest, WindowUpdateFrame) {
SpdyFramer framer(spdy_version_);
scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
net::SpdyWindowUpdateIR(1, 0x12345678)));
const char kDescription[] = "WINDOW_UPDATE frame, stream 1, delta 0x12345678";
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x09,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x12, 0x34, 0x56, 0x78
};
const unsigned char kV4FrameData[] = {
0x00, 0x04, 0x08, 0x00,
0x00, 0x00, 0x00, 0x01,
0x12, 0x34, 0x56, 0x78
};
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
TEST_P(SpdyFramerTest, CreateDataFrame) {
SpdyFramer framer(spdy_version_);
{
const char kDescription[] = "'hello' data frame, no FIN";
const unsigned char kV3FrameData[] = {
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x05,
'h', 'e', 'l', 'l',
'o'
};
const unsigned char kV4FrameData[] = {
0x00, 0x05, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
'h', 'e', 'l', 'l',
'o'
};
const char bytes[] = "hello";
SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes)));
scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
if (IsSpdy4()) {
CompareFrame(
kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(
kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
SpdyDataIR data_header_ir(1);
data_header_ir.SetDataShallow(base::StringPiece(bytes, strlen(bytes)));
frame.reset(framer.SerializeDataFrameHeader(data_header_ir));
CompareCharArraysWithHexError(
kDescription,
reinterpret_cast<const unsigned char*>(frame->data()),
framer.GetDataFrameMinimumSize(),
IsSpdy4() ? kV4FrameData : kV3FrameData,
framer.GetDataFrameMinimumSize());
}
{
const char kDescription[] = "'hello' data frame with padding, no FIN";
const unsigned char kV3FrameData[] = {
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x05,
'h', 'e', 'l', 'l',
'o'
};
const unsigned char kV4FrameData[] = {
0x00, 0x0d, 0x00, 0x10,
0x00, 0x00, 0x00, 0x01,
0x07,
'h', 'e', 'l', 'l',
'o',
'0', '0', '0', '0',
'0', '0', '0'
};
const char bytes[] = "hello";
SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes)));
data_ir.set_padding_len(8);
scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
if (IsSpdy4()) {
CompareFrame(
kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(
kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
{
const char kDescription[] = "Data frame with negative data byte, no FIN";
const unsigned char kV3FrameData[] = {
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
0xff
};
const unsigned char kV4FrameData[] = {
0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xff
};
net::SpdyDataIR data_ir(1, StringPiece("\xff", 1));
scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
if (IsSpdy4()) {
CompareFrame(
kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(
kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
{
const char kDescription[] = "'hello' data frame, with FIN";
const unsigned char kV3FrameData[] = {
0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x05,
'h', 'e', 'l', 'l',
'o'
};
const unsigned char kV4FrameData[] = {
0x00, 0x05, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
'h', 'e', 'l', 'l',
'o'
};
net::SpdyDataIR data_ir(1, StringPiece("hello", 5));
data_ir.set_fin(true);
scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
if (IsSpdy4()) {
CompareFrame(
kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(
kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
{
const char kDescription[] = "Empty data frame";
const unsigned char kV3FrameData[] = {
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
};
const unsigned char kV4FrameData[] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
};
net::SpdyDataIR data_ir(1, StringPiece());
scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
if (IsSpdy4()) {
CompareFrame(
kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(
kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
{
const char kDescription[] = "Data frame with max stream ID";
const unsigned char kV3FrameData[] = {
0x7f, 0xff, 0xff, 0xff,
0x01, 0x00, 0x00, 0x05,
'h', 'e', 'l', 'l',
'o'
};
const unsigned char kV4FrameData[] = {
0x00, 0x05, 0x00, 0x01,
0x7f, 0xff, 0xff, 0xff,
'h', 'e', 'l', 'l',
'o'
};
net::SpdyDataIR data_ir(0x7fffffff, "hello");
data_ir.set_fin(true);
scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
if (IsSpdy4()) {
CompareFrame(
kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(
kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
if (!IsSpdy4()) {
const char kDescription[] = "Large data frame";
const int kDataSize = 4 * 1024 * 1024;
const string kData(kDataSize, 'A');
const unsigned char kFrameHeader[] = {
0x00, 0x00, 0x00, 0x01,
0x01, 0x40, 0x00, 0x00,
};
const int kFrameSize = arraysize(kFrameHeader) + kDataSize;
scoped_ptr<unsigned char[]> expected_frame_data(
new unsigned char[kFrameSize]);
memcpy(expected_frame_data.get(), kFrameHeader, arraysize(kFrameHeader));
memset(expected_frame_data.get() + arraysize(kFrameHeader), 'A', kDataSize);
net::SpdyDataIR data_ir(1, StringPiece(kData.data(), kData.size()));
data_ir.set_fin(true);
scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
CompareFrame(kDescription, *frame, expected_frame_data.get(), kFrameSize);
}
}
TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
{
const char kDescription[] = "SYN_STREAM frame, lowest pri, no FIN";
const unsigned char kPri = IsSpdy2() ? 0xC0 : 0xE0;
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
kPri, 0x00, 0x00, 0x02,
0x00, 0x03, 'b', 'a',
'r', 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x03, 'b', 'a', 'r'
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x2a,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
kPri, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00,
0x00, 0x03, 'b', 'a',
'r', 0x00, 0x00, 0x00,
0x03, 'f', 'o', 'o',
0x00, 0x00, 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x00, 0x00, 0x03, 'b',
'a', 'r'
};
const unsigned char kV4FrameData[] = {
0x00, 0x16, 0x01, 0x0c,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x07,
0x40, 0x03, 0x62, 0x61,
0x72, 0x03, 0x66, 0x6f,
0x6f, 0x40, 0x03, 0x66,
0x6f, 0x6f, 0x03, 0x62,
0x61, 0x72,
};
SpdySynStreamIR syn_stream(1);
syn_stream.set_priority(framer.GetLowestPriority());
syn_stream.SetHeader("bar", "foo");
syn_stream.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
{
const char kDescription[] =
"SYN_STREAM frame with a 0-length header name, highest pri, FIN, "
"max stream ID";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x01, 0x00, 0x00, 0x1D,
0x7f, 0xff, 0xff, 0xff,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x03, 'f', 'o', 'o',
0x00, 0x03, 'b', 'a',
'r'
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x01, 0x00, 0x00, 0x27,
0x7f, 0xff, 0xff, 0xff,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 'f', 'o',
'o', 0x00, 0x00, 0x00,
0x03, 'f', 'o', 'o',
0x00, 0x00, 0x00, 0x03,
'b', 'a', 'r'
};
const unsigned char kV4FrameData[] = {
0x00, 0x13, 0x01, 0x0d,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x03, 0x66,
0x6f, 0x6f, 0x40, 0x03,
0x66, 0x6f, 0x6f, 0x03,
0x62, 0x61, 0x72,
};
SpdySynStreamIR syn_stream(0x7fffffff);
syn_stream.set_associated_to_stream_id(0x7fffffff);
syn_stream.set_priority(framer.GetHighestPriority());
syn_stream.set_fin(true);
syn_stream.SetHeader("", "foo");
syn_stream.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
{
const char kDescription[] =
"SYN_STREAM frame with a 0-length header val, high pri, FIN, "
"max stream ID";
const unsigned char kPri = IsSpdy2() ? 0x40 : 0x20;
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x01, 0x00, 0x00, 0x1D,
0x7f, 0xff, 0xff, 0xff,
0x7f, 0xff, 0xff, 0xff,
kPri, 0x00, 0x00, 0x02,
0x00, 0x03, 'b', 'a',
'r', 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x00
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x01, 0x00, 0x00, 0x27,
0x7f, 0xff, 0xff, 0xff,
0x7f, 0xff, 0xff, 0xff,
kPri, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00,
0x00, 0x03, 'b', 'a',
'r', 0x00, 0x00, 0x00,
0x03, 'f', 'o', 'o',
0x00, 0x00, 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x00, 0x00, 0x00
};
const unsigned char kV4FrameData[] = {
0x00, 0x13, 0x01, 0x0d,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01,
0x40, 0x03, 0x62, 0x61,
0x72, 0x03, 0x66, 0x6f,
0x6f, 0x40, 0x03, 0x66,
0x6f, 0x6f, 0x00,
};
SpdySynStreamIR syn_stream(0x7fffffff);
syn_stream.set_associated_to_stream_id(0x7fffffff);
syn_stream.set_priority(1);
syn_stream.set_fin(true);
syn_stream.SetHeader("bar", "foo");
syn_stream.SetHeader("foo", "");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
}
#if !defined(USE_SYSTEM_ZLIB)
TEST_P(SpdyFramerTest, CreateSynStreamCompressed) {
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(true);
{
const char kDescription[] =
"SYN_STREAM frame, low pri, no FIN";
const SpdyPriority priority = IsSpdy2() ? 2 : 4;
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x36,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x80, 0x00, 0x38, 0xea,
0xdf, 0xa2, 0x51, 0xb2,
0x62, 0x60, 0x62, 0x60,
0x4e, 0x4a, 0x2c, 0x62,
0x60, 0x06, 0x08, 0xa0,
0xb4, 0xfc, 0x7c, 0x80,
0x00, 0x62, 0x60, 0x4e,
0xcb, 0xcf, 0x67, 0x60,
0x06, 0x08, 0xa0, 0xa4,
0xc4, 0x22, 0x80, 0x00,
0x02, 0x00, 0x00, 0x00,
0xff, 0xff,
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x01,
0x00, 0x00, 0x00, 0x37,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x80, 0x00, 0x38, 0xEA,
0xE3, 0xC6, 0xA7, 0xC2,
0x02, 0xE5, 0x0E, 0x50,
0xC2, 0x4B, 0x4A, 0x04,
0xE5, 0x0B, 0x66, 0x80,
0x00, 0x4A, 0xCB, 0xCF,
0x07, 0x08, 0x20, 0x10,
0x95, 0x96, 0x9F, 0x0F,
0xA2, 0x00, 0x02, 0x28,
0x29, 0xB1, 0x08, 0x20,
0x80, 0x00, 0x00, 0x00,
0x00, 0xFF, 0xFF,
};
SpdySynStreamIR syn_stream(1);
syn_stream.set_priority(priority);
syn_stream.SetHeader("bar", "foo");
syn_stream.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
}
}
}
#endif
TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) {
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
{
const char kDescription[] = "SYN_REPLY frame, no FIN";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x00, 0x00, 0x00, 0x1C,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
0x00, 0x03, 'b', 'a',
'r', 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x03, 'b', 'a', 'r'
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x00, 0x00, 0x00, 0x24,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x03,
'b', 'a', 'r', 0x00,
0x00, 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x00,
0x00, 0x03, 'f', 'o',
'o', 0x00, 0x00, 0x00,
0x03, 'b', 'a', 'r'
};
const unsigned char kV4FrameData[] = {
0x00, 0x12, 0x01, 0x04,
0x00, 0x00, 0x00, 0x01,
0x40, 0x03, 0x62, 0x61,
0x72, 0x03, 0x66, 0x6f,
0x6f, 0x40, 0x03, 0x66,
0x6f, 0x6f, 0x03, 0x62,
0x61, 0x72,
};
SpdySynReplyIR syn_reply(1);
syn_reply.SetHeader("bar", "foo");
syn_reply.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynReply(syn_reply));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
{
const char kDescription[] =
"SYN_REPLY frame with a 0-length header name, FIN, max stream ID";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x01, 0x00, 0x00, 0x19,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x03, 'f', 'o', 'o',
0x00, 0x03, 'b', 'a',
'r'
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x01, 0x00, 0x00, 0x21,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x00, 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x00,
0x00, 0x03, 'b', 'a',
'r'
};
const unsigned char kV4FrameData[] = {
0x00, 0x0f, 0x01, 0x05,
0x7f, 0xff, 0xff, 0xff,
0x40, 0x00, 0x03, 0x66,
0x6f, 0x6f, 0x40, 0x03,
0x66, 0x6f, 0x6f, 0x03,
0x62, 0x61, 0x72,
};
SpdySynReplyIR syn_reply(0x7fffffff);
syn_reply.set_fin(true);
syn_reply.SetHeader("", "foo");
syn_reply.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynReply(syn_reply));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
{
const char kDescription[] =
"SYN_REPLY frame with a 0-length header val, FIN, max stream ID";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x01, 0x00, 0x00, 0x19,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
0x00, 0x03, 'b', 'a',
'r', 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x00
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x01, 0x00, 0x00, 0x21,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x03,
'b', 'a', 'r', 0x00,
0x00, 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x00,
0x00, 0x03, 'f', 'o',
'o', 0x00, 0x00, 0x00,
0x00
};
const unsigned char kV4FrameData[] = {
0x00, 0x0f, 0x01, 0x05,
0x7f, 0xff, 0xff, 0xff,
0x40, 0x03, 0x62, 0x61,
0x72, 0x03, 0x66, 0x6f,
0x6f, 0x40, 0x03, 0x66,
0x6f, 0x6f, 0x00,
};
SpdySynReplyIR syn_reply(0x7fffffff);
syn_reply.set_fin(true);
syn_reply.SetHeader("bar", "foo");
syn_reply.SetHeader("foo", "");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynReply(syn_reply));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
}
#if !defined(USE_SYSTEM_ZLIB)
TEST_P(SpdyFramerTest, CreateSynReplyCompressed) {
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(true);
{
const char kDescription[] = "SYN_REPLY frame, no FIN";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x00, 0x00, 0x00, 0x32,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x38, 0xea,
0xdf, 0xa2, 0x51, 0xb2,
0x62, 0x60, 0x62, 0x60,
0x4e, 0x4a, 0x2c, 0x62,
0x60, 0x06, 0x08, 0xa0,
0xb4, 0xfc, 0x7c, 0x80,
0x00, 0x62, 0x60, 0x4e,
0xcb, 0xcf, 0x67, 0x60,
0x06, 0x08, 0xa0, 0xa4,
0xc4, 0x22, 0x80, 0x00,
0x02, 0x00, 0x00, 0x00,
0xff, 0xff,
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x02,
0x00, 0x00, 0x00, 0x31,
0x00, 0x00, 0x00, 0x01,
0x38, 0xea, 0xe3, 0xc6,
0xa7, 0xc2, 0x02, 0xe5,
0x0e, 0x50, 0xc2, 0x4b,
0x4a, 0x04, 0xe5, 0x0b,
0x66, 0x80, 0x00, 0x4a,
0xcb, 0xcf, 0x07, 0x08,
0x20, 0x10, 0x95, 0x96,
0x9f, 0x0f, 0xa2, 0x00,
0x02, 0x28, 0x29, 0xb1,
0x08, 0x20, 0x80, 0x00,
0x00, 0x00, 0x00, 0xff,
0xff,
};
SpdySynReplyIR syn_reply(1);
syn_reply.SetHeader("bar", "foo");
syn_reply.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynReply(syn_reply));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
}
}
}
#endif
TEST_P(SpdyFramerTest, CreateRstStream) {
SpdyFramer framer(spdy_version_);
{
const char kDescription[] = "RST_STREAM frame";
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
};
const unsigned char kV4FrameData[] = {
0x00, 0x07, 0x03, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
0x52, 0x53, 0x54
};
net::SpdyRstStreamIR rst_stream(1, RST_STREAM_PROTOCOL_ERROR, "RST");
scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
{
const char kDescription[] = "RST_STREAM frame with max stream ID";
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01,
};
const unsigned char kV4FrameData[] = {
0x00, 0x04, 0x03, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01,
};
net::SpdyRstStreamIR rst_stream(0x7FFFFFFF,
RST_STREAM_PROTOCOL_ERROR,
"");
scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
{
const char kDescription[] = "RST_STREAM frame with max status code";
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x06,
};
const unsigned char kV4FrameData[] = {
0x00, 0x04, 0x03, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x06,
};
net::SpdyRstStreamIR rst_stream(0x7FFFFFFF,
RST_STREAM_INTERNAL_ERROR,
"");
scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
}
TEST_P(SpdyFramerTest, CreateSettings) {
SpdyFramer framer(spdy_version_);
{
const char kDescription[] = "Network byte order SETTINGS frame";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x0c,
0x00, 0x00, 0x00, 0x01,
0x07, 0x00, 0x00, 0x01,
0x0a, 0x0b, 0x0c, 0x0d,
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x0c,
0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x07,
0x0a, 0x0b, 0x0c, 0x0d,
};
const unsigned char kV4FrameData[] = {
0x00, 0x05, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00,
0x04, 0x0a, 0x0b, 0x0c,
0x0d,
};
uint32 kValue = 0x0a0b0c0d;
SpdySettingsIR settings_ir;
SpdySettingsFlags kFlags = static_cast<SpdySettingsFlags>(0x01);
SpdySettingsIds kId = SETTINGS_INITIAL_WINDOW_SIZE;
SettingsMap settings;
settings[kId] = SettingsFlagsAndValue(kFlags, kValue);
EXPECT_EQ(kFlags, settings[kId].first);
EXPECT_EQ(kValue, settings[kId].second);
settings_ir.AddSetting(kId,
kFlags & SETTINGS_FLAG_PLEASE_PERSIST,
kFlags & SETTINGS_FLAG_PERSISTED,
kValue);
scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
{
const char kDescription[] = "Basic SETTINGS frame";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x24,
0x00, 0x00, 0x00, 0x04,
0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05,
0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06,
0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07,
0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08,
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x24,
0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x05,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x06,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x07,
0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x08,
};
const unsigned char kV4FrameData[] = {
0x00, 0x14, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00,
0x03,
0x00, 0x00, 0x00, 0x07,
0x04,
0x00, 0x00, 0x00, 0x08,
0x01,
0x00, 0x00, 0x00, 0x05,
0x02,
0x00, 0x00, 0x00, 0x06,
};
SpdySettingsIR settings_ir;
settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 1),
false,
false,
5);
settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 2),
false,
false,
6);
settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 3),
false,
false,
7);
settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 4),
false,
false,
8);
scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
{
const char kDescription[] = "Empty SETTINGS frame";
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00,
};
const unsigned char kV4FrameData[] = {
0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00,
};
SpdySettingsIR settings_ir;
scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
}
TEST_P(SpdyFramerTest, CreatePingFrame) {
SpdyFramer framer(spdy_version_);
{
const char kDescription[] = "PING frame";
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x06,
0x00, 0x00, 0x00, 0x04,
0x12, 0x34, 0x56, 0x78,
};
const unsigned char kV4FrameData[] = {
0x00, 0x08, 0x06, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x56, 0x78,
0x9a, 0xbc, 0xde, 0xff,
};
const unsigned char kV4FrameDataWithAck[] = {
0x00, 0x08, 0x06, 0x01,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x56, 0x78,
0x9a, 0xbc, 0xde, 0xff,
};
scoped_ptr<SpdyFrame> frame;
if (IsSpdy4()) {
const SpdyPingId kPingId = 0x123456789abcdeffULL;
SpdyPingIR ping_ir(kPingId);
ASSERT_FALSE(ping_ir.is_ack());
frame.reset(framer.SerializePing(ping_ir));
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
ping_ir.set_is_ack(true);
frame.reset(framer.SerializePing(ping_ir));
CompareFrame(kDescription, *frame,
kV4FrameDataWithAck, arraysize(kV4FrameDataWithAck));
} else {
frame.reset(framer.SerializePing(SpdyPingIR(0x12345678ull)));
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
}
TEST_P(SpdyFramerTest, CreateGoAway) {
SpdyFramer framer(spdy_version_);
{
const char kDescription[] = "GOAWAY frame";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00,
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
const unsigned char kV4FrameData[] = {
0x00, 0x0a, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x47, 0x41,
};
SpdyGoAwayIR goaway_ir(0, GOAWAY_OK, "GA");
scoped_ptr<SpdyFrame> frame(framer.SerializeGoAway(goaway_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
{
const char kDescription[] = "GOAWAY frame with max stream ID, status";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x04,
0x7f, 0xff, 0xff, 0xff,
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x08,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
};
const unsigned char kV4FrameData[] = {
0x00, 0x0a, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
0x47, 0x41,
};
SpdyGoAwayIR goaway_ir(0x7FFFFFFF, GOAWAY_INTERNAL_ERROR, "GA");
scoped_ptr<SpdyFrame> frame(framer.SerializeGoAway(goaway_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
}
TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
{
const char kDescription[] = "HEADERS frame, no FIN";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x00, 0x00, 0x00, 0x1C,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
0x00, 0x03, 'b', 'a',
'r', 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x03, 'b', 'a', 'r'
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x00, 0x00, 0x00, 0x24,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x03,
'b', 'a', 'r', 0x00,
0x00, 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x00,
0x00, 0x03, 'f', 'o',
'o', 0x00, 0x00, 0x00,
0x03, 'b', 'a', 'r'
};
const unsigned char kV4FrameData[] = {
0x00, 0x12, 0x01, 0x04,
0x00, 0x00, 0x00, 0x01,
0x40, 0x03, 0x62, 0x61,
0x72, 0x03, 0x66, 0x6f,
0x6f, 0x40, 0x03, 0x66,
0x6f, 0x6f, 0x03, 0x62,
0x61, 0x72,
};
SpdyHeadersIR headers_ir(1);
headers_ir.SetHeader("bar", "foo");
headers_ir.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
{
const char kDescription[] =
"HEADERS frame with a 0-length header name, FIN, max stream ID";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x01, 0x00, 0x00, 0x19,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x03, 'f', 'o', 'o',
0x00, 0x03, 'b', 'a',
'r'
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x01, 0x00, 0x00, 0x21,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x00, 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x00,
0x00, 0x03, 'b', 'a',
'r'
};
const unsigned char kV4FrameData[] = {
0x00, 0x0f, 0x01, 0x05,
0x7f, 0xff, 0xff, 0xff,
0x40, 0x00, 0x03, 0x66,
0x6f, 0x6f, 0x40, 0x03,
0x66, 0x6f, 0x6f, 0x03,
0x62, 0x61, 0x72,
};
SpdyHeadersIR headers_ir(0x7fffffff);
headers_ir.set_fin(true);
headers_ir.SetHeader("", "foo");
headers_ir.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
{
const char kDescription[] =
"HEADERS frame with a 0-length header val, FIN, max stream ID";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x01, 0x00, 0x00, 0x19,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
0x00, 0x03, 'b', 'a',
'r', 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x00
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x01, 0x00, 0x00, 0x21,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x03,
'b', 'a', 'r', 0x00,
0x00, 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x00,
0x00, 0x03, 'f', 'o',
'o', 0x00, 0x00, 0x00,
0x00
};
const unsigned char kV4FrameData[] = {
0x00, 0x0f, 0x01, 0x05,
0x7f, 0xff, 0xff, 0xff,
0x40, 0x03, 0x62, 0x61,
0x72, 0x03, 0x66, 0x6f,
0x6f, 0x40, 0x03, 0x66,
0x6f, 0x6f, 0x00,
};
SpdyHeadersIR headers_ir(0x7fffffff);
headers_ir.set_fin(true);
headers_ir.SetHeader("bar", "foo");
headers_ir.SetHeader("foo", "");
scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
}
#if !defined(USE_SYSTEM_ZLIB)
TEST_P(SpdyFramerTest, CreateHeadersCompressed) {
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(true);
{
const char kDescription[] = "HEADERS frame, no FIN";
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x00, 0x00, 0x00, 0x32,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x38, 0xea,
0xdf, 0xa2, 0x51, 0xb2,
0x62, 0x60, 0x62, 0x60,
0x4e, 0x4a, 0x2c, 0x62,
0x60, 0x06, 0x08, 0xa0,
0xb4, 0xfc, 0x7c, 0x80,
0x00, 0x62, 0x60, 0x4e,
0xcb, 0xcf, 0x67, 0x60,
0x06, 0x08, 0xa0, 0xa4,
0xc4, 0x22, 0x80, 0x00,
0x02, 0x00, 0x00, 0x00,
0xff, 0xff,
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x08,
0x00, 0x00, 0x00, 0x31,
0x00, 0x00, 0x00, 0x01,
0x38, 0xea, 0xe3, 0xc6,
0xa7, 0xc2, 0x02, 0xe5,
0x0e, 0x50, 0xc2, 0x4b,
0x4a, 0x04, 0xe5, 0x0b,
0x66, 0x80, 0x00, 0x4a,
0xcb, 0xcf, 0x07, 0x08,
0x20, 0x10, 0x95, 0x96,
0x9f, 0x0f, 0xa2, 0x00,
0x02, 0x28, 0x29, 0xb1,
0x08, 0x20, 0x80, 0x00,
0x00, 0x00, 0x00, 0xff,
0xff,
};
SpdyHeadersIR headers_ir(1);
headers_ir.SetHeader("bar", "foo");
headers_ir.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
} else {
}
}
}
#endif
TEST_P(SpdyFramerTest, CreateWindowUpdate) {
SpdyFramer framer(spdy_version_);
{
const char kDescription[] = "WINDOW_UPDATE frame";
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x09,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
};
const unsigned char kV4FrameData[] = {
0x00, 0x04, 0x08, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
};
scoped_ptr<SpdyFrame> frame(
framer.SerializeWindowUpdate(net::SpdyWindowUpdateIR(1, 1)));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
{
const char kDescription[] = "WINDOW_UPDATE frame with max stream ID";
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x09,
0x00, 0x00, 0x00, 0x08,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01,
};
const unsigned char kV4FrameData[] = {
0x00, 0x04, 0x08, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01,
};
scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
net::SpdyWindowUpdateIR(0x7FFFFFFF, 1)));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
{
const char kDescription[] = "WINDOW_UPDATE frame with max window delta";
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x09,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x7f, 0xff, 0xff, 0xff,
};
const unsigned char kV4FrameData[] = {
0x00, 0x04, 0x08, 0x00,
0x00, 0x00, 0x00, 0x01,
0x7f, 0xff, 0xff, 0xff,
};
scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
net::SpdyWindowUpdateIR(1, 0x7FFFFFFF)));
if (IsSpdy4()) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} else {
CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
}
}
}
TEST_P(SpdyFramerTest, SerializeBlocked) {
if (spdy_version_ < SPDY4) {
return;
}
SpdyFramer framer(spdy_version_);
const char kDescription[] = "BLOCKED frame";
const unsigned char kFrameData[] = {
0x00, 0x00, 0x0a, 0x00,
0x00, 0x00, 0x00, 0x00,
};
SpdyBlockedIR blocked_ir(0);
scoped_ptr<SpdySerializedFrame> frame(framer.SerializeFrame(blocked_ir));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
}
TEST_P(SpdyFramerTest, CreateBlocked) {
if (spdy_version_ < SPDY4) {
return;
}
SpdyFramer framer(spdy_version_);
const char kDescription[] = "BLOCKED frame";
const SpdyStreamId kStreamId = 3;
scoped_ptr<SpdySerializedFrame> frame_serialized(
framer.SerializeBlocked(SpdyBlockedIR(kStreamId)));
SpdyBlockedIR blocked_ir(kStreamId);
scoped_ptr<SpdySerializedFrame> frame_created(
framer.SerializeFrame(blocked_ir));
CompareFrames(kDescription, *frame_serialized, *frame_created);
}
TEST_P(SpdyFramerTest, CreatePushPromise) {
if (spdy_version_ < SPDY4) {
return;
}
SpdyFramer framer(spdy_version_);
const char kDescription[] = "PUSH_PROMISE frame";
const unsigned char kFrameData[] = {
0x00, 0x16, 0x05, 0x04,
0x00, 0x00, 0x00, 0x2a,
0x00, 0x00, 0x00, 0x39,
0x40, 0x03, 0x62, 0x61,
0x72, 0x03, 0x66, 0x6f,
0x6f, 0x40, 0x03, 0x66,
0x6f, 0x6f, 0x03, 0x62,
0x61, 0x72,
};
SpdyPushPromiseIR push_promise(42, 57);
push_promise.SetHeader("bar", "foo");
push_promise.SetHeader("foo", "bar");
scoped_ptr<SpdySerializedFrame> frame(
framer.SerializePushPromise(push_promise));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
}
TEST_P(SpdyFramerTest, ReadCompressedSynStreamHeaderBlock) {
SpdyFramer framer(spdy_version_);
SpdySynStreamIR syn_stream(1);
syn_stream.set_priority(1);
syn_stream.SetHeader("aa", "vv");
syn_stream.SetHeader("bb", "ww");
SpdyHeaderBlock headers = syn_stream.name_value_block();
scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
EXPECT_EQ(1, visitor.syn_frame_count_);
EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
}
TEST_P(SpdyFramerTest, ReadCompressedSynReplyHeaderBlock) {
SpdyFramer framer(spdy_version_);
SpdySynReplyIR syn_reply(1);
syn_reply.SetHeader("alpha", "beta");
syn_reply.SetHeader("gamma", "delta");
SpdyHeaderBlock headers = syn_reply.name_value_block();
scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynReply(syn_reply));
EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
if (IsSpdy4()) {
EXPECT_EQ(0, visitor.syn_reply_frame_count_);
EXPECT_EQ(1, visitor.headers_frame_count_);
} else {
EXPECT_EQ(1, visitor.syn_reply_frame_count_);
EXPECT_EQ(0, visitor.headers_frame_count_);
}
EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
}
TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlock) {
SpdyFramer framer(spdy_version_);
SpdyHeadersIR headers_ir(1);
headers_ir.SetHeader("alpha", "beta");
headers_ir.SetHeader("gamma", "delta");
SpdyHeaderBlock headers = headers_ir.name_value_block();
scoped_ptr<SpdyFrame> control_frame(framer.SerializeHeaders(headers_ir));
EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
EXPECT_EQ(1, visitor.headers_frame_count_);
EXPECT_LE(2, visitor.control_frame_header_data_count_);
EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
}
TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) {
SpdyFramer framer(spdy_version_);
SpdyHeadersIR headers_ir(1);
headers_ir.set_fin(true);
headers_ir.SetHeader("alpha", "beta");
headers_ir.SetHeader("gamma", "delta");
SpdyHeaderBlock headers = headers_ir.name_value_block();
scoped_ptr<SpdyFrame> control_frame(framer.SerializeHeaders(headers_ir));
EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
EXPECT_EQ(1, visitor.headers_frame_count_);
EXPECT_LE(2, visitor.control_frame_header_data_count_);
EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
}
TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) {
if (spdy_version_ >= 4) {
return;
}
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
SpdySynStreamIR syn_stream(1);
syn_stream.set_priority(1);
syn_stream.SetHeader("aa", "");
scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
const size_t kBigValueSize =
framer.GetControlFrameBufferMaxSize() - control_frame->size();
string big_value(kBigValueSize, 'x');
syn_stream.SetHeader("aa", big_value.c_str());
control_frame.reset(framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(control_frame.get() != NULL);
EXPECT_EQ(framer.GetControlFrameBufferMaxSize(), control_frame->size());
TestSpdyVisitor visitor(spdy_version_);
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
EXPECT_TRUE(visitor.header_buffer_valid_);
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.syn_frame_count_);
EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
EXPECT_LT(kBigValueSize, visitor.header_buffer_length_);
}
TEST_P(SpdyFramerTest, ControlFrameTooLarge) {
if (spdy_version_ >= 4) {
return;
}
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
SpdySynStreamIR syn_stream(1);
syn_stream.SetHeader("aa", "");
syn_stream.set_priority(1);
scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
const size_t kBigValueSize =
framer.GetControlFrameBufferMaxSize() - control_frame->size() + 1;
string big_value(kBigValueSize, 'x');
syn_stream.SetHeader("aa", big_value.c_str());
control_frame.reset(framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(control_frame.get() != NULL);
EXPECT_EQ(framer.GetControlFrameBufferMaxSize() + 1,
control_frame->size());
TestSpdyVisitor visitor(spdy_version_);
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
EXPECT_FALSE(visitor.header_buffer_valid_);
EXPECT_EQ(1, visitor.error_count_);
EXPECT_EQ(SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE,
visitor.framer_.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
EXPECT_EQ(0, visitor.syn_frame_count_);
EXPECT_EQ(0u, visitor.header_buffer_length_);
}
TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) {
const size_t kHeaderBufferChunks = 4;
const size_t kHeaderBufferSize =
TestSpdyVisitor::header_data_chunk_max_size() * kHeaderBufferChunks;
const size_t kBigValueSize = kHeaderBufferSize * 2;
string big_value(kBigValueSize, 'x');
SpdyFramer framer(spdy_version_);
SpdySynStreamIR syn_stream(1);
syn_stream.set_priority(1);
syn_stream.set_fin(true);
syn_stream.SetHeader("aa", big_value.c_str());
scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
EXPECT_TRUE(control_frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.set_header_buffer_size(kHeaderBufferSize);
visitor.use_compression_ = true;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
EXPECT_FALSE(visitor.header_buffer_valid_);
EXPECT_EQ(1, visitor.error_count_);
EXPECT_EQ(SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE,
visitor.framer_.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
EXPECT_LE(kHeaderBufferChunks + 1,
static_cast<unsigned>(visitor.control_frame_header_data_count_));
EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_);
EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
}
TEST_P(SpdyFramerTest, DecompressCorruptHeaderBlock) {
if (spdy_version_ >= 4) {
return;
}
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
SpdySynStreamIR syn_stream(1);
syn_stream.set_priority(1);
syn_stream.SetHeader("aa", "alpha beta gamma delta");
scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
EXPECT_EQ(1, visitor.error_count_);
EXPECT_EQ(SpdyFramer::SPDY_DECOMPRESS_FAILURE, visitor.framer_.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
EXPECT_EQ(0u, visitor.header_buffer_length_);
}
TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) {
SpdyFramer framer(spdy_version_);
ASSERT_GE(250u, SpdyFramer::kControlFrameBufferSize);
const unsigned char length = 1 + SpdyFramer::kControlFrameBufferSize;
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, length,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
const unsigned char less_than_min_length =
framer.GetGoAwayMinimumSize() - framer.GetControlFrameHeaderSize() - 1;
const unsigned char kV4FrameData[] = {
0x00, static_cast<uint8>(less_than_min_length), 0x07, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
const size_t pad_length =
length + framer.GetControlFrameHeaderSize() -
(IsSpdy4() ? sizeof(kV4FrameData) : sizeof(kV3FrameData));
string pad('A', pad_length);
TestSpdyVisitor visitor(spdy_version_);
if (IsSpdy4()) {
visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData));
} else {
visitor.SimulateInFramer(kV3FrameData, sizeof(kV3FrameData));
}
visitor.SimulateInFramer(
reinterpret_cast<const unsigned char*>(pad.c_str()),
pad.length());
EXPECT_EQ(1, visitor.error_count_);
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
visitor.framer_.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
EXPECT_EQ(0, visitor.goaway_count_);
}
TEST_P(SpdyFramerTest, ReadZeroLenSettingsFrame) {
SpdyFramer framer(spdy_version_);
SpdySettingsIR settings_ir;
scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir));
SetFrameLength(control_frame.get(), 0, spdy_version_);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
framer.GetControlFrameHeaderSize());
if (spdy_version_ < 4) {
EXPECT_EQ(1, visitor.error_count_);
} else {
EXPECT_EQ(0, visitor.error_count_);
}
}
TEST_P(SpdyFramerTest, ReadBogusLenSettingsFrame) {
SpdyFramer framer(spdy_version_);
SpdySettingsIR settings_ir;
settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE,
false,
false,
0x00000002);
scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir));
const size_t kNewLength = 14;
SetFrameLength(control_frame.get(), kNewLength, spdy_version_);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
framer.GetControlFrameHeaderSize() + kNewLength);
EXPECT_EQ(1, visitor.error_count_);
}
TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) {
SpdyFramer framer(spdy_version_);
SpdySettingsIR settings_ir;
settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 1),
false,
false,
5);
settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 2),
false,
false,
6);
settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 3),
false,
false,
7);
scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir));
EXPECT_LT(SpdyFramer::kControlFrameBufferSize,
control_frame->size());
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(3, visitor.setting_count_);
if (spdy_version_ >= 4) {
EXPECT_EQ(1, visitor.settings_ack_sent_);
}
size_t framed_data = 0;
size_t unframed_data = control_frame->size();
size_t kReadChunkSize = 5;
while (unframed_data > 0) {
size_t to_read = min(kReadChunkSize, unframed_data);
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data() + framed_data),
to_read);
unframed_data -= to_read;
framed_data += to_read;
}
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(3 * 2, visitor.setting_count_);
if (spdy_version_ >= 4) {
EXPECT_EQ(2, visitor.settings_ack_sent_);
}
}
TEST_P(SpdyFramerTest, ReadDuplicateSettings) {
SpdyFramer framer(spdy_version_);
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x1C,
0x00, 0x00, 0x00, 0x03,
0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02,
0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x1C,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x03,
};
const unsigned char kV4FrameData[] = {
0x00, 0x0f, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00,
0x01,
0x00, 0x00, 0x00, 0x02,
0x01,
0x00, 0x00, 0x00, 0x03,
0x03,
0x00, 0x00, 0x00, 0x03,
};
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
if (IsSpdy2()) {
visitor.SimulateInFramer(kV2FrameData, sizeof(kV2FrameData));
} else if (IsSpdy3()) {
visitor.SimulateInFramer(kV3FrameData, sizeof(kV3FrameData));
} else {
visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData));
}
if (!IsSpdy4()) {
EXPECT_EQ(1, visitor.setting_count_);
EXPECT_EQ(1, visitor.error_count_);
} else {
EXPECT_EQ(3, visitor.setting_count_);
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.settings_ack_sent_);
}
}
TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) {
SpdyFramer framer(spdy_version_);
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x1C,
0x00, 0x00, 0x00, 0x03,
0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02,
0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x04,
0x00, 0x00, 0x00, 0x1C,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x01, 0x03,
0x00, 0x00, 0x00, 0x03,
};
const unsigned char kV4FrameData[] = {
0x00, 0x0f, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00,
0x02,
0x00, 0x00, 0x00, 0x02,
0x01,
0x00, 0x00, 0x00, 0x03,
0x03,
0x00, 0x00, 0x00, 0x03,
};
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
if (IsSpdy2()) {
visitor.SimulateInFramer(kV2FrameData, sizeof(kV2FrameData));
} else if (IsSpdy3()) {
visitor.SimulateInFramer(kV3FrameData, sizeof(kV3FrameData));
} else {
visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData));
}
if (!IsSpdy4()) {
EXPECT_EQ(1, visitor.setting_count_);
EXPECT_EQ(1, visitor.error_count_);
} else {
EXPECT_EQ(3, visitor.setting_count_);
EXPECT_EQ(0, visitor.error_count_);
}
}
TEST_P(SpdyFramerTest, ProcessSettingsAckFrame) {
if (spdy_version_ < 4) {
return;
}
SpdyFramer framer(spdy_version_);
const unsigned char kFrameData[] = {
0x00, 0x00, 0x04, 0x01,
0x00, 0x00, 0x00, 0x00,
};
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(kFrameData, sizeof(kFrameData));
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(0, visitor.setting_count_);
EXPECT_EQ(1, visitor.settings_ack_received_);
}
TEST_P(SpdyFramerTest, ProcessDataFrameWithPadding) {
if (spdy_version_ < 4) {
return;
}
const int kPaddingLen = 512;
const char data_payload[] = "hello";
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
SpdyDataIR data_ir(1, StringPiece(data_payload, strlen(data_payload)));
data_ir.set_padding_len(kPaddingLen);
scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
ASSERT_TRUE(frame.get() != NULL);
int bytes_consumed = 0;
EXPECT_CALL(visitor, OnDataFrameHeader(1,
kPaddingLen + strlen(data_payload),
false));
CHECK_EQ(8u, framer.ProcessInput(frame->data(), 8));
CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_PADDING_LENGTH);
CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
bytes_consumed += 8;
CHECK_EQ(1u, framer.ProcessInput(frame->data() + bytes_consumed, 1));
CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_PADDING_LENGTH);
CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
bytes_consumed += 1;
CHECK_EQ(1u, framer.ProcessInput(frame->data() + bytes_consumed, 1));
CHECK_EQ(framer.state(), SpdyFramer::SPDY_FORWARD_STREAM_FRAME);
CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
bytes_consumed += 1;
EXPECT_CALL(visitor, OnStreamFrameData(1, _, 2, false));
CHECK_EQ(2u, framer.ProcessInput(frame->data() + bytes_consumed, 2));
CHECK_EQ(framer.state(), SpdyFramer::SPDY_FORWARD_STREAM_FRAME);
CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
bytes_consumed += 2;
EXPECT_CALL(visitor, OnStreamFrameData(1, _, 3, false));
CHECK_EQ(3u, framer.ProcessInput(frame->data() + bytes_consumed, 3));
CHECK_EQ(framer.state(), SpdyFramer::SPDY_CONSUME_PADDING);
CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
bytes_consumed += 3;
EXPECT_CALL(visitor, OnStreamFrameData(1, NULL, 100, false));
CHECK_EQ(100u, framer.ProcessInput(frame->data() + bytes_consumed, 100));
CHECK_EQ(framer.state(), SpdyFramer::SPDY_CONSUME_PADDING);
CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
bytes_consumed += 100;
EXPECT_CALL(visitor, OnStreamFrameData(1, NULL, 410, false));
CHECK_EQ(410u, framer.ProcessInput(frame->data() + bytes_consumed, 410));
CHECK_EQ(framer.state(), SpdyFramer::SPDY_RESET);
CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
}
TEST_P(SpdyFramerTest, ReadWindowUpdate) {
SpdyFramer framer(spdy_version_);
scoped_ptr<SpdyFrame> control_frame(
framer.SerializeWindowUpdate(net::SpdyWindowUpdateIR(1, 2)));
TestSpdyVisitor visitor(spdy_version_);
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(control_frame->data()),
control_frame->size());
EXPECT_EQ(1u, visitor.last_window_update_stream_);
EXPECT_EQ(2u, visitor.last_window_update_delta_);
}
TEST_P(SpdyFramerTest, ReceiveCredentialFrame) {
if (!IsSpdy3()) {
return;
}
SpdyFramer framer(spdy_version_);
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x0A,
0x00, 0x00, 0x00, 0x33,
0x00, 0x03, 0x00, 0x00,
0x00, 0x05, 'p', 'r',
'o', 'o', 'f', 0x00,
0x00, 0x00, 0x06, 'a',
' ', 'c', 'e', 'r',
't', 0x00, 0x00, 0x00,
0x0C, 'a', 'n', 'o',
't', 'h', 'e', 'r',
' ', 'c', 'e', 'r',
't', 0x00, 0x00, 0x00,
0x0A, 'f', 'i', 'n',
'a', 'l', ' ', 'c',
'e', 'r', 't',
};
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(kV3FrameData, arraysize(kV3FrameData));
EXPECT_EQ(0, visitor.error_count_);
}
TEST_P(SpdyFramerTest, ReadCredentialFrameFollowedByAnotherFrame) {
if (!IsSpdy3()) {
return;
}
SpdyFramer framer(spdy_version_);
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x0A,
0x00, 0x00, 0x00, 0x33,
0x00, 0x03, 0x00, 0x00,
0x00, 0x05, 'p', 'r',
'o', 'o', 'f', 0x00,
0x00, 0x00, 0x06, 'a',
' ', 'c', 'e', 'r',
't', 0x00, 0x00, 0x00,
0x0C, 'a', 'n', 'o',
't', 'h', 'e', 'r',
' ', 'c', 'e', 'r',
't', 0x00, 0x00, 0x00,
0x0A, 'f', 'i', 'n',
'a', 'l', ' ', 'c',
'e', 'r', 't',
};
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
string multiple_frame_data(reinterpret_cast<const char*>(kV3FrameData),
arraysize(kV3FrameData));
scoped_ptr<SpdyFrame> control_frame(
framer.SerializeWindowUpdate(net::SpdyWindowUpdateIR(1, 2)));
multiple_frame_data.append(string(control_frame->data(),
control_frame->size()));
visitor.SimulateInFramer(
reinterpret_cast<unsigned const char*>(multiple_frame_data.data()),
multiple_frame_data.length());
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1u, visitor.last_window_update_stream_);
EXPECT_EQ(2u, visitor.last_window_update_delta_);
}
TEST_P(SpdyFramerTest, CreateContinuation) {
if (spdy_version_ < SPDY4) {
return;
}
SpdyFramer framer(spdy_version_);
const char kDescription[] = "CONTINUATION frame";
const unsigned char kFrameData[] = {
0x00, 0x12, 0x09, 0x00,
0x00, 0x00, 0x00, 0x2a,
0x40, 0x03, 0x62, 0x61,
0x72, 0x03, 0x66, 0x6f,
0x6f, 0x40, 0x03, 0x66,
0x6f, 0x6f, 0x03, 0x62,
0x61, 0x72,
};
SpdyContinuationIR continuation(42);
continuation.SetHeader("bar", "foo");
continuation.SetHeader("foo", "bar");
scoped_ptr<SpdySerializedFrame> frame(
framer.SerializeContinuation(continuation));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
}
TEST_P(SpdyFramerTest, ReadCompressedPushPromise) {
if (spdy_version_ < 4) {
return;
}
SpdyFramer framer(spdy_version_);
SpdyPushPromiseIR push_promise(42, 57);
push_promise.SetHeader("foo", "bar");
push_promise.SetHeader("bar", "foofoo");
SpdyHeaderBlock headers = push_promise.name_value_block();
scoped_ptr<SpdySerializedFrame> frame(
framer.SerializePushPromise(push_promise));
EXPECT_TRUE(frame.get() != NULL);
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = true;
visitor.SimulateInFramer(
reinterpret_cast<unsigned char*>(frame->data()),
frame->size());
EXPECT_EQ(42u, visitor.last_push_promise_stream_);
EXPECT_EQ(57u, visitor.last_push_promise_promised_stream_);
EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
}
TEST_P(SpdyFramerTest, ReadHeadersWithContinuation) {
if (spdy_version_ < 4) {
return;
}
const unsigned char kInput[] = {
0x00, 0x10, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x07, 0x66, 0x6f, 0x6f,
0x3d, 0x62, 0x61, 0x72,
0x00, 0x14, 0x09, 0x00,
0x00, 0x00, 0x00, 0x01,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x08, 0x62, 0x61, 0x7a,
0x3d, 0x62, 0x69, 0x6e,
0x67, 0x40, 0x06, 0x43,
0x00, 0x12, 0x09, 0x04,
0x00, 0x00, 0x00, 0x01,
0x6f, 0x6f, 0x6b, 0x69,
0x65, 0x00, 0x40, 0x04,
0x6e, 0x61, 0x6d, 0x65,
0x05, 0x76, 0x61, 0x6c,
0x75, 0x65,
};
SpdyFramer framer(spdy_version_);
TestSpdyVisitor visitor(spdy_version_);
visitor.SimulateInFramer(kInput, sizeof(kInput));
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.headers_frame_count_);
EXPECT_EQ(2, visitor.continuation_count_);
EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
EXPECT_THAT(visitor.headers_, ElementsAre(
Pair("Cookie", "foo=bar; baz=bing; "),
Pair("name", "value")));
}
TEST_P(SpdyFramerTest, ReadHeadersWithContinuationAndFin) {
if (spdy_version_ < 4) {
return;
}
const unsigned char kInput[] = {
0x00, 0x10, 0x01, 0x01,
0x00, 0x00, 0x00, 0x01,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x07, 0x66, 0x6f, 0x6f,
0x3d, 0x62, 0x61, 0x72,
0x00, 0x14, 0x09, 0x00,
0x00, 0x00, 0x00, 0x01,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x08, 0x62, 0x61, 0x7a,
0x3d, 0x62, 0x69, 0x6e,
0x67, 0x40, 0x06, 0x43,
0x00, 0x12, 0x09, 0x04,
0x00, 0x00, 0x00, 0x01,
0x6f, 0x6f, 0x6b, 0x69,
0x65, 0x00, 0x40, 0x04,
0x6e, 0x61, 0x6d, 0x65,
0x05, 0x76, 0x61, 0x6c,
0x75, 0x65,
};
SpdyFramer framer(spdy_version_);
TestSpdyVisitor visitor(spdy_version_);
visitor.SimulateInFramer(kInput, sizeof(kInput));
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.headers_frame_count_);
EXPECT_EQ(2, visitor.continuation_count_);
EXPECT_EQ(1, visitor.fin_flag_count_);
EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
EXPECT_THAT(visitor.headers_, ElementsAre(
Pair("Cookie", "foo=bar; baz=bing; "),
Pair("name", "value")));
}
TEST_P(SpdyFramerTest, ReadPushPromiseWithContinuation) {
if (spdy_version_ < 4) {
return;
}
const unsigned char kInput[] = {
0x00, 0x14, 0x05, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x2A,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x07, 0x66, 0x6f, 0x6f,
0x3d, 0x62, 0x61, 0x72,
0x00, 0x14, 0x09, 0x00,
0x00, 0x00, 0x00, 0x01,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x08, 0x62, 0x61, 0x7a,
0x3d, 0x62, 0x69, 0x6e,
0x67, 0x40, 0x06, 0x43,
0x00, 0x12, 0x09, 0x04,
0x00, 0x00, 0x00, 0x01,
0x6f, 0x6f, 0x6b, 0x69,
0x65, 0x00, 0x40, 0x04,
0x6e, 0x61, 0x6d, 0x65,
0x05, 0x76, 0x61, 0x6c,
0x75, 0x65,
};
SpdyFramer framer(spdy_version_);
TestSpdyVisitor visitor(spdy_version_);
visitor.SimulateInFramer(kInput, sizeof(kInput));
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1u, visitor.last_push_promise_stream_);
EXPECT_EQ(42u, visitor.last_push_promise_promised_stream_);
EXPECT_EQ(2, visitor.continuation_count_);
EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
EXPECT_THAT(visitor.headers_, ElementsAre(
Pair("Cookie", "foo=bar; baz=bing; "),
Pair("name", "value")));
}
TEST_P(SpdyFramerTest, ReadContinuationWithWrongStreamId) {
if (spdy_version_ < 4) {
return;
}
const unsigned char kInput[] = {
0x00, 0x10, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x07, 0x66, 0x6f, 0x6f,
0x3d, 0x62, 0x61, 0x72,
0x00, 0x14, 0x09, 0x00,
0x00, 0x00, 0x00, 0x02,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x08, 0x62, 0x61, 0x7a,
0x3d, 0x62, 0x69, 0x6e,
0x67, 0x40, 0x06, 0x43,
};
SpdyFramer framer(spdy_version_);
TestSpdyVisitor visitor(spdy_version_);
framer.set_visitor(&visitor);
visitor.SimulateInFramer(kInput, sizeof(kInput));
EXPECT_EQ(1, visitor.error_count_);
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
visitor.framer_.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
EXPECT_EQ(1, visitor.headers_frame_count_);
EXPECT_EQ(0, visitor.continuation_count_);
EXPECT_EQ(0u, visitor.header_buffer_length_);
}
TEST_P(SpdyFramerTest, ReadContinuationOutOfOrder) {
if (spdy_version_ < 4) {
return;
}
const unsigned char kInput[] = {
0x00, 0x10, 0x09, 0x00,
0x00, 0x00, 0x00, 0x01,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x07, 0x66, 0x6f, 0x6f,
0x3d, 0x62, 0x61, 0x72,
};
SpdyFramer framer(spdy_version_);
TestSpdyVisitor visitor(spdy_version_);
framer.set_visitor(&visitor);
visitor.SimulateInFramer(kInput, sizeof(kInput));
EXPECT_EQ(1, visitor.error_count_);
EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME,
visitor.framer_.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
EXPECT_EQ(0, visitor.continuation_count_);
EXPECT_EQ(0u, visitor.header_buffer_length_);
}
TEST_P(SpdyFramerTest, ExpectContinuationReceiveData) {
if (spdy_version_ < 4) {
return;
}
const unsigned char kInput[] = {
0x00, 0x10, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x07, 0x66, 0x6f, 0x6f,
0x3d, 0x62, 0x61, 0x72,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x04,
0xde, 0xad, 0xbe, 0xef,
};
SpdyFramer framer(spdy_version_);
TestSpdyVisitor visitor(spdy_version_);
framer.set_visitor(&visitor);
visitor.SimulateInFramer(kInput, sizeof(kInput));
EXPECT_EQ(1, visitor.error_count_);
EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME,
visitor.framer_.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
EXPECT_EQ(1, visitor.headers_frame_count_);
EXPECT_EQ(0, visitor.continuation_count_);
EXPECT_EQ(0u, visitor.header_buffer_length_);
EXPECT_EQ(0, visitor.data_frame_count_);
}
TEST_P(SpdyFramerTest, ExpectContinuationReceiveControlFrame) {
if (spdy_version_ < 4) {
return;
}
const unsigned char kInput[] = {
0x00, 0x10, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x07, 0x66, 0x6f, 0x6f,
0x3d, 0x62, 0x61, 0x72,
0x00, 0x14, 0x08, 0x00,
0x00, 0x00, 0x00, 0x01,
0x40, 0x06, 0x43, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x08, 0x62, 0x61, 0x7a,
0x3d, 0x62, 0x69, 0x6e,
0x67, 0x40, 0x06, 0x43,
};
SpdyFramer framer(spdy_version_);
TestSpdyVisitor visitor(spdy_version_);
framer.set_visitor(&visitor);
visitor.SimulateInFramer(kInput, sizeof(kInput));
EXPECT_EQ(1, visitor.error_count_);
EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME,
visitor.framer_.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
EXPECT_EQ(1, visitor.headers_frame_count_);
EXPECT_EQ(0, visitor.continuation_count_);
EXPECT_EQ(0u, visitor.header_buffer_length_);
EXPECT_EQ(0, visitor.data_frame_count_);
}
TEST_P(SpdyFramerTest, ReadGarbage) {
SpdyFramer framer(spdy_version_);
unsigned char garbage_frame[256];
memset(garbage_frame, ~0, sizeof(garbage_frame));
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(garbage_frame, sizeof(garbage_frame));
EXPECT_EQ(1, visitor.error_count_);
}
TEST_P(SpdyFramerTest, ReadGarbageWithValidLength) {
if (!IsSpdy4()) {
return;
}
SpdyFramer framer(spdy_version_);
const unsigned char kFrameData[] = {
0x00, 0x10, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
};
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(kFrameData, arraysize(kFrameData));
EXPECT_EQ(1, visitor.error_count_);
}
TEST_P(SpdyFramerTest, ReadGarbageWithValidVersion) {
if (IsSpdy4()) {
return;
}
SpdyFramer framer(spdy_version_);
const unsigned char kFrameData[] = {
0x80, spdy_version_ch_, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
};
TestSpdyVisitor visitor(spdy_version_);
visitor.use_compression_ = false;
visitor.SimulateInFramer(kFrameData, arraysize(kFrameData));
EXPECT_EQ(1, visitor.error_count_);
}
TEST_P(SpdyFramerTest, SizesTest) {
SpdyFramer framer(spdy_version_);
EXPECT_EQ(8u, framer.GetDataFrameMinimumSize());
if (IsSpdy4()) {
EXPECT_EQ(8u, framer.GetSynReplyMinimumSize());
EXPECT_EQ(12u, framer.GetRstStreamMinimumSize());
EXPECT_EQ(8u, framer.GetSettingsMinimumSize());
EXPECT_EQ(16u, framer.GetPingSize());
EXPECT_EQ(16u, framer.GetGoAwayMinimumSize());
EXPECT_EQ(8u, framer.GetHeadersMinimumSize());
EXPECT_EQ(12u, framer.GetWindowUpdateSize());
EXPECT_EQ(8u, framer.GetBlockedSize());
EXPECT_EQ(12u, framer.GetPushPromiseMinimumSize());
EXPECT_EQ(8u, framer.GetFrameMinimumSize());
EXPECT_EQ(16383u, framer.GetFrameMaximumSize());
EXPECT_EQ(16375u, framer.GetDataFrameMaximumPayload());
} else {
EXPECT_EQ(8u, framer.GetControlFrameHeaderSize());
EXPECT_EQ(18u, framer.GetSynStreamMinimumSize());
EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetSynReplyMinimumSize());
EXPECT_EQ(16u, framer.GetRstStreamMinimumSize());
EXPECT_EQ(12u, framer.GetSettingsMinimumSize());
EXPECT_EQ(12u, framer.GetPingSize());
EXPECT_EQ(IsSpdy2() ? 12u : 16u, framer.GetGoAwayMinimumSize());
EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetHeadersMinimumSize());
EXPECT_EQ(16u, framer.GetWindowUpdateSize());
EXPECT_EQ(8u, framer.GetFrameMinimumSize());
EXPECT_EQ(16777223u, framer.GetFrameMaximumSize());
EXPECT_EQ(16777215u, framer.GetDataFrameMaximumPayload());
}
}
TEST_P(SpdyFramerTest, StateToStringTest) {
EXPECT_STREQ("ERROR",
SpdyFramer::StateToString(SpdyFramer::SPDY_ERROR));
EXPECT_STREQ("AUTO_RESET",
SpdyFramer::StateToString(SpdyFramer::SPDY_AUTO_RESET));
EXPECT_STREQ("RESET",
SpdyFramer::StateToString(SpdyFramer::SPDY_RESET));
EXPECT_STREQ("READING_COMMON_HEADER",
SpdyFramer::StateToString(
SpdyFramer::SPDY_READING_COMMON_HEADER));
EXPECT_STREQ("CONTROL_FRAME_PAYLOAD",
SpdyFramer::StateToString(
SpdyFramer::SPDY_CONTROL_FRAME_PAYLOAD));
EXPECT_STREQ("IGNORE_REMAINING_PAYLOAD",
SpdyFramer::StateToString(
SpdyFramer::SPDY_IGNORE_REMAINING_PAYLOAD));
EXPECT_STREQ("FORWARD_STREAM_FRAME",
SpdyFramer::StateToString(
SpdyFramer::SPDY_FORWARD_STREAM_FRAME));
EXPECT_STREQ("SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK",
SpdyFramer::StateToString(
SpdyFramer::SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK));
EXPECT_STREQ("SPDY_CONTROL_FRAME_HEADER_BLOCK",
SpdyFramer::StateToString(
SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK));
EXPECT_STREQ("SPDY_SETTINGS_FRAME_PAYLOAD",
SpdyFramer::StateToString(
SpdyFramer::SPDY_SETTINGS_FRAME_PAYLOAD));
EXPECT_STREQ("UNKNOWN_STATE",
SpdyFramer::StateToString(
SpdyFramer::SPDY_SETTINGS_FRAME_PAYLOAD + 1));
}
TEST_P(SpdyFramerTest, ErrorCodeToStringTest) {
EXPECT_STREQ("NO_ERROR",
SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_NO_ERROR));
EXPECT_STREQ("INVALID_CONTROL_FRAME",
SpdyFramer::ErrorCodeToString(
SpdyFramer::SPDY_INVALID_CONTROL_FRAME));
EXPECT_STREQ("CONTROL_PAYLOAD_TOO_LARGE",
SpdyFramer::ErrorCodeToString(
SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE));
EXPECT_STREQ("ZLIB_INIT_FAILURE",
SpdyFramer::ErrorCodeToString(
SpdyFramer::SPDY_ZLIB_INIT_FAILURE));
EXPECT_STREQ("UNSUPPORTED_VERSION",
SpdyFramer::ErrorCodeToString(
SpdyFramer::SPDY_UNSUPPORTED_VERSION));
EXPECT_STREQ("DECOMPRESS_FAILURE",
SpdyFramer::ErrorCodeToString(
SpdyFramer::SPDY_DECOMPRESS_FAILURE));
EXPECT_STREQ("COMPRESS_FAILURE",
SpdyFramer::ErrorCodeToString(
SpdyFramer::SPDY_COMPRESS_FAILURE));
EXPECT_STREQ("SPDY_INVALID_DATA_FRAME_FLAGS",
SpdyFramer::ErrorCodeToString(
SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS));
EXPECT_STREQ("SPDY_INVALID_CONTROL_FRAME_FLAGS",
SpdyFramer::ErrorCodeToString(
SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS));
EXPECT_STREQ("UNKNOWN_ERROR",
SpdyFramer::ErrorCodeToString(SpdyFramer::LAST_ERROR));
}
TEST_P(SpdyFramerTest, StatusCodeToStringTest) {
EXPECT_STREQ("INVALID",
SpdyFramer::StatusCodeToString(RST_STREAM_INVALID));
EXPECT_STREQ("PROTOCOL_ERROR",
SpdyFramer::StatusCodeToString(RST_STREAM_PROTOCOL_ERROR));
EXPECT_STREQ("INVALID_STREAM",
SpdyFramer::StatusCodeToString(RST_STREAM_INVALID_STREAM));
EXPECT_STREQ("REFUSED_STREAM",
SpdyFramer::StatusCodeToString(RST_STREAM_REFUSED_STREAM));
EXPECT_STREQ("UNSUPPORTED_VERSION",
SpdyFramer::StatusCodeToString(RST_STREAM_UNSUPPORTED_VERSION));
EXPECT_STREQ("CANCEL",
SpdyFramer::StatusCodeToString(RST_STREAM_CANCEL));
EXPECT_STREQ("INTERNAL_ERROR",
SpdyFramer::StatusCodeToString(RST_STREAM_INTERNAL_ERROR));
EXPECT_STREQ("FLOW_CONTROL_ERROR",
SpdyFramer::StatusCodeToString(RST_STREAM_FLOW_CONTROL_ERROR));
EXPECT_STREQ("UNKNOWN_STATUS",
SpdyFramer::StatusCodeToString(RST_STREAM_NUM_STATUS_CODES));
}
TEST_P(SpdyFramerTest, FrameTypeToStringTest) {
EXPECT_STREQ("DATA",
SpdyFramer::FrameTypeToString(DATA));
EXPECT_STREQ("SYN_STREAM",
SpdyFramer::FrameTypeToString(SYN_STREAM));
EXPECT_STREQ("SYN_REPLY",
SpdyFramer::FrameTypeToString(SYN_REPLY));
EXPECT_STREQ("RST_STREAM",
SpdyFramer::FrameTypeToString(RST_STREAM));
EXPECT_STREQ("SETTINGS",
SpdyFramer::FrameTypeToString(SETTINGS));
EXPECT_STREQ("NOOP",
SpdyFramer::FrameTypeToString(NOOP));
EXPECT_STREQ("PING",
SpdyFramer::FrameTypeToString(PING));
EXPECT_STREQ("GOAWAY",
SpdyFramer::FrameTypeToString(GOAWAY));
EXPECT_STREQ("HEADERS",
SpdyFramer::FrameTypeToString(HEADERS));
EXPECT_STREQ("WINDOW_UPDATE",
SpdyFramer::FrameTypeToString(WINDOW_UPDATE));
EXPECT_STREQ("PUSH_PROMISE",
SpdyFramer::FrameTypeToString(PUSH_PROMISE));
EXPECT_STREQ("CREDENTIAL",
SpdyFramer::FrameTypeToString(CREDENTIAL));
EXPECT_STREQ("CONTINUATION",
SpdyFramer::FrameTypeToString(CONTINUATION));
}
TEST_P(SpdyFramerTest, CatchProbableHttpResponse) {
if (IsSpdy4()) {
return;
}
{
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
EXPECT_CALL(visitor, OnError(_));
framer.ProcessInput("HTTP/1.1", 8);
EXPECT_TRUE(framer.probable_http_response());
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
{
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
EXPECT_CALL(visitor, OnError(_));
framer.ProcessInput("HTTP/1.0", 8);
EXPECT_TRUE(framer.probable_http_response());
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
TEST_P(SpdyFramerTest, DataFrameFlagsV2V3) {
if (spdy_version_ >= 4) {
return;
}
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
net::SpdyDataIR data_ir(1, StringPiece("hello", 5));
scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags & ~DATA_FLAG_FIN) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(visitor, OnDataFrameHeader(1, 5, flags & DATA_FLAG_FIN));
EXPECT_CALL(visitor, OnStreamFrameData(_, _, 5, false));
if (flags & DATA_FLAG_FIN) {
EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
}
}
framer.ProcessInput(frame->data(), frame->size());
if (flags & ~DATA_FLAG_FIN) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, DataFrameFlagsV4) {
if (spdy_version_ < 4) {
return;
}
uint8 valid_data_flags = DATA_FLAG_FIN | DATA_FLAG_END_SEGMENT |
DATA_FLAG_PAD_LOW | DATA_FLAG_PAD_HIGH;
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
net::SpdyDataIR data_ir(1, StringPiece("hello", 5));
scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags & ~valid_data_flags) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(visitor, OnDataFrameHeader(1, 5, flags & DATA_FLAG_FIN));
if ((flags & DATA_FLAG_PAD_LOW) || (flags & DATA_FLAG_PAD_HIGH)) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(visitor, OnStreamFrameData(_, _, 5, false));
if (flags & DATA_FLAG_FIN) {
EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
}
}
}
framer.ProcessInput(frame->data(), frame->size());
if ((flags & ~valid_data_flags) || (flags & DATA_FLAG_PAD_LOW) ||
(flags & DATA_FLAG_PAD_HIGH)) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, SynStreamFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
testing::StrictMock<test::MockDebugVisitor> debug_visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
framer.set_debug_visitor(&debug_visitor);
EXPECT_CALL(debug_visitor, OnSendCompressedFrame(8, SYN_STREAM, _, _));
SpdySynStreamIR syn_stream(8);
syn_stream.set_associated_to_stream_id(3);
syn_stream.set_priority(1);
syn_stream.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
int set_flags = flags;
if (IsSpdy4()) {
set_flags |= HEADERS_FLAG_PRIORITY;
}
SetFrameFlags(frame.get(), set_flags, spdy_version_);
if (!IsSpdy4() &&
flags & ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) {
EXPECT_CALL(visitor, OnError(_));
} else if (IsSpdy4() &&
flags & ~(CONTROL_FLAG_FIN |
HEADERS_FLAG_PRIORITY |
HEADERS_FLAG_END_HEADERS)) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(8, SYN_STREAM, _));
if (IsSpdy4()) {
EXPECT_CALL(visitor, OnSynStream(8, 0, 1, flags & CONTROL_FLAG_FIN,
false));
} else {
EXPECT_CALL(visitor, OnSynStream(8, 3, 1, flags & CONTROL_FLAG_FIN,
flags & CONTROL_FLAG_UNIDIRECTIONAL));
}
EXPECT_CALL(visitor, OnControlFrameHeaderData(8, _, _))
.WillRepeatedly(testing::Return(true));
if (flags & DATA_FLAG_FIN && (!IsSpdy4() ||
flags & HEADERS_FLAG_END_HEADERS)) {
EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
} else {
EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true)).Times(0);
}
}
framer.ProcessInput(frame->data(), frame->size());
if (!IsSpdy4() &&
flags & ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else if (IsSpdy4() &&
flags & ~(CONTROL_FLAG_FIN |
HEADERS_FLAG_PRIORITY |
HEADERS_FLAG_END_HEADERS)) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, SynReplyFrameFlags) {
if (IsSpdy4()) {
return;
}
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
SpdySynReplyIR syn_reply(37);
syn_reply.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeSynReply(syn_reply));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags & ~CONTROL_FLAG_FIN) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(visitor, OnSynReply(37, flags & CONTROL_FLAG_FIN));
EXPECT_CALL(visitor, OnControlFrameHeaderData(37, _, _))
.WillRepeatedly(testing::Return(true));
if (flags & DATA_FLAG_FIN) {
EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
}
}
framer.ProcessInput(frame->data(), frame->size());
if (flags & ~CONTROL_FLAG_FIN) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, RstStreamFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
net::SpdyRstStreamIR rst_stream(13, RST_STREAM_CANCEL, "");
scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags != 0) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(visitor, OnRstStream(13, RST_STREAM_CANCEL));
}
framer.ProcessInput(frame->data(), frame->size());
if (flags != 0) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, SettingsFrameFlagsOldFormat) {
if (spdy_version_ >= 4) { return; }
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
SpdySettingsIR settings_ir;
settings_ir.AddSetting(SETTINGS_UPLOAD_BANDWIDTH,
false,
false,
54321);
scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags & ~SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(visitor, OnSettings(
flags & SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS));
EXPECT_CALL(visitor, OnSetting(SETTINGS_UPLOAD_BANDWIDTH,
SETTINGS_FLAG_NONE, 54321));
EXPECT_CALL(visitor, OnSettingsEnd());
}
framer.ProcessInput(frame->data(), frame->size());
if (flags & ~SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, SettingsFrameFlags) {
if (spdy_version_ < 4) { return; }
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
SpdySettingsIR settings_ir;
settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0, 0, 16);
scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags != 0) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(visitor, OnSettings(flags & SETTINGS_FLAG_ACK));
EXPECT_CALL(visitor, OnSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0, 16));
EXPECT_CALL(visitor, OnSettingsEnd());
}
framer.ProcessInput(frame->data(), frame->size());
if (flags & ~SETTINGS_FLAG_ACK) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else if (flags & SETTINGS_FLAG_ACK) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, GoawayFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
SpdyGoAwayIR goaway_ir(97, GOAWAY_OK, "test");
scoped_ptr<SpdyFrame> frame(framer.SerializeGoAway(goaway_ir));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags != 0) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(visitor, OnGoAway(97, GOAWAY_OK));
}
framer.ProcessInput(frame->data(), frame->size());
if (flags != 0) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, HeadersFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
if (IsSpdy4() && flags & HEADERS_FLAG_PRIORITY) {
continue;
}
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
SpdyHeadersIR headers_ir(57);
headers_ir.SetHeader("foo", "bar");
scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (!IsSpdy4() && flags & ~CONTROL_FLAG_FIN) {
EXPECT_CALL(visitor, OnError(_));
} else if (IsSpdy4() && flags & ~(CONTROL_FLAG_FIN |
HEADERS_FLAG_END_HEADERS)) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(visitor, OnHeaders(57,
flags & CONTROL_FLAG_FIN,
(flags & HEADERS_FLAG_END_HEADERS) ||
!IsSpdy4()));
EXPECT_CALL(visitor, OnControlFrameHeaderData(57, _, _))
.WillRepeatedly(testing::Return(true));
if (flags & DATA_FLAG_FIN && (!IsSpdy4() ||
flags & HEADERS_FLAG_END_HEADERS)) {
EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
} else {
EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true)).Times(0);
}
}
framer.ProcessInput(frame->data(), frame->size());
if (!IsSpdy4() && flags & ~CONTROL_FLAG_FIN) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else if (IsSpdy4() && flags & ~(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY |
HEADERS_FLAG_END_HEADERS)) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else if (IsSpdy4() && ~(flags & HEADERS_FLAG_END_HEADERS)) {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, PingFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
scoped_ptr<SpdyFrame> frame(framer.SerializePing(SpdyPingIR(42)));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (spdy_version_ >= SPDY4 &&
flags == PING_FLAG_ACK) {
EXPECT_CALL(visitor, OnPing(42, true));
} else if (flags == 0) {
EXPECT_CALL(visitor, OnPing(42, false));
} else {
EXPECT_CALL(visitor, OnError(_));
}
framer.ProcessInput(frame->data(), frame->size());
if ((spdy_version_ >= SPDY4 && flags == PING_FLAG_ACK) ||
flags == 0) {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, WindowUpdateFrameFlags) {
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
net::SpdyWindowUpdateIR(4, 1024)));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags != 0) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(visitor, OnWindowUpdate(4, 1024));
}
framer.ProcessInput(frame->data(), frame->size());
if (flags != 0) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, PushPromiseFrameFlags) {
if (spdy_version_ < SPDY4) {
return;
}
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<net::test::MockSpdyFramerVisitor> visitor;
testing::StrictMock<net::test::MockDebugVisitor> debug_visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
framer.set_debug_visitor(&debug_visitor);
EXPECT_CALL(debug_visitor, OnSendCompressedFrame(42, PUSH_PROMISE, _, _));
SpdyPushPromiseIR push_promise(42, 57);
push_promise.SetHeader("foo", "bar");
scoped_ptr<SpdySerializedFrame> frame(
framer.SerializePushPromise(push_promise));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags & ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE)) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, PUSH_PROMISE, _));
EXPECT_CALL(visitor, OnPushPromise(42, 57,
flags & PUSH_PROMISE_FLAG_END_PUSH_PROMISE));
EXPECT_CALL(visitor, OnControlFrameHeaderData(42, _, _))
.WillRepeatedly(testing::Return(true));
}
framer.ProcessInput(frame->data(), frame->size());
if (flags & ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE)) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, ContinuationFrameFlags) {
if (spdy_version_ < SPDY4) {
return;
}
for (int flags = 0; flags < 256; ++flags) {
SCOPED_TRACE(testing::Message() << "Flags " << flags);
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
testing::StrictMock<net::test::MockDebugVisitor> debug_visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
framer.set_debug_visitor(&debug_visitor);
EXPECT_CALL(debug_visitor, OnSendCompressedFrame(42, HEADERS, _, _));
EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, HEADERS, _));
EXPECT_CALL(visitor, OnHeaders(42, 0, false));
EXPECT_CALL(visitor, OnControlFrameHeaderData(42, _, _))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(debug_visitor, OnSendCompressedFrame(42, CONTINUATION, _, _));
SpdyHeadersIR headers_ir(42);
headers_ir.SetHeader("foo", "bar");
headers_ir.set_end_headers(false);
scoped_ptr<SpdyFrame> frame0(framer.SerializeHeaders(headers_ir));
SpdyContinuationIR continuation(42);
continuation.SetHeader("foo", "bar");
scoped_ptr<SpdySerializedFrame> frame(
framer.SerializeContinuation(continuation));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags & ~(HEADERS_FLAG_END_HEADERS)) {
EXPECT_CALL(visitor, OnError(_));
} else {
EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, CONTINUATION, _));
EXPECT_CALL(visitor, OnContinuation(42,
flags & HEADERS_FLAG_END_HEADERS));
EXPECT_CALL(visitor, OnControlFrameHeaderData(42, _, _))
.WillRepeatedly(testing::Return(true));
}
framer.ProcessInput(frame0->data(), frame0->size());
framer.ProcessInput(frame->data(), frame->size());
if (flags & ~(HEADERS_FLAG_END_HEADERS)) {
EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
} else {
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}
}
TEST_P(SpdyFramerTest, EmptySynStream) {
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
testing::StrictMock<test::MockDebugVisitor> debug_visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
framer.set_debug_visitor(&debug_visitor);
EXPECT_CALL(debug_visitor, OnSendCompressedFrame(1, SYN_STREAM, _, _));
SpdySynStreamIR syn_stream(1);
syn_stream.set_priority(1);
scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
SetFrameLength(
frame.get(),
framer.GetSynStreamMinimumSize() - framer.GetControlFrameHeaderSize(),
spdy_version_);
EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(1, SYN_STREAM, _));
EXPECT_CALL(visitor, OnSynStream(1, 0, 1, false, false));
EXPECT_CALL(visitor, OnControlFrameHeaderData(1, NULL, 0));
framer.ProcessInput(frame->data(), framer.GetSynStreamMinimumSize());
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
TEST_P(SpdyFramerTest, SettingsFlagsAndId) {
const uint32 kId = 0x020304;
const uint32 kFlags = 0x01;
const uint32 kWireFormat = htonl(IsSpdy2() ? 0x04030201 : 0x01020304);
SettingsFlagsAndId id_and_flags =
SettingsFlagsAndId::FromWireFormat(spdy_version_, kWireFormat);
EXPECT_EQ(kId, id_and_flags.id());
EXPECT_EQ(kFlags, id_and_flags.flags());
EXPECT_EQ(kWireFormat, id_and_flags.GetWireFormat(spdy_version_));
}
TEST_P(SpdyFramerTest, RstStreamStatusBounds) {
DCHECK_GE(0xff, RST_STREAM_NUM_STATUS_CODES);
const unsigned char kV3RstStreamInvalid[] = {
0x80, spdy_version_ch_, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, RST_STREAM_INVALID
};
const unsigned char kV4RstStreamInvalid[] = {
0x00, 0x04, 0x03, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, RST_STREAM_INVALID
};
const unsigned char kV3RstStreamNumStatusCodes[] = {
0x80, spdy_version_ch_, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, RST_STREAM_NUM_STATUS_CODES
};
const unsigned char kV4RstStreamNumStatusCodes[] = {
0x00, 0x04, 0x03, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, RST_STREAM_NUM_STATUS_CODES
};
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
if (IsSpdy4()) {
framer.ProcessInput(reinterpret_cast<const char*>(kV4RstStreamInvalid),
arraysize(kV4RstStreamInvalid));
} else {
framer.ProcessInput(reinterpret_cast<const char*>(kV3RstStreamInvalid),
arraysize(kV3RstStreamInvalid));
}
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
if (IsSpdy4()) {
framer.ProcessInput(
reinterpret_cast<const char*>(kV4RstStreamNumStatusCodes),
arraysize(kV4RstStreamNumStatusCodes));
} else {
framer.ProcessInput(
reinterpret_cast<const char*>(kV3RstStreamNumStatusCodes),
arraysize(kV3RstStreamNumStatusCodes));
}
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
TEST_P(SpdyFramerTest, GoAwayStreamIdBounds) {
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x04,
0xff, 0xff, 0xff, 0xff,
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x08,
0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00,
};
const unsigned char kV4FrameData[] = {
0x00, 0x08, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00,
};
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
EXPECT_CALL(visitor, OnGoAway(0x7fffffff, GOAWAY_OK));
if (IsSpdy2()) {
framer.ProcessInput(reinterpret_cast<const char*>(kV2FrameData),
arraysize(kV2FrameData));
} else if (IsSpdy3()) {
framer.ProcessInput(reinterpret_cast<const char*>(kV3FrameData),
arraysize(kV3FrameData));
} else {
framer.ProcessInput(reinterpret_cast<const char*>(kV4FrameData),
arraysize(kV4FrameData));
}
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
TEST_P(SpdyFramerTest, OnBlocked) {
if (spdy_version_ < SPDY4) {
return;
}
const SpdyStreamId kStreamId = 0;
testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
EXPECT_CALL(visitor, OnBlocked(kStreamId));
SpdyBlockedIR blocked_ir(0);
scoped_ptr<SpdySerializedFrame> frame(framer.SerializeFrame(blocked_ir));
framer.ProcessInput(frame->data(), framer.GetBlockedSize());
EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
<< SpdyFramer::ErrorCodeToString(framer.error_code());
}
}