diff options
Diffstat (limited to 'chromium/net/spdy/spdy_framer_test.cc')
-rw-r--r-- | chromium/net/spdy/spdy_framer_test.cc | 3612 |
1 files changed, 2241 insertions, 1371 deletions
diff --git a/chromium/net/spdy/spdy_framer_test.cc b/chromium/net/spdy/spdy_framer_test.cc index f9c25036097..b6c5041934d 100644 --- a/chromium/net/spdy/spdy_framer_test.cc +++ b/chromium/net/spdy/spdy_framer_test.cc @@ -8,17 +8,23 @@ #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_frame_reader.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 { @@ -27,46 +33,6 @@ namespace test { static const size_t kMaxDecompressedSize = 1024; -// TODO(akalin): Make sure expectations on mocks are set before mock -// functions are called, as interleaving expectations and calls is -// undefined. -class MockVisitor : public SpdyFramerVisitorInterface { - public: - MOCK_METHOD1(OnError, void(SpdyFramer* framer)); - MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId stream_id, - size_t length, - bool fin)); - MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId stream_id, - const char* data, - size_t len, - bool fin)); - MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id, - const char* header_data, - size_t len)); - MOCK_METHOD6(OnSynStream, void(SpdyStreamId stream_id, - SpdyStreamId associated_stream_id, - SpdyPriority priority, - uint8 slot, - bool fin, - bool unidirectional)); - MOCK_METHOD2(OnSynReply, void(SpdyStreamId stream_id, bool fin)); - MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id, - SpdyRstStreamStatus status)); - MOCK_METHOD1(OnSettings, void(bool clear_persisted)); - MOCK_METHOD3(OnSetting, void(SpdySettingsIds id, uint8 flags, uint32 value)); - MOCK_METHOD1(OnPing, void(uint32 unique_id)); - MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id, - SpdyGoAwayStatus status)); - MOCK_METHOD2(OnHeaders, void(SpdyStreamId stream_id, bool fin)); - MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id, - uint32 delta_window_size)); - MOCK_METHOD2(OnCredentialFrameData, bool(const char* credential_data, - size_t len)); - MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id)); - MOCK_METHOD2(OnPushPromise, void(SpdyStreamId stream_id, - SpdyStreamId promised_stream_id)); -}; - class MockDebugVisitor : public SpdyFramerDebugVisitorInterface { public: MOCK_METHOD4(OnSendCompressedFrame, void(SpdyStreamId stream_id, @@ -98,15 +64,9 @@ class SpdyFramerTestUtil { char* buffer = visitor.ReleaseBuffer(); CHECK(buffer != NULL); SpdyFrame* decompressed_frame = new SpdyFrame(buffer, visitor.size(), true); - if (framer->protocol_version() == 4) { - SetFrameLength(decompressed_frame, - visitor.size(), - framer->protocol_version()); - } else { - SetFrameLength(decompressed_frame, - visitor.size() - framer->GetControlFrameHeaderSize(), - framer->protocol_version()); - } + SetFrameLength(decompressed_frame, + visitor.size() - framer->GetControlFrameHeaderSize(), + framer->protocol_version()); return decompressed_frame; } @@ -154,26 +114,16 @@ class SpdyFramerTestUtil { virtual void OnSynStream(SpdyStreamId stream_id, SpdyStreamId associated_stream_id, SpdyPriority priority, - uint8 slot, bool fin, bool unidirectional) OVERRIDE { SpdyFramer framer(version_); framer.set_enable_compression(false); - const SpdyHeaderBlock null_headers; - int flags = CONTROL_FLAG_NONE; - if (fin) { - flags &= CONTROL_FLAG_FIN; - } - if (unidirectional) { - flags &= CONTROL_FLAG_UNIDIRECTIONAL; - } - scoped_ptr<SpdyFrame> frame( - framer.CreateSynStream(stream_id, - associated_stream_id, - priority, - slot, - static_cast<SpdyControlFlags>(flags), - &null_headers)); + 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(); @@ -182,15 +132,9 @@ class SpdyFramerTestUtil { virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE { SpdyFramer framer(version_); framer.set_enable_compression(false); - const SpdyHeaderBlock null_headers; - int flags = CONTROL_FLAG_NONE; - if (fin) { - flags &= CONTROL_FLAG_FIN; - } - scoped_ptr<SpdyFrame> frame( - framer.CreateHeaders(stream_id, - static_cast<SpdyControlFlags>(flags), - &null_headers)); + 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(); @@ -205,26 +149,23 @@ class SpdyFramerTestUtil { uint32 value) OVERRIDE { LOG(FATAL); } - virtual void OnPing(uint32 unique_id) OVERRIDE { + 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) OVERRIDE { + virtual void OnHeaders(SpdyStreamId stream_id, + bool fin, + bool end) OVERRIDE { SpdyFramer framer(version_); framer.set_enable_compression(false); - const SpdyHeaderBlock null_headers; - int flags = CONTROL_FLAG_NONE; - if (fin) { - flags &= CONTROL_FLAG_FIN; - } - scoped_ptr<SpdyFrame> frame( - framer.CreateHeaders(stream_id, - static_cast<SpdyControlFlags>(flags), - &null_headers)); + 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(); @@ -233,25 +174,23 @@ class SpdyFramerTestUtil { virtual void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) { LOG(FATAL); } - virtual bool OnCredentialFrameData(const char* /*credential_data*/, - size_t /*len*/) OVERRIDE { - LOG(FATAL) << "Unexpected CREDENTIAL Frame"; - return false; - } virtual void OnPushPromise(SpdyStreamId stream_id, - SpdyStreamId promised_stream_id) OVERRIDE { + SpdyStreamId promised_stream_id, + bool end) OVERRIDE { SpdyFramer framer(version_); framer.set_enable_compression(false); - const SpdyHeaderBlock null_headers; - scoped_ptr<SpdyFrame> frame( - framer.CreatePushPromise(stream_id, promised_stream_id, - &null_headers)); + 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(); @@ -283,8 +222,9 @@ class SpdyFramerTestUtil { class TestSpdyVisitor : public SpdyFramerVisitorInterface, public SpdyFramerDebugVisitorInterface { public: + // This is larger than our max frame size because header blocks that + // are too long can spill over into CONTINUATION frames. static const size_t kDefaultHeaderBufferSize = 16 * 1024 * 1024; - static const size_t kDefaultCredentialBufferSize = 16 * 1024; explicit TestSpdyVisitor(SpdyMajorVersion version) : framer_(version), @@ -293,14 +233,21 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, syn_frame_count_(0), syn_reply_frame_count_(0), headers_frame_count_(0), + push_promise_frame_count_(0), goaway_count_(0), setting_count_(0), + settings_ack_sent_(0), + settings_ack_received_(0), + continuation_count_(0), + altsvc_count_(0), + test_altsvc_ir_(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), @@ -313,22 +260,19 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, header_buffer_size_(kDefaultHeaderBufferSize), header_stream_id_(-1), header_control_type_(DATA), - header_buffer_valid_(false), - credential_buffer_(new char[kDefaultCredentialBufferSize]), - credential_buffer_length_(0), - credential_buffer_size_(kDefaultCredentialBufferSize) { + header_buffer_valid_(false) { } virtual void OnError(SpdyFramer* f) OVERRIDE { LOG(INFO) << "SpdyFramer Error: " << SpdyFramer::ErrorCodeToString(f->error_code()); - error_count_++; + ++error_count_; } virtual void OnDataFrameHeader(SpdyStreamId stream_id, size_t length, bool fin) OVERRIDE { - data_frame_count_++; + ++data_frame_count_; header_stream_id_ = stream_id; } @@ -358,10 +302,13 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, if (len == 0) { ++zero_length_control_frame_header_data_count_; // Indicates end-of-header-block. + 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); + LOG_IF(DFATAL, header_buffer_length_ != parsed_length) + << "Check failed: header_buffer_length_ == parsed_length " + << "(" << header_buffer_length_ << " vs. " << parsed_length << ")"; return true; } const size_t available = header_buffer_size_ - header_buffer_length_; @@ -377,49 +324,66 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, virtual void OnSynStream(SpdyStreamId stream_id, SpdyStreamId associated_stream_id, SpdyPriority priority, - uint8 credential_slot, bool fin, bool unidirectional) OVERRIDE { - syn_frame_count_++; + ++syn_frame_count_; InitHeaderStreaming(SYN_STREAM, stream_id); if (fin) { - fin_flag_count_++; + ++fin_flag_count_; } } virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE { - syn_reply_frame_count_++; + ++syn_reply_frame_count_; InitHeaderStreaming(SYN_REPLY, stream_id); if (fin) { - fin_flag_count_++; + ++fin_flag_count_; } } virtual void OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) OVERRIDE { - fin_frame_count_++; + ++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_++; + ++setting_count_; + } + + virtual void OnSettingsAck() OVERRIDE { + DCHECK_LT(SPDY3, framer_.protocol_version()); + ++settings_ack_received_; + } + + virtual void OnSettingsEnd() OVERRIDE { + if (framer_.protocol_version() <= SPDY3) { return; } + ++settings_ack_sent_; } - virtual void OnPing(uint32 unique_id) OVERRIDE { + 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_++; + ++goaway_count_; } - virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE { - headers_frame_count_++; + virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) OVERRIDE { + ++headers_frame_count_; InitHeaderStreaming(HEADERS, stream_id); if (fin) { - fin_flag_count_++; + ++fin_flag_count_; } } @@ -429,35 +393,36 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, last_window_update_delta_ = delta_window_size; } - virtual bool OnCredentialFrameData(const char* credential_data, - size_t len) OVERRIDE { - if (len == 0) { - if (!framer_.ParseCredentialData(credential_buffer_.get(), - credential_buffer_length_, - &credential_)) { - LOG(INFO) << "Error parsing credential data."; - ++error_count_; - } - return true; - } - const size_t available = - credential_buffer_size_ - credential_buffer_length_; - if (len > available) { - return false; - } - memcpy(credential_buffer_.get() + credential_buffer_length_, - credential_data, len); - credential_buffer_length_ += len; - return true; - } - virtual void OnPushPromise(SpdyStreamId stream_id, - SpdyStreamId promised_stream_id) OVERRIDE { + SpdyStreamId promised_stream_id, + bool end) OVERRIDE { + ++push_promise_frame_count_; 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 OnAltSvc(SpdyStreamId stream_id, + uint32 max_age, + uint16 port, + StringPiece protocol_id, + StringPiece host, + StringPiece origin) OVERRIDE { + test_altsvc_ir_.set_stream_id(stream_id); + test_altsvc_ir_.set_max_age(max_age); + test_altsvc_ir_.set_port(port); + test_altsvc_ir_.set_protocol_id(protocol_id.as_string()); + test_altsvc_ir_.set_host(host.as_string()); + if (origin.length() > 0) { + test_altsvc_ir_.set_origin(origin.as_string()); + } + ++altsvc_count_; + } + virtual void OnSendCompressedFrame(SpdyStreamId stream_id, SpdyFrameType type, size_t payload_len, @@ -522,14 +487,21 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, int syn_frame_count_; int syn_reply_frame_count_; int headers_frame_count_; + int push_promise_frame_count_; int goaway_count_; int setting_count_; + int settings_ack_sent_; + int settings_ack_received_; + int continuation_count_; + int altsvc_count_; + SpdyAltSvcIR test_altsvc_ir_; 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_; // The count of RST_STREAM type frames received. + std::string fin_opaque_data_; int fin_flag_count_; // The count of frames with the FIN flag set. int zero_length_data_frame_count_; // The count of zero-length data frames. int control_frame_header_data_count_; // The count of chunks received. @@ -547,19 +519,41 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, SpdyFrameType header_control_type_; bool header_buffer_valid_; SpdyHeaderBlock headers_; - - scoped_ptr<char[]> credential_buffer_; - size_t credential_buffer_length_; - size_t credential_buffer_size_; - SpdyCredential credential_; }; -// Retrieves serialized headers from SYN_STREAM frame. -// Does not check that the given frame is a SYN_STREAM. +// Retrieves serialized headers from a HEADERS or SYN_STREAM frame. base::StringPiece GetSerializedHeaders(const SpdyFrame* frame, const SpdyFramer& framer) { - return base::StringPiece(frame->data() + framer.GetSynStreamMinimumSize(), - frame->size() - framer.GetSynStreamMinimumSize()); + SpdyFrameReader reader(frame->data(), frame->size()); + reader.Seek(2); // Seek past the frame length. + SpdyFrameType frame_type; + if (framer.protocol_version() > SPDY3) { + uint8 serialized_type; + reader.ReadUInt8(&serialized_type); + frame_type = SpdyConstants::ParseFrameType(framer.protocol_version(), + serialized_type); + DCHECK_EQ(HEADERS, frame_type); + uint8 flags; + reader.ReadUInt8(&flags); + if (flags & HEADERS_FLAG_PRIORITY) { + frame_type = SYN_STREAM; + } + } else { + uint16 serialized_type; + reader.ReadUInt16(&serialized_type); + frame_type = SpdyConstants::ParseFrameType(framer.protocol_version(), + serialized_type); + DCHECK(frame_type == HEADERS || + frame_type == SYN_STREAM) << frame_type; + } + + if (frame_type == SYN_STREAM) { + return StringPiece(frame->data() + framer.GetSynStreamMinimumSize(), + frame->size() - framer.GetSynStreamMinimumSize()); + } else { + return StringPiece(frame->data() + framer.GetHeadersMinimumSize(), + frame->size() - framer.GetHeadersMinimumSize()); + } } } // namespace test @@ -579,7 +573,8 @@ class SpdyFramerTest : public ::testing::TestWithParam<SpdyMajorVersion> { protected: virtual void SetUp() { spdy_version_ = GetParam(); - spdy_version_ch_ = static_cast<unsigned char>(spdy_version_); + spdy_version_ch_ = static_cast<unsigned char>( + SpdyConstants::SerializeMajorVersion(spdy_version_)); } void CompareFrame(const string& description, @@ -630,21 +625,10 @@ class SpdyFramerTest : public ::testing::TestWithParam<SpdyMajorVersion> { return true; } - void AddSpdySettingFromWireFormat(SettingsMap* settings, - uint32 key, - uint32 value) { - SettingsFlagsAndId flags_and_id = - SettingsFlagsAndId::FromWireFormat(spdy_version_, key); - SpdySettingsIds id = static_cast<SpdySettingsIds>(flags_and_id.id()); - SpdySettingsFlags flags = - static_cast<SpdySettingsFlags>(flags_and_id.flags()); - CHECK(settings->find(id) == settings->end()); - settings->insert(std::make_pair(id, SettingsFlagsAndValue(flags, value))); - } - bool IsSpdy2() { return spdy_version_ == SPDY2; } bool IsSpdy3() { return spdy_version_ == SPDY3; } bool IsSpdy4() { return spdy_version_ == SPDY4; } + bool IsSpdy5() { return spdy_version_ == SPDY5; } // Version of SPDY protocol to be used. SpdyMajorVersion spdy_version_; @@ -658,139 +642,62 @@ INSTANTIATE_TEST_CASE_P(SpdyFramerTests, // Test that we can encode and decode a SpdyHeaderBlock in serialized form. TEST_P(SpdyFramerTest, HeaderBlockInBuffer) { - SpdyHeaderBlock headers; - headers["alpha"] = "beta"; - headers["gamma"] = "charlie"; SpdyFramer framer(spdy_version_); framer.set_enable_compression(false); // Encode the header block into a SynStream frame. - scoped_ptr<SpdyFrame> frame( - framer.CreateSynStream(1, // stream id - 0, // associated stream id - 1, // priority - 0, // credential slot - CONTROL_FLAG_NONE, - &headers)); + 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); - base::StringPiece serialized_headers = - GetSerializedHeaders(frame.get(), framer); - SpdyHeaderBlock new_headers; - EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(), - serialized_headers.size(), - &new_headers)); - EXPECT_EQ(headers.size(), new_headers.size()); - EXPECT_EQ(headers["alpha"], new_headers["alpha"]); - EXPECT_EQ(headers["gamma"], new_headers["gamma"]); + 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 that if there's not a full frame, we fail to parse it. TEST_P(SpdyFramerTest, UndersizedHeaderBlockInBuffer) { - SpdyHeaderBlock headers; - headers["alpha"] = "beta"; - headers["gamma"] = "charlie"; SpdyFramer framer(spdy_version_); framer.set_enable_compression(false); // Encode the header block into a SynStream frame. - scoped_ptr<SpdyFrame> frame( - framer.CreateSynStream(1, // stream id - 0, // associated stream id - 1, // priority - 0, // credential slot - CONTROL_FLAG_NONE, - &headers)); + 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); - base::StringPiece serialized_headers = - GetSerializedHeaders(frame.get(), framer); - SpdyHeaderBlock new_headers; - EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(), - serialized_headers.size() - 2, - &new_headers)); -} - -TEST_P(SpdyFramerTest, OutOfOrderHeaders) { - SpdyFramer framer(spdy_version_); - framer.set_enable_compression(false); - - // Frame builder with plentiful buffer size. - SpdyFrameBuilder frame(1024); - if (spdy_version_ < 4) { - frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE); - frame.WriteUInt32(3); // stream_id - } else { - frame.WriteFramePrefix(framer, SYN_STREAM, CONTROL_FLAG_NONE, 3); - } - - frame.WriteUInt32(0); // Associated stream id - frame.WriteUInt16(0); // Priority. - - if (IsSpdy2()) { - frame.WriteUInt16(2); // Number of headers. - frame.WriteString("gamma"); - frame.WriteString("gamma"); - frame.WriteString("alpha"); - frame.WriteString("alpha"); - } else { - frame.WriteUInt32(2); // Number of headers. - frame.WriteStringPiece32("gamma"); - frame.WriteStringPiece32("gamma"); - frame.WriteStringPiece32("alpha"); - frame.WriteStringPiece32("alpha"); - } - // write the length - frame.RewriteLength(framer); - - SpdyHeaderBlock new_headers; - scoped_ptr<SpdyFrame> control_frame(frame.take()); - base::StringPiece serialized_headers = - GetSerializedHeaders(control_frame.get(), framer); - EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(), - serialized_headers.size(), - &new_headers)); -} - -// Test that if we receive a SYN_STREAM with stream ID zero, we signal an error -// (but don't crash). -TEST_P(SpdyFramerTest, SynStreamWithStreamIdZero) { - testing::StrictMock<net::test::MockVisitor> visitor; - SpdyFramer framer(spdy_version_); - framer.set_visitor(&visitor); - - SpdyHeaderBlock headers; - headers["alpha"] = "beta"; - scoped_ptr<SpdySerializedFrame> frame( - framer.CreateSynStream(0, // stream id - 0, // associated stream id - 1, // priority - 0, // credential slot - CONTROL_FLAG_NONE, - &headers)); - ASSERT_TRUE(frame.get() != NULL); + TestSpdyVisitor visitor(spdy_version_); + visitor.use_compression_ = false; + visitor.SimulateInFramer( + reinterpret_cast<unsigned char*>(frame->data()), + frame->size() - 2); - // We shouldn't have to read the whole frame before we signal an error. - 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()); + EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); + EXPECT_EQ(0u, visitor.headers_.size()); } // Test that if we receive a SYN_REPLY with stream ID zero, we signal an error // (but don't crash). TEST_P(SpdyFramerTest, SynReplyWithStreamIdZero) { - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - SpdyHeaderBlock headers; - headers["alpha"] = "beta"; - scoped_ptr<SpdySerializedFrame> frame( - framer.CreateSynReply(0, // stream id - CONTROL_FLAG_NONE, - &headers)); + SpdySynReplyIR syn_reply(0); + syn_reply.SetHeader("alpha", "beta"); + scoped_ptr<SpdySerializedFrame> frame(framer.SerializeSynReply(syn_reply)); ASSERT_TRUE(frame.get() != NULL); // We shouldn't have to read the whole frame before we signal an error. @@ -804,16 +711,13 @@ TEST_P(SpdyFramerTest, SynReplyWithStreamIdZero) { // Test that if we receive a HEADERS with stream ID zero, we signal an error // (but don't crash). TEST_P(SpdyFramerTest, HeadersWithStreamIdZero) { - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - SpdyHeaderBlock headers; - headers["alpha"] = "beta"; - scoped_ptr<SpdySerializedFrame> frame( - framer.CreateHeaders(0, // stream id - CONTROL_FLAG_NONE, - &headers)); + SpdyHeadersIR headers_ir(0); + headers_ir.SetHeader("alpha", "beta"); + scoped_ptr<SpdySerializedFrame> frame(framer.SerializeHeaders(headers_ir)); ASSERT_TRUE(frame.get() != NULL); // We shouldn't have to read the whole frame before we signal an error. @@ -827,20 +731,18 @@ TEST_P(SpdyFramerTest, HeadersWithStreamIdZero) { // Test that if we receive a PUSH_PROMISE with stream ID zero, we signal an // error (but don't crash). TEST_P(SpdyFramerTest, PushPromiseWithStreamIdZero) { - if (spdy_version_ < SPDY4) { + if (spdy_version_ <= SPDY3) { return; } - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - SpdyHeaderBlock headers; - headers["alpha"] = "beta"; + SpdyPushPromiseIR push_promise(0, 4); + push_promise.SetHeader("alpha", "beta"); scoped_ptr<SpdySerializedFrame> frame( - framer.CreatePushPromise(0, // stream id - 4, // promised stream id - &headers)); + framer.SerializePushPromise(push_promise)); ASSERT_TRUE(frame.get() != NULL); // We shouldn't have to read the whole frame before we signal an error. @@ -854,20 +756,18 @@ TEST_P(SpdyFramerTest, PushPromiseWithStreamIdZero) { // Test that if we receive a PUSH_PROMISE with promised stream ID zero, we // signal an error (but don't crash). TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) { - if (spdy_version_ < SPDY4) { + if (spdy_version_ <= SPDY3) { return; } - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - SpdyHeaderBlock headers; - headers["alpha"] = "beta"; + SpdyPushPromiseIR push_promise(3, 0); + push_promise.SetHeader("alpha", "beta"); scoped_ptr<SpdySerializedFrame> frame( - framer.CreatePushPromise(3, // stream id - 0, // promised stream id - &headers)); + framer.SerializePushPromise(push_promise)); ASSERT_TRUE(frame.get() != NULL); // We shouldn't have to read the whole frame before we signal an error. @@ -878,139 +778,26 @@ TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) { << SpdyFramer::ErrorCodeToString(framer.error_code()); } -TEST_P(SpdyFramerTest, CreateCredential) { - SpdyFramer framer(spdy_version_); - - { - const char kDescription[] = "CREDENTIAL frame"; - const unsigned char kV3FrameData[] = { // Also applies for V2. - 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', - }; - const unsigned char kV4FrameData[] = { - 0x00, 0x3b, 0x0A, 0x00, - 0x00, 0x00, 0x00, 0x00, - 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', - }; - SpdyCredential credential; - credential.slot = 3; - credential.proof = "proof"; - credential.certs.push_back("a cert"); - credential.certs.push_back("another cert"); - credential.certs.push_back("final cert"); - scoped_ptr<SpdyFrame> frame(framer.CreateCredentialFrame(credential)); - if (IsSpdy4()) { - CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); - } else { - CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); - } - } -} - -TEST_P(SpdyFramerTest, ParseCredentialFrameData) { - SpdyFramer framer(spdy_version_); - - { - const unsigned char kV3FrameData[] = { // Also applies for V2. - 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', - }; - const unsigned char kV4FrameData[] = { - 0x00, 0x37, 0x0A, 0x00, - 0x00, 0x00, 0x00, 0x00, - 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', - }; - - SpdyCredential credential; - if (IsSpdy4()) { - EXPECT_TRUE(SpdyFramer::ParseCredentialData( - reinterpret_cast<const char*>(kV4FrameData) + - framer.GetControlFrameHeaderSize(), - arraysize(kV4FrameData) - framer.GetControlFrameHeaderSize(), - &credential)); - } else { - EXPECT_TRUE(SpdyFramer::ParseCredentialData( - reinterpret_cast<const char*>(kV3FrameData) + - framer.GetControlFrameHeaderSize(), - arraysize(kV3FrameData) - framer.GetControlFrameHeaderSize(), - &credential)); - } - EXPECT_EQ(3u, credential.slot); - EXPECT_EQ("proof", credential.proof); - EXPECT_EQ("a cert", credential.certs.front()); - credential.certs.erase(credential.certs.begin()); - EXPECT_EQ("another cert", credential.certs.front()); - credential.certs.erase(credential.certs.begin()); - EXPECT_EQ("final cert", credential.certs.front()); - credential.certs.erase(credential.certs.begin()); - EXPECT_TRUE(credential.certs.empty()); - } -} - TEST_P(SpdyFramerTest, DuplicateHeader) { + if (spdy_version_ > SPDY3) { + // TODO(jgraettinger): Punting on this because we haven't determined + // whether duplicate HPACK headers other than Cookie are an error. + // If they are, this will need to be updated to use HpackOutputStream. + return; + } SpdyFramer framer(spdy_version_); // Frame builder with plentiful buffer size. - SpdyFrameBuilder frame(1024); - if (spdy_version_ < 4) { + SpdyFrameBuilder frame(1024, spdy_version_); + if (spdy_version_ <= SPDY3) { frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE); frame.WriteUInt32(3); // stream_id + frame.WriteUInt32(0); // associated stream id + frame.WriteUInt16(0); // Priority. } else { - frame.WriteFramePrefix(framer, SYN_STREAM, CONTROL_FLAG_NONE, 3); + frame.BeginNewFrame(framer, HEADERS, HEADERS_FLAG_PRIORITY, 3); + frame.WriteUInt32(framer.GetHighestPriority()); } - frame.WriteUInt32(0); // associated stream id - frame.WriteUInt16(0); // Priority. - if (IsSpdy2()) { frame.WriteUInt16(2); // Number of headers. frame.WriteString("name"); @@ -1041,22 +828,34 @@ TEST_P(SpdyFramerTest, DuplicateHeader) { TEST_P(SpdyFramerTest, MultiValueHeader) { SpdyFramer framer(spdy_version_); // Frame builder with plentiful buffer size. - SpdyFrameBuilder frame(1024); - if (spdy_version_ < 4) { + SpdyFrameBuilder frame(1024, spdy_version_); + if (spdy_version_ <= SPDY3) { frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE); frame.WriteUInt32(3); // stream_id + frame.WriteUInt32(0); // associated stream id + frame.WriteUInt16(0); // Priority. } else { - frame.WriteFramePrefix(framer, SYN_STREAM, CONTROL_FLAG_NONE, 3); + frame.BeginNewFrame(framer, + HEADERS, + HEADERS_FLAG_PRIORITY | HEADERS_FLAG_END_HEADERS, + 3); + frame.WriteUInt32(0); // Priority exclusivity and dependent stream. + frame.WriteUInt8(255); // Priority weight. } - frame.WriteUInt32(0); // associated stream id - frame.WriteUInt16(0); // Priority. - - string value("value1\0value2"); + string value("value1\0value2", 13); if (IsSpdy2()) { frame.WriteUInt16(1); // Number of headers. frame.WriteString("name"); frame.WriteString(value); + } else if (spdy_version_ > SPDY3) { + // TODO(jgraettinger): If this pattern appears again, move to test class. + std::map<string, string> header_set; + header_set["name"] = value; + string buffer; + HpackEncoder encoder(ObtainHpackHuffmanTable()); + encoder.EncodeHeaderSetWithoutCompression(header_set, &buffer); + frame.WriteBytes(&buffer[0], buffer.size()); } else { frame.WriteUInt32(1); // Number of headers. frame.WriteStringPiece32("name"); @@ -1065,37 +864,36 @@ TEST_P(SpdyFramerTest, MultiValueHeader) { // write the length 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_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(), - serialized_headers.size(), - &new_headers)); - EXPECT_TRUE(new_headers.find("name") != new_headers.end()); - EXPECT_EQ(value, new_headers.find("name")->second); + + 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) { - SpdyHeaderBlock headers; - headers["server"] = "SpdyServer 1.0"; - headers["date"] = "Mon 12 Jan 2009 12:12:12 PST"; - headers["status"] = "200"; - headers["version"] = "HTTP/1.1"; - headers["content-type"] = "text/html"; - headers["content-length"] = "12"; - + if (spdy_version_ > SPDY3) { + // Deflate compression doesn't apply to HPACK. + return; + } scoped_ptr<TestSpdyVisitor> visitor(new TestSpdyVisitor(spdy_version_)); SpdyFramer framer(spdy_version_); framer.set_debug_visitor(visitor.get()); - scoped_ptr<SpdyFrame> frame1( - framer.CreateSynStream(1, // stream id - 0, // associated stream id - 1, // priority - 0, // credential slot - CONTROL_FLAG_NONE, - &headers)); + 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(); @@ -1114,13 +912,7 @@ TEST_P(SpdyFramerTest, BasicCompression) { EXPECT_EQ(117u, compressed_size1); #endif // !defined(USE_SYSTEM_ZLIB) } - scoped_ptr<SpdyFrame> frame2( - framer.CreateSynStream(1, // stream id - 0, // associated stream id - 1, // priority - 0, // credential slot - CONTROL_FLAG_NONE, - &headers)); + 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(); @@ -1168,35 +960,24 @@ TEST_P(SpdyFramerTest, BasicCompression) { // from scratch. framer.set_enable_compression(false); scoped_ptr<SpdyFrame> uncompressed_frame( - framer.CreateSynStream(1, // stream id - 0, // associated stream id - 1, // priority - 0, // credential slot - CONTROL_FLAG_NONE, - &headers)); + framer.SerializeSynStream(syn_stream)); CompareFrames("Uncompressed SYN_STREAM", *frame3, *uncompressed_frame); } TEST_P(SpdyFramerTest, CompressEmptyHeaders) { // See crbug.com/172383 - SpdyHeaderBlock headers; - headers["server"] = "SpdyServer 1.0"; - headers["date"] = "Mon 12 Jan 2009 12:12:12 PST"; - headers["status"] = "200"; - headers["version"] = "HTTP/1.1"; - headers["content-type"] = "text/html"; - headers["content-length"] = "12"; - headers["x-empty-header"] = ""; + 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.CreateSynStream(1, // stream id - 0, // associated stream id - 1, // priority - 0, // credential slot - CONTROL_FLAG_NONE, - &headers)); + scoped_ptr<SpdyFrame> frame1(framer.SerializeSynStream(syn_stream)); } TEST_P(SpdyFramerTest, Basic) { @@ -1242,7 +1023,7 @@ TEST_P(SpdyFramerTest, Basic) { 0x80, spdy_version_ch_, 0x00, 0x03, // RST_STREAM on Stream #1 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, // RST_STREAM_CANCEL 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3 0x00, 0x00, 0x00, 0x00, @@ -1250,7 +1031,7 @@ TEST_P(SpdyFramerTest, Basic) { 0x80, spdy_version_ch_, 0x00, 0x03, // RST_STREAM on Stream #3 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, // RST_STREAM_CANCEL }; const unsigned char kV3Input[] = { @@ -1301,7 +1082,7 @@ TEST_P(SpdyFramerTest, Basic) { 0x80, spdy_version_ch_, 0x00, 0x03, // RST_STREAM on Stream #1 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, // RST_STREAM_CANCEL 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3 0x00, 0x00, 0x00, 0x00, @@ -1309,60 +1090,54 @@ TEST_P(SpdyFramerTest, Basic) { 0x80, spdy_version_ch_, 0x00, 0x03, // RST_STREAM on Stream #3 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, // RST_STREAM_CANCEL }; + // SYN_STREAM doesn't exist in SPDY4, so instead we send + // HEADERS frames with PRIORITY and END_HEADERS set. const unsigned char kV4Input[] = { - 0x00, 0x1e, 0x01, 0x00, // SYN_STREAM #1 - 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', + 0x00, 0x05, 0x01, 0x24, // HEADERS: PRIORITY | END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x00, 0x00, 0x00, // Priority 0 + 0x82, // :method: GET - 0x00, 0x24, 0x08, 0x00, // HEADERS on Stream #1 - 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, 0x01, 0x01, 0x04, // HEADERS: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x8c, // :status: 200 - 0x00, 0x14, 0x00, 0x00, // DATA on Stream #1 + 0x00, 0x0c, 0x00, 0x00, // DATA on Stream #1 0x00, 0x00, 0x00, 0x01, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x12, 0x01, 0x00, // SYN Stream #3 - 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, + 0x00, 0x05, 0x01, 0x24, // HEADERS: PRIORITY | END_HEADERS + 0x00, 0x00, 0x00, 0x03, // Stream 3 + 0x00, 0x00, 0x00, 0x00, // Priority 0 + 0x82, // :method: GET - 0x00, 0x10, 0x00, 0x00, // DATA on Stream #3 + 0x00, 0x08, 0x00, 0x00, // DATA on Stream #3 0x00, 0x00, 0x00, 0x03, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x0c, 0x00, 0x00, // DATA on Stream #1 + 0x00, 0x04, 0x00, 0x00, // DATA on Stream #1 0x00, 0x00, 0x00, 0x01, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x0c, 0x03, 0x00, // RST_STREAM on Stream #1 + 0x00, 0x04, 0x03, 0x00, // RST_STREAM on Stream #1 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, // RST_STREAM_CANCEL - 0x00, 0x08, 0x00, 0x00, // DATA on Stream #3 + 0x00, 0x00, 0x00, 0x00, // DATA on Stream #3 0x00, 0x00, 0x00, 0x03, - 0x00, 0x0c, 0x03, 0x00, // RST_STREAM on Stream #3 + 0x00, 0x0f, 0x03, 0x00, // RST_STREAM on Stream #3 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, // RST_STREAM_CANCEL + 0x52, 0x45, 0x53, 0x45, // opaque data + 0x54, 0x53, 0x54, 0x52, + 0x45, 0x41, 0x4d, }; TestSpdyVisitor visitor(spdy_version_); @@ -1374,15 +1149,25 @@ TEST_P(SpdyFramerTest, Basic) { visitor.SimulateInFramer(kV4Input, sizeof(kV4Input)); } - EXPECT_EQ(0, visitor.error_count_); 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 that the FIN flag on a data frame signifies EOF. @@ -1442,30 +1227,26 @@ TEST_P(SpdyFramerTest, FinOnDataFrame) { 0x01, 0x00, 0x00, 0x04, 0xde, 0xad, 0xbe, 0xef, }; + + // SYN_STREAM and SYN_REPLY don't exist in SPDY4, so instead we send + // HEADERS frames with PRIORITY(SYN_STREAM only) and END_HEADERS set. const unsigned char kV4Input[] = { - 0x00, 0x1e, 0x01, 0x00, // SYN_STREAM #1 - 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', + 0x00, 0x05, 0x01, 0x24, // HEADERS: PRIORITY | END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x00, 0x00, 0x00, // Priority 0 + 0x82, // :method: GET - 0x00, 0x18, 0x02, 0x00, // SYN REPLY Stream #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'a', 'a', 0x00, 0x00, - 0x00, 0x02, 'b', 'b', + 0x00, 0x01, 0x01, 0x04, // HEADERS: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x8c, // :status: 200 - 0x00, 0x14, 0x00, 0x00, // DATA on Stream #1 + 0x00, 0x0c, 0x00, 0x00, // DATA on Stream #1 0x00, 0x00, 0x00, 0x01, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x0c, 0x00, 0x01, // DATA on Stream #1, with FIN + 0x00, 0x04, 0x00, 0x01, // DATA on Stream #1, with FIN 0x00, 0x00, 0x00, 0x01, 0xde, 0xad, 0xbe, 0xef, }; @@ -1481,8 +1262,13 @@ TEST_P(SpdyFramerTest, FinOnDataFrame) { EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(1, visitor.syn_frame_count_); - EXPECT_EQ(1, visitor.syn_reply_frame_count_); - EXPECT_EQ(0, visitor.headers_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_); @@ -1527,22 +1313,18 @@ TEST_P(SpdyFramerTest, FinOnSynReplyFrame) { 'a', 'a', 0x00, 0x00, 0x00, 0x02, 'b', 'b', }; - const unsigned char kV4Input[] = { - 0x00, 0x1e, 0x01, 0x00, // SYN_STREAM #1 - 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', - 0x00, 0x18, 0x02, 0x01, // SYN_REPLY #1, with FIN - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'a', 'a', 0x00, 0x00, - 0x00, 0x02, 'b', 'b', + // SYN_STREAM and SYN_REPLY don't exist in SPDY4, so instead we send + // HEADERS frames with PRIORITY(SYN_STREAM only) and END_HEADERS set. + const unsigned char kV4Input[] = { + 0x00, 0x05, 0x01, 0x24, // HEADERS: PRIORITY | END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x00, 0x00, 0x00, // Priority 0 + 0x82, // :method: GET + + 0x00, 0x01, 0x01, 0x05, // HEADERS: FIN | END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x8c, // :status: 200 }; TestSpdyVisitor visitor(spdy_version_); @@ -1556,8 +1338,13 @@ TEST_P(SpdyFramerTest, FinOnSynReplyFrame) { EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(1, visitor.syn_frame_count_); - EXPECT_EQ(1, visitor.syn_reply_frame_count_); - EXPECT_EQ(0, visitor.headers_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_); @@ -1566,6 +1353,10 @@ TEST_P(SpdyFramerTest, FinOnSynReplyFrame) { } TEST_P(SpdyFramerTest, HeaderCompression) { + if (spdy_version_ > SPDY3) { + // Deflate compression doesn't apply to HPACK. + return; + } SpdyFramer send_framer(spdy_version_); SpdyFramer recv_framer(spdy_version_); @@ -1583,22 +1374,16 @@ TEST_P(SpdyFramerTest, HeaderCompression) { SpdyHeaderBlock block; block[kHeader1] = kValue1; block[kHeader2] = kValue2; - SpdyControlFlags flags(CONTROL_FLAG_NONE); SpdySynStreamIR syn_ir_1(1); - syn_ir_1.SetHeader(kHeader1, kValue1); - syn_ir_1.SetHeader(kHeader2, kValue2); + 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); // SYN_STREAM #2 block[kHeader3] = kValue3; - scoped_ptr<SpdyFrame> syn_frame_2( - send_framer.CreateSynStream(3, // stream id - 0, // associated stream id - 0, // priority - 0, // credential slot - flags, - &block)); + 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); // Now start decompressing @@ -1645,24 +1430,16 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressors) { const char kValue1[] = "value1"; const char kValue2[] = "value2"; - SpdyHeaderBlock block; - block[kHeader1] = kValue1; - block[kHeader2] = kValue2; - SpdyControlFlags flags(CONTROL_FLAG_NONE); - scoped_ptr<SpdyFrame> syn_frame( - send_framer.CreateSynStream(1, // stream id - 0, // associated stream id - 0, // priority - 0, // credential slot - flags, - &block)); + 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!"; - scoped_ptr<SpdyFrame> send_frame( - send_framer.CreateDataFrame( - 1, bytes, arraysize(bytes), - static_cast<SpdyDataFlags>(DATA_FLAG_FIN))); + StringPiece bytes = "this is a test test test test test!"; + 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); // Run the inputs through the framer. @@ -1678,7 +1455,7 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressors) { 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(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_); @@ -1697,24 +1474,16 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) { const char kValue1[] = "value1"; const char kValue2[] = "value2"; - SpdyHeaderBlock block; - block[kHeader1] = kValue1; - block[kHeader2] = kValue2; - SpdyControlFlags flags(CONTROL_FLAG_NONE); - scoped_ptr<SpdyFrame> syn_frame( - send_framer.CreateSynStream(1, // stream id - 0, // associated stream id - 0, // priority - 0, // credential slot - flags, - &block)); + 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!"; - scoped_ptr<SpdyFrame> send_frame( - send_framer.CreateDataFrame( - 1, bytes, arraysize(bytes), - static_cast<SpdyDataFlags>(DATA_FLAG_FIN))); + 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); // Run the inputs through the framer. @@ -1745,7 +1514,8 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) { TEST_P(SpdyFramerTest, WindowUpdateFrame) { SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 0x12345678)); + scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate( + SpdyWindowUpdateIR(1, 0x12345678))); const char kDescription[] = "WINDOW_UPDATE frame, stream 1, delta 0x12345678"; const unsigned char kV3FrameData[] = { // Also applies for V2. @@ -1755,15 +1525,15 @@ TEST_P(SpdyFramerTest, WindowUpdateFrame) { 0x12, 0x34, 0x56, 0x78 }; const unsigned char kV4FrameData[] = { - 0x00, 0x0c, 0x09, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x34, 0x56, 0x78 }; if (IsSpdy4()) { - CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); } else { - CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); } } @@ -1779,15 +1549,120 @@ TEST_P(SpdyFramerTest, CreateDataFrame) { 'o' }; const unsigned char kV4FrameData[] = { - 0x00, 0x0d, 0x00, 0x00, + 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.SerializeDataFrameHeaderWithPaddingLengthField( + 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 more padding, no FIN"; + const unsigned char kV3FrameData[] = { // Also applies for V2. + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x05, + 'h', 'e', 'l', 'l', + 'o' + }; + + const unsigned char kV4FrameData[] = { + 0x01, 0x0b, 0x00, 0x18, // Length = 267. PAD_HIGH and PAD_LOW set. + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x04, // Pad Low and Pad High fields. + 'h', 'e', 'l', 'l', // Data + 'o', + // Padding of 260 zeros (so both PAD_HIGH and PAD_LOW fields are used). + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + }; + const char bytes[] = "hello"; + + SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes))); + // 260 zeros and the pad low/high fields make the overall padding to be 262 + // bytes. + data_ir.set_padding_len(262); + scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir)); + if (IsSpdy4()) { + CompareFrame( + kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame( + kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } + + frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir)); + CompareCharArraysWithHexError( + kDescription, + reinterpret_cast<const unsigned char*>(frame->data()), + framer.GetDataFrameMinimumSize(), + IsSpdy4() ? kV4FrameData : kV3FrameData, + framer.GetDataFrameMinimumSize()); + } + + { + const char kDescription[] = "'hello' data frame with few padding, no FIN"; + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x05, 'h', 'e', 'l', 'l', 'o' }; + + const unsigned char kV4FrameData[] = { + 0x00, 0x0d, 0x00, 0x08, // Length = 13. PAD_LOW set. + 0x00, 0x00, 0x00, 0x01, + 0x07, // Pad Low field. + 'h', 'e', 'l', 'l', // Data + 'o', + '0', '0', '0', '0', // Padding + '0', '0', '0' + }; const char bytes[] = "hello"; - scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( - 1, bytes, strlen(bytes), DATA_FLAG_NONE)); + SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes))); + // 7 zeros and the pad low field make the overall padding to be 8 bytes. + data_ir.set_padding_len(8); + scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir)); if (IsSpdy4()) { CompareFrame( kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); @@ -1795,10 +1670,41 @@ TEST_P(SpdyFramerTest, CreateDataFrame) { CompareFrame( kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); } + } + + { + const char kDescription[] = + "'hello' data frame with 1 byte padding, no FIN"; + const unsigned char kV3FrameData[] = { // Also applies for V2. + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x05, + 'h', 'e', 'l', 'l', + 'o' + }; - SpdyDataIR data_ir(1); - data_ir.SetDataShallow(base::StringPiece(bytes, strlen(bytes))); - frame.reset(framer.SerializeDataFrameHeader(data_ir)); + const unsigned char kV4FrameData[] = { + 0x00, 0x06, 0x00, 0x08, // Length = 6. PAD_LOW set. + 0x00, 0x00, 0x00, 0x01, + 0x00, // Pad Low field. + 'h', 'e', 'l', 'l', // Data + 'o', + }; + const char bytes[] = "hello"; + + SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes))); + // The pad low field itself is used for the 1-byte padding and no padding + // payload is needed. + data_ir.set_padding_len(1); + scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir)); + if (IsSpdy4()) { + CompareFrame( + kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame( + kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } + + frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir)); CompareCharArraysWithHexError( kDescription, reinterpret_cast<const unsigned char*>(frame->data()), @@ -1815,12 +1721,12 @@ TEST_P(SpdyFramerTest, CreateDataFrame) { 0xff }; const unsigned char kV4FrameData[] = { - 0x00, 0x09, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff }; - scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( - 1, "\xff", 1, DATA_FLAG_NONE)); + SpdyDataIR data_ir(1, StringPiece("\xff", 1)); + scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir)); if (IsSpdy4()) { CompareFrame( kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); @@ -1839,13 +1745,14 @@ TEST_P(SpdyFramerTest, CreateDataFrame) { 'o' }; const unsigned char kV4FrameData[] = { - 0x00, 0x0d, 0x00, 0x01, + 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 'h', 'e', 'l', 'l', 'o' }; - scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( - 1, "hello", 5, DATA_FLAG_FIN)); + 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)); @@ -1862,11 +1769,11 @@ TEST_P(SpdyFramerTest, CreateDataFrame) { 0x00, 0x00, 0x00, 0x00, }; const unsigned char kV4FrameData[] = { - 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }; - scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( - 1, "", 0, DATA_FLAG_NONE)); + SpdyDataIR data_ir(1, StringPiece()); + scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir)); if (IsSpdy4()) { CompareFrame( kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); @@ -1874,6 +1781,14 @@ TEST_P(SpdyFramerTest, CreateDataFrame) { CompareFrame( kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); } + + frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir)); + CompareCharArraysWithHexError( + kDescription, + reinterpret_cast<const unsigned char*>(frame->data()), + framer.GetDataFrameMinimumSize(), + IsSpdy4() ? kV4FrameData : kV3FrameData, + framer.GetDataFrameMinimumSize()); } { @@ -1885,13 +1800,14 @@ TEST_P(SpdyFramerTest, CreateDataFrame) { 'o' }; const unsigned char kV4FrameData[] = { - 0x00, 0x0d, 0x00, 0x01, + 0x00, 0x05, 0x00, 0x01, 0x7f, 0xff, 0xff, 0xff, 'h', 'e', 'l', 'l', 'o' }; - scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( - 0x7fffffff, "hello", 5, DATA_FLAG_FIN)); + 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)); @@ -1918,8 +1834,9 @@ TEST_P(SpdyFramerTest, CreateDataFrame) { memcpy(expected_frame_data.get(), kFrameHeader, arraysize(kFrameHeader)); memset(expected_frame_data.get() + arraysize(kFrameHeader), 'A', kDataSize); - scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( - 1, kData.data(), kData.size(), DATA_FLAG_FIN)); + 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); } } @@ -1929,14 +1846,9 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { framer.set_enable_compression(false); { - const char kDescription[] = "SYN_STREAM frame, lowest pri, slot 2, no FIN"; - - SpdyHeaderBlock headers; - headers["bar"] = "foo"; - headers["foo"] = "bar"; + const char kDescription[] = "SYN_STREAM frame, lowest pri, no FIN"; const unsigned char kPri = IsSpdy2() ? 0xC0 : 0xE0; - const unsigned char kCre = IsSpdy2() ? 0 : 2; const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, @@ -1954,7 +1866,7 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - kPri, kCre, 0x00, 0x00, + kPri, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 'b', 'a', 'r', 0x00, 0x00, 0x00, @@ -1965,26 +1877,20 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { 'a', 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x2e, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, - kPri, kCre, 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' + 0x00, 0x17, 0x01, 0x24, // HEADERS: PRIORITY | END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x00, 0x00, 0x00, // Non-exclusive dependency 0. Weight 0. + 0x00, 0x00, 0x03, 0x62, + 0x61, 0x72, 0x03, 0x66, + 0x6f, 0x6f, 0x00, 0x03, + 0x66, 0x6f, 0x6f, 0x03, + 0x62, 0x61, 0x72, }; - scoped_ptr<SpdyFrame> frame( - framer.CreateSynStream(1, // stream id - 0, // associated stream id - framer.GetLowestPriority(), - kCre, // credential slot - CONTROL_FLAG_NONE, - &headers)); + 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()) { @@ -1999,10 +1905,6 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { "SYN_STREAM frame with a 0-length header name, highest pri, FIN, " "max stream ID"; - SpdyHeaderBlock headers; - headers[std::string()] = "foo"; - headers["foo"] = "bar"; - const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x01, 0x01, 0x00, 0x00, 0x1D, @@ -2030,25 +1932,21 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { 'b', 'a', 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x2b, 0x01, 0x01, - 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' + 0x00, 0x14, 0x01, 0x25, // HEADERS: PRIORITY | FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x00, 0x00, 0x00, 0x00, // Non-exclusive dependency 0. Weight 255. + 0xff, 0x00, 0x00, 0x03, + 0x66, 0x6f, 0x6f, 0x00, + 0x03, 0x66, 0x6f, 0x6f, + 0x03, 0x62, 0x61, 0x72, }; - scoped_ptr<SpdyFrame> frame( - framer.CreateSynStream(0x7fffffff, // stream id - 0x7fffffff, // associated stream id - framer.GetHighestPriority(), - 0, // credential slot - CONTROL_FLAG_FIN, - &headers)); + 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()) { @@ -2063,10 +1961,6 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { "SYN_STREAM frame with a 0-length header val, high pri, FIN, " "max stream ID"; - SpdyHeaderBlock headers; - headers["bar"] = "foo"; - headers["foo"] = ""; - const unsigned char kPri = IsSpdy2() ? 0x40 : 0x20; const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x01, @@ -2095,25 +1989,21 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { 0x00, 0x00, 0x00 }; const unsigned char kV4FrameData[] = { - 0x00, 0x2b, 0x01, 0x01, - 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 + 0x00, 0x14, 0x01, 0x25, // HEADERS: PRIORITY | FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x00, 0x00, 0x00, 0x00, // Non-exclusive dependency 0. Weight 219. + 0xdb, 0x00, 0x03, 0x62, + 0x61, 0x72, 0x03, 0x66, + 0x6f, 0x6f, 0x00, 0x03, + 0x66, 0x6f, 0x6f, 0x00, }; - scoped_ptr<SpdyFrame> frame( - framer.CreateSynStream(0x7fffffff, // stream id - 0x7fffffff, // associated stream id - 1, // priority - 0, // credential slot - CONTROL_FLAG_FIN, - &headers)); + 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()) { @@ -2135,10 +2025,6 @@ TEST_P(SpdyFramerTest, CreateSynStreamCompressed) { const char kDescription[] = "SYN_STREAM frame, low pri, no FIN"; - SpdyHeaderBlock headers; - headers["bar"] = "foo"; - headers["foo"] = "bar"; - const SpdyPriority priority = IsSpdy2() ? 2 : 4; const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x01, @@ -2176,36 +2062,17 @@ TEST_P(SpdyFramerTest, CreateSynStreamCompressed) { 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, }; - const unsigned char kV4FrameData[] = { - 0x00, 0x3b, 0x01, 0x00, - 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, - }; - scoped_ptr<SpdyFrame> frame( - framer.CreateSynStream(1, // stream id - 0, // associated stream id - priority, - 0, // credential slot - CONTROL_FLAG_NONE, - &headers)); + 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 { - CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + // Deflate compression doesn't apply to HPACK. } } } @@ -2218,10 +2085,6 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { { const char kDescription[] = "SYN_REPLY frame, no FIN"; - SpdyHeaderBlock headers; - headers["bar"] = "foo"; - headers["foo"] = "bar"; - const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1C, @@ -2247,19 +2110,18 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { 0x03, 'b', 'a', 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x28, 0x02, 0x00, - 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' + 0x00, 0x12, 0x01, 0x04, // HEADER: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x00, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x03, 0x62, // oo.b + 0x61, 0x72, // ar }; - scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( - 1, CONTROL_FLAG_NONE, &headers)); + 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()) { @@ -2273,10 +2135,6 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { const char kDescription[] = "SYN_REPLY frame with a 0-length header name, FIN, max stream ID"; - SpdyHeaderBlock headers; - headers[std::string()] = "foo"; - headers["foo"] = "bar"; - const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x02, 0x01, 0x00, 0x00, 0x19, @@ -2302,19 +2160,18 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x25, 0x02, 0x01, - 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' + 0x00, 0x0f, 0x01, 0x05, // HEADER: FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x00, 0x00, 0x03, 0x66, // @..f + 0x6f, 0x6f, 0x00, 0x03, // oo@. + 0x66, 0x6f, 0x6f, 0x03, // foo. + 0x62, 0x61, 0x72, // bar }; - scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( - 0x7fffffff, CONTROL_FLAG_FIN, &headers)); + 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()) { @@ -2328,10 +2185,6 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { const char kDescription[] = "SYN_REPLY frame with a 0-length header val, FIN, max stream ID"; - SpdyHeaderBlock headers; - headers["bar"] = "foo"; - headers["foo"] = ""; - const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x02, 0x01, 0x00, 0x00, 0x19, @@ -2357,19 +2210,18 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { 0x00 }; const unsigned char kV4FrameData[] = { - 0x00, 0x25, 0x02, 0x01, - 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 + 0x00, 0x0f, 0x01, 0x05, // HEADER: FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x00, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x00, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x00, // oo. }; - scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( - 0x7fffffff, CONTROL_FLAG_FIN, &headers)); + 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()) { @@ -2390,10 +2242,6 @@ TEST_P(SpdyFramerTest, CreateSynReplyCompressed) { { const char kDescription[] = "SYN_REPLY frame, no FIN"; - SpdyHeaderBlock headers; - headers["bar"] = "foo"; - headers["foo"] = "bar"; - const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x02, 0x00, 0x00, 0x00, 0x32, @@ -2428,30 +2276,16 @@ TEST_P(SpdyFramerTest, CreateSynReplyCompressed) { 0x00, 0x00, 0x00, 0xff, 0xff, }; - const unsigned char kV4FrameData[] = { - 0x00, 0x35, 0x02, 0x00, - 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, - }; - scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( - 1, CONTROL_FLAG_NONE, &headers)); + 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)); + // Deflate compression doesn't apply to HPACK. } } } @@ -2469,12 +2303,13 @@ TEST_P(SpdyFramerTest, CreateRstStream) { 0x00, 0x00, 0x00, 0x01, }; const unsigned char kV4FrameData[] = { - 0x00, 0x0c, 0x03, 0x00, + 0x00, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x52, 0x53, 0x54 }; - scoped_ptr<SpdyFrame> frame( - framer.CreateRstStream(1, RST_STREAM_PROTOCOL_ERROR)); + 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 { @@ -2491,12 +2326,14 @@ TEST_P(SpdyFramerTest, CreateRstStream) { 0x00, 0x00, 0x00, 0x01, }; const unsigned char kV4FrameData[] = { - 0x00, 0x0c, 0x03, 0x00, + 0x00, 0x04, 0x03, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, }; - scoped_ptr<SpdyFrame> frame(framer.CreateRstStream( - 0x7FFFFFFF, RST_STREAM_PROTOCOL_ERROR)); + 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 { @@ -2513,12 +2350,14 @@ TEST_P(SpdyFramerTest, CreateRstStream) { 0x00, 0x00, 0x00, 0x06, }; const unsigned char kV4FrameData[] = { - 0x00, 0x0c, 0x03, 0x00, + 0x00, 0x04, 0x03, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x06, }; - scoped_ptr<SpdyFrame> frame(framer.CreateRstStream( - 0x7FFFFFFF, RST_STREAM_INTERNAL_ERROR)); + 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 { @@ -2533,39 +2372,42 @@ TEST_P(SpdyFramerTest, CreateSettings) { { const char kDescription[] = "Network byte order SETTINGS frame"; - uint32 kValue = 0x0a0b0c0d; - SpdySettingsFlags kFlags = static_cast<SpdySettingsFlags>(0x01); - SpdySettingsIds kId = static_cast<SpdySettingsIds>(0x020304); - - SettingsMap settings; - settings[kId] = SettingsFlagsAndValue(kFlags, kValue); - - EXPECT_EQ(kFlags, settings[kId].first); - EXPECT_EQ(kValue, settings[kId].second); - const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, - 0x04, 0x03, 0x02, 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, 0x02, 0x03, 0x04, + 0x01, 0x00, 0x00, 0x07, 0x0a, 0x0b, 0x0c, 0x0d, }; const unsigned char kV4FrameData[] = { - 0x00, 0x14, 0x04, 0x00, + 0x00, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x01, 0x02, 0x03, 0x04, - 0x0a, 0x0b, 0x0c, 0x0d, + 0x04, 0x0a, 0x0b, 0x0c, + 0x0d, }; - scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings)); + 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()) { @@ -2578,66 +2420,90 @@ TEST_P(SpdyFramerTest, CreateSettings) { { const char kDescription[] = "Basic SETTINGS frame"; - SettingsMap settings; - AddSpdySettingFromWireFormat( - &settings, 0x00000000, 0x00000001); // 1st Setting - AddSpdySettingFromWireFormat( - &settings, 0x01000001, 0x00000002); // 2nd Setting - AddSpdySettingFromWireFormat( - &settings, 0x02000002, 0x00000003); // 3rd Setting - AddSpdySettingFromWireFormat( - &settings, 0x03000003, 0xff000004); // 4th Setting - - const unsigned char kV3FrameData[] = { // Also applies for V2. + const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x04, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, // 1st Setting - 0x00, 0x00, 0x00, 0x01, - 0x01, 0x00, 0x00, 0x01, // 2nd Setting - 0x00, 0x00, 0x00, 0x02, - 0x02, 0x00, 0x00, 0x02, // 3rd Setting - 0x00, 0x00, 0x00, 0x03, - 0x03, 0x00, 0x00, 0x03, // 4th Setting - 0xff, 0x00, 0x00, 0x04, + 0x01, 0x00, 0x00, 0x00, // 1st Setting + 0x00, 0x00, 0x00, 0x05, + 0x02, 0x00, 0x00, 0x00, // 2nd Setting + 0x00, 0x00, 0x00, 0x06, + 0x03, 0x00, 0x00, 0x00, // 3rd Setting + 0x00, 0x00, 0x00, 0x07, + 0x04, 0x00, 0x00, 0x00, // 4th Setting + 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, // 1st Setting + 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x02, // 2nd Setting + 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x03, // 3rd Setting + 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x04, // 4th Setting + 0x00, 0x00, 0x00, 0x08, }; + // These end up seemingly out of order because of the way that our internal + // ordering for settings_ir works. HTTP2 has no requirement on ordering on + // the wire. const unsigned char kV4FrameData[] = { - 0x00, 0x2c, 0x04, 0x00, + 0x00, 0x14, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, // 1st Setting - 0x00, 0x00, 0x00, 0x01, - 0x01, 0x00, 0x00, 0x01, // 2nd Setting - 0x00, 0x00, 0x00, 0x02, - 0x02, 0x00, 0x00, 0x02, // 3rd Setting - 0x00, 0x00, 0x00, 0x03, - 0x03, 0x00, 0x00, 0x03, // 4th Setting - 0xff, 0x00, 0x00, 0x04, + 0x03, // 3rd Setting + 0x00, 0x00, 0x00, 0x07, + 0x04, // 4th Setting + 0x00, 0x00, 0x00, 0x08, + 0x01, // 1st Setting + 0x00, 0x00, 0x00, 0x05, + 0x02, // 2nd Setting + 0x00, 0x00, 0x00, 0x06, }; - scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings)); - if (IsSpdy4()) { - CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); - } else { + + SpdySettingsIR settings_ir; + settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 1), + false, // persist + false, // persisted + 5); + settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 2), + false, // persist + false, // persisted + 6); + settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 3), + false, // persist + false, // persisted + 7); + settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 4), + false, // persist + false, // persisted + 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"; - SettingsMap settings; - const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_ch_, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, }; const unsigned char kV4FrameData[] = { - 0x00, 0x0c, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, }; - scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings)); + SpdySettingsIR settings_ir; + scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir)); if (IsSpdy4()) { CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); } else { @@ -2657,14 +2523,34 @@ TEST_P(SpdyFramerTest, CreatePingFrame) { 0x12, 0x34, 0x56, 0x78, }; const unsigned char kV4FrameData[] = { - 0x00, 0x0c, 0x06, 0x00, + 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(framer.CreatePingFrame(0x12345678u)); + scoped_ptr<SpdyFrame> frame; if (IsSpdy4()) { + const SpdyPingId kPingId = 0x123456789abcdeffULL; + SpdyPingIR ping_ir(kPingId); + // Tests SpdyPingIR when the ping is not an ack. + ASSERT_FALSE(ping_ir.is_ack()); + frame.reset(framer.SerializePing(ping_ir)); CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + + // Tests SpdyPingIR when the ping is an ack. + 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)); } } @@ -2678,21 +2564,23 @@ TEST_P(SpdyFramerTest, CreateGoAway) { const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // Stream Id }; const unsigned char kV3FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // Stream Id + 0x00, 0x00, 0x00, 0x00, // Status }; const unsigned char kV4FrameData[] = { - 0x00, 0x10, 0x07, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // Stream id + 0x00, 0x00, 0x00, 0x00, // Status + 0x47, 0x41, // Opaque Description }; - scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0, GOAWAY_OK)); + 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()) { @@ -2707,22 +2595,23 @@ TEST_P(SpdyFramerTest, CreateGoAway) { const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, - 0x7f, 0xff, 0xff, 0xff, + 0x7f, 0xff, 0xff, 0xff, // Stream Id }; const unsigned char kV3FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, - 0x7f, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x02, + 0x7f, 0xff, 0xff, 0xff, // Stream Id + 0x00, 0x00, 0x00, 0x01, // Status: PROTOCOL_ERROR. }; const unsigned char kV4FrameData[] = { - 0x00, 0x10, 0x07, 0x00, + 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7f, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x02, + 0x7f, 0xff, 0xff, 0xff, // Stream Id + 0x00, 0x00, 0x00, 0x02, // Status: INTERNAL_ERROR. + 0x47, 0x41, // Opaque Description }; - scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0x7FFFFFFF, - GOAWAY_INTERNAL_ERROR)); + 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()) { @@ -2740,10 +2629,6 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { { const char kDescription[] = "HEADERS frame, no FIN"; - SpdyHeaderBlock headers; - headers["bar"] = "foo"; - headers["foo"] = "bar"; - const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1C, @@ -2769,19 +2654,18 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { 0x03, 'b', 'a', 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x28, 0x08, 0x00, - 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' + 0x00, 0x12, 0x01, 0x04, // Headers: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x00, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x03, 0x62, // oo.b + 0x61, 0x72, // ar }; - scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( - 1, CONTROL_FLAG_NONE, &headers)); + 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()) { @@ -2795,10 +2679,6 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { const char kDescription[] = "HEADERS frame with a 0-length header name, FIN, max stream ID"; - SpdyHeaderBlock headers; - headers[std::string()] = "foo"; - headers["foo"] = "bar"; - const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x08, 0x01, 0x00, 0x00, 0x19, @@ -2824,19 +2704,18 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x25, 0x08, 0x01, - 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' + 0x00, 0x0f, 0x01, 0x05, // HEADER: FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x00, 0x00, 0x03, 0x66, // @..f + 0x6f, 0x6f, 0x00, 0x03, // oo@. + 0x66, 0x6f, 0x6f, 0x03, // foo. + 0x62, 0x61, 0x72, // bar }; - scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( - 0x7fffffff, CONTROL_FLAG_FIN, &headers)); + 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()) { @@ -2850,10 +2729,6 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { const char kDescription[] = "HEADERS frame with a 0-length header val, FIN, max stream ID"; - SpdyHeaderBlock headers; - headers["bar"] = "foo"; - headers["foo"] = ""; - const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x08, 0x01, 0x00, 0x00, 0x19, @@ -2879,19 +2754,18 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { 0x00 }; const unsigned char kV4FrameData[] = { - 0x00, 0x25, 0x08, 0x01, - 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 + 0x00, 0x0f, 0x01, 0x05, // HEADER: FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x00, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x00, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x00, // oo. }; - scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( - 0x7fffffff, CONTROL_FLAG_FIN, &headers)); + 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()) { @@ -2912,10 +2786,6 @@ TEST_P(SpdyFramerTest, CreateHeadersCompressed) { { const char kDescription[] = "HEADERS frame, no FIN"; - SpdyHeaderBlock headers; - headers["bar"] = "foo"; - headers["foo"] = "bar"; - const unsigned char kV2FrameData[] = { 0x80, spdy_version_ch_, 0x00, 0x08, 0x00, 0x00, 0x00, 0x32, @@ -2950,30 +2820,16 @@ TEST_P(SpdyFramerTest, CreateHeadersCompressed) { 0x00, 0x00, 0x00, 0xff, 0xff, }; - const unsigned char kV4FrameData[] = { - 0x00, 0x35, 0x08, 0x00, - 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 - }; - scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( - 1, CONTROL_FLAG_NONE, &headers)); + 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)); + // Deflate compression doesn't apply to HPACK. } } } @@ -2991,12 +2847,12 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) { 0x00, 0x00, 0x00, 0x01, }; const unsigned char kV4FrameData[] = { - 0x00, 0x0c, 0x09, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, }; scoped_ptr<SpdyFrame> frame( - framer.CreateWindowUpdate(1, 1)); + framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 1))); if (IsSpdy4()) { CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); } else { @@ -3013,11 +2869,12 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) { 0x00, 0x00, 0x00, 0x01, }; const unsigned char kV4FrameData[] = { - 0x00, 0x0c, 0x09, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, }; - scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(0x7FFFFFFF, 1)); + scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate( + SpdyWindowUpdateIR(0x7FFFFFFF, 1))); if (IsSpdy4()) { CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); } else { @@ -3034,11 +2891,12 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) { 0x7f, 0xff, 0xff, 0xff, }; const unsigned char kV4FrameData[] = { - 0x00, 0x0c, 0x09, 0x00, + 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0xff, 0xff, 0xff, }; - scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 0x7FFFFFFF)); + scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate( + SpdyWindowUpdateIR(1, 0x7FFFFFFF))); if (IsSpdy4()) { CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); } else { @@ -3048,103 +2906,109 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) { } TEST_P(SpdyFramerTest, SerializeBlocked) { - if (spdy_version_ < SPDY4) { + if (spdy_version_ <= SPDY3) { return; } SpdyFramer framer(spdy_version_); const char kDescription[] = "BLOCKED frame"; + const unsigned char kType = static_cast<unsigned char>( + SpdyConstants::SerializeFrameType(spdy_version_, BLOCKED)); const unsigned char kFrameData[] = { - 0x00, 0x08, 0x0b, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, kType, 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_ <= SPDY3) { + 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, CreatePushPromiseUncompressed) { - if (spdy_version_ < SPDY4) { + if (spdy_version_ <= SPDY3) { return; } SpdyFramer framer(spdy_version_); framer.set_enable_compression(false); - const char kDescription[] = "PUSH_PROMISE frame"; - SpdyHeaderBlock headers; - headers["bar"] = "foo"; - headers["foo"] = "bar"; const unsigned char kFrameData[] = { - 0x00, 0x2C, 0x0C, 0x00, // length = 44, type = 12, flags = 0 - 0x00, 0x00, 0x00, 0x2A, // stream id = 42 - 0x00, 0x00, 0x00, 0x39, // promised stream id = 57 - 0x00, 0x00, 0x00, 0x02, // start of uncompressed header block - 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' // end of uncompressed header block + 0x00, 0x16, 0x05, 0x04, // PUSH_PROMISE: END_HEADERS + 0x00, 0x00, 0x00, 0x2a, // Stream 42 + 0x00, 0x00, 0x00, 0x39, // Promised stream 57 + 0x00, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x00, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x03, 0x62, // oo.b + 0x61, 0x72, // ar }; - scoped_ptr<SpdySerializedFrame> frame(framer.CreatePushPromise( - 42, 57, &headers)); + 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, CreatePushPromiseCompressed) { - if (spdy_version_ < SPDY4) { +TEST_P(SpdyFramerTest, CreateAltSvc) { + if (spdy_version_ <= SPDY3) { return; } SpdyFramer framer(spdy_version_); - framer.set_enable_compression(true); - - const char kDescription[] = "PUSH_PROMISE frame"; - SpdyHeaderBlock headers; - headers["bar"] = "foo"; - headers["foo"] = "bar"; + const char kDescription[] = "ALTSVC frame"; + const unsigned char kType = static_cast<unsigned char>( + SpdyConstants::SerializeFrameType(spdy_version_, ALTSVC)); const unsigned char kFrameData[] = { - 0x00, 0x39, 0x0C, 0x00, // length = 57, type = 12, flags = 0 - 0x00, 0x00, 0x00, 0x2A, // stream id = 42 - 0x00, 0x00, 0x00, 0x39, // promised stream id = 57 - 0x38, 0xea, 0xe3, 0xc6, // start of compressed header block - 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 // end of compressed header block + 0x00, 0x17, kType, 0x00, + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x05, + 0x01, 0xbb, 0x00, 0x04, // Port = 443 + 'p', 'i', 'd', '1', // Protocol-ID + 0x04, 'h', 'o', 's', + 't', 'o', 'r', 'i', + 'g', 'i', 'n', }; - - scoped_ptr<SpdySerializedFrame> frame(framer.CreatePushPromise( - 42, 57, &headers)); + SpdyAltSvcIR altsvc_ir(3); + altsvc_ir.set_max_age(5); + altsvc_ir.set_port(443); + altsvc_ir.set_protocol_id("pid1"); + altsvc_ir.set_host("host"); + altsvc_ir.set_origin("origin"); + scoped_ptr<SpdySerializedFrame> frame(framer.SerializeFrame(altsvc_ir)); CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); } TEST_P(SpdyFramerTest, ReadCompressedSynStreamHeaderBlock) { - SpdyHeaderBlock headers; - headers["aa"] = "vv"; - headers["bb"] = "ww"; SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> control_frame( - framer.CreateSynStream(1, // stream_id - 0, // associated_stream_id - 1, // priority - 0, // credential_slot - CONTROL_FLAG_NONE, - &headers)); + 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; @@ -3156,33 +3020,35 @@ TEST_P(SpdyFramerTest, ReadCompressedSynStreamHeaderBlock) { } TEST_P(SpdyFramerTest, ReadCompressedSynReplyHeaderBlock) { - SpdyHeaderBlock headers; - headers["alpha"] = "beta"; - headers["gamma"] = "delta"; SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> control_frame( - framer.CreateSynReply(1, // stream_id - CONTROL_FLAG_NONE, - &headers)); + 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()); - EXPECT_EQ(1, visitor.syn_reply_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_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); } TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlock) { - SpdyHeaderBlock headers; - headers["alpha"] = "beta"; - headers["gamma"] = "delta"; SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> control_frame( - framer.CreateHeaders(1, // stream_id - CONTROL_FLAG_NONE, - &headers)); + 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; @@ -3201,14 +3067,13 @@ TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlock) { } TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) { - SpdyHeaderBlock headers; - headers["alpha"] = "beta"; - headers["gamma"] = "delta"; SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> control_frame( - framer.CreateHeaders(1, // stream_id - CONTROL_FLAG_FIN, - &headers)); + 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; @@ -3227,32 +3092,25 @@ TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) { } TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) { + if (spdy_version_ > SPDY3) { + // TODO(jgraettinger): This test setup doesn't work with HPACK. + return; + } // First find the size of the header value in order to just reach the control // frame max size. SpdyFramer framer(spdy_version_); framer.set_enable_compression(false); - SpdyHeaderBlock headers; - headers["aa"] = ""; - scoped_ptr<SpdyFrame> control_frame( - framer.CreateSynStream(1, // stream_id - 0, // associated_stream_id - 1, // priority - 0, // credential_slot - CONTROL_FLAG_NONE, - &headers)); + 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(); - // Create a frame at exatly that size. + // Create a frame at exactly that size. string big_value(kBigValueSize, 'x'); - headers["aa"] = big_value.c_str(); - control_frame.reset( - framer.CreateSynStream(1, // stream_id - 0, // associated_stream_id - 1, // priority - 0, // credential_slot - CONTROL_FLAG_NONE, - &headers)); + syn_stream.SetHeader("aa", big_value); + control_frame.reset(framer.SerializeSynStream(syn_stream)); EXPECT_TRUE(control_frame.get() != NULL); EXPECT_EQ(framer.GetControlFrameBufferMaxSize(), control_frame->size()); @@ -3269,32 +3127,29 @@ TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) { } TEST_P(SpdyFramerTest, ControlFrameTooLarge) { + if (spdy_version_ > SPDY3) { + // TODO(jgraettinger): This test setup doesn't work with HPACK. + return; + } // First find the size of the header value in order to just reach the control // frame max size. SpdyFramer framer(spdy_version_); framer.set_enable_compression(false); - SpdyHeaderBlock headers; - headers["aa"] = ""; - scoped_ptr<SpdyFrame> control_frame( - framer.CreateSynStream(1, // stream_id - 0, // associated_stream_id - 1, // priority - 0, // credential_slot - CONTROL_FLAG_NONE, - &headers)); + 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; // Create a frame at exatly that size. string big_value(kBigValueSize, 'x'); - headers["aa"] = big_value.c_str(); - control_frame.reset( - framer.CreateSynStream(1, // stream_id - 0, // associated_stream_id - 1, // priority - 0, // credential_slot - CONTROL_FLAG_NONE, - &headers)); + syn_stream.SetHeader("aa", big_value); + // Upstream branches here and wraps SPDY4 with EXPECT_DEBUG_DFATAL. We + // neither support that in Chromium, nor do we use the same DFATAL (see + // SpdyFrameBuilder::WriteFramePrefix()). + control_frame.reset(framer.SerializeSynStream(syn_stream)); + EXPECT_TRUE(control_frame.get() != NULL); EXPECT_EQ(framer.GetControlFrameBufferMaxSize() + 1, control_frame->size()); @@ -3312,25 +3167,78 @@ TEST_P(SpdyFramerTest, ControlFrameTooLarge) { EXPECT_EQ(0u, visitor.header_buffer_length_); } +TEST_P(SpdyFramerTest, TooLargeHeadersFrameUsesContinuation) { + if (spdy_version_ <= SPDY3) { + return; + } + SpdyFramer framer(spdy_version_); + framer.set_enable_compression(false); + SpdyHeadersIR headers(1); + + // Exact payload length will change with HPACK, but this should be long + // enough to cause an overflow. + const size_t kBigValueSize = framer.GetControlFrameBufferMaxSize(); + string big_value(kBigValueSize, 'x'); + headers.SetHeader("aa", big_value); + scoped_ptr<SpdyFrame> control_frame(framer.SerializeHeaders(headers)); + EXPECT_TRUE(control_frame.get() != NULL); + EXPECT_GT(control_frame->size(), framer.GetControlFrameBufferMaxSize()); + + 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.headers_frame_count_); + EXPECT_EQ(1, visitor.continuation_count_); + EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); +} + +TEST_P(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation) { + if (spdy_version_ <= SPDY3) { + return; + } + SpdyFramer framer(spdy_version_); + framer.set_enable_compression(false); + SpdyPushPromiseIR push_promise(1, 2); + + // Exact payload length will change with HPACK, but this should be long + // enough to cause an overflow. + const size_t kBigValueSize = framer.GetControlFrameBufferMaxSize(); + string big_value(kBigValueSize, 'x'); + push_promise.SetHeader("aa", big_value); + scoped_ptr<SpdyFrame> control_frame( + framer.SerializePushPromise(push_promise)); + EXPECT_TRUE(control_frame.get() != NULL); + EXPECT_GT(control_frame->size(), framer.GetControlFrameBufferMaxSize()); + + 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.push_promise_frame_count_); + EXPECT_EQ(1, visitor.continuation_count_); + EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); +} + // Check that the framer stops delivering header data chunks once the visitor // declares it doesn't want any more. This is important to guard against // "zip bomb" types of attacks. TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) { - SpdyHeaderBlock headers; 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'); - headers["aa"] = big_value.c_str(); SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> control_frame( - framer.CreateSynStream(1, // stream_id - 0, // associated_stream_id - 1, // priority - 0, // credential_slot - CONTROL_FLAG_FIN, // half close - &headers)); + SpdySynStreamIR syn_stream(1); + syn_stream.set_priority(1); + syn_stream.set_fin(true); + syn_stream.SetHeader("aa", big_value); + 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); @@ -3360,20 +3268,19 @@ TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) { } TEST_P(SpdyFramerTest, DecompressCorruptHeaderBlock) { - SpdyHeaderBlock headers; - headers["aa"] = "alpha beta gamma delta"; + if (spdy_version_ > SPDY3) { + // Deflate compression doesn't apply to HPACK. + return; + } SpdyFramer framer(spdy_version_); framer.set_enable_compression(false); // Construct a SYN_STREAM control frame without compressing the header block, // and have the framer try to decompress it. This will cause the framer to // deal with a decompression error. - scoped_ptr<SpdyFrame> control_frame( - framer.CreateSynStream(1, // stream_id - 0, // associated_stream_id - 1, // priority - 0, // credential_slot - CONTROL_FLAG_NONE, - &headers)); + 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( @@ -3386,6 +3293,7 @@ TEST_P(SpdyFramerTest, DecompressCorruptHeaderBlock) { } TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) { + SpdyFramer framer(spdy_version_); // Create a GoAway frame that has a few extra bytes at the end. // We create enough overhead to overflow the framer's control frame buffer. ASSERT_GE(250u, SpdyFramer::kControlFrameBufferSize); @@ -3393,15 +3301,20 @@ TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) { const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_ch_, 0x00, 0x07, 0x00, 0x00, 0x00, length, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // Stream ID + 0x00, 0x00, 0x00, 0x00, // Status }; + + // SPDY version 4 and up GOAWAY frames are only bound to a minimal length, + // since it may carry opaque data. Verify that minimal length is tested. + const unsigned char less_than_min_length = + framer.GetGoAwayMinimumSize() - framer.GetControlFrameHeaderSize() - 1; const unsigned char kV4FrameData[] = { - 0x00, static_cast<uint8>(length + 4), 0x07, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, static_cast<uint8>(less_than_min_length), 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // Stream Id + 0x00, 0x00, 0x00, 0x00, // Status }; - SpdyFramer framer(spdy_version_); const size_t pad_length = length + framer.GetControlFrameHeaderSize() - (IsSpdy4() ? sizeof(kV4FrameData) : sizeof(kV3FrameData)); @@ -3426,49 +3339,65 @@ TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) { TEST_P(SpdyFramerTest, ReadZeroLenSettingsFrame) { SpdyFramer framer(spdy_version_); - SettingsMap settings; - scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings)); + 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()); - // Should generate an error, since zero-len settings frames are unsupported. - EXPECT_EQ(1, visitor.error_count_); + if (spdy_version_ <= SPDY3) { + // Should generate an error, since zero-len settings frames are unsupported. + EXPECT_EQ(1, visitor.error_count_); + } else { + // Zero-len settings frames are permitted as of SPDY 4. + EXPECT_EQ(0, visitor.error_count_); + } } // Tests handling of SETTINGS frames with invalid length. TEST_P(SpdyFramerTest, ReadBogusLenSettingsFrame) { SpdyFramer framer(spdy_version_); - SettingsMap settings; + SpdySettingsIR settings_ir; + // Add a setting to pad the frame so that we don't get a buffer overflow when // calling SimulateInFramer() below. - settings[SETTINGS_UPLOAD_BANDWIDTH] = - SettingsFlagsAndValue(SETTINGS_FLAG_PLEASE_PERSIST, 0x00000002); - scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings)); - const size_t kNewLength = 5; + 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); - // Should generate an error, since zero-len settings frames are unsupported. + // Should generate an error, since its not possible to have a + // settings frame of length kNewLength. EXPECT_EQ(1, visitor.error_count_); } // Tests handling of SETTINGS frames larger than the frame buffer size. TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) { SpdyFramer framer(spdy_version_); - SettingsMap settings; - SpdySettingsFlags flags = SETTINGS_FLAG_PLEASE_PERSIST; - settings[SETTINGS_UPLOAD_BANDWIDTH] = - SettingsFlagsAndValue(flags, 0x00000002); - settings[SETTINGS_DOWNLOAD_BANDWIDTH] = - SettingsFlagsAndValue(flags, 0x00000003); - settings[SETTINGS_ROUND_TRIP_TIME] = SettingsFlagsAndValue(flags, 0x00000004); - scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings)); + SpdySettingsIR settings_ir; + settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 1), + false, // persist + false, // persisted + 5); + settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 2), + false, // persist + false, // persisted + 6); + settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 3), + false, // persist + false, // persisted + 7); + + scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir)); EXPECT_LT(SpdyFramer::kControlFrameBufferSize, control_frame->size()); TestSpdyVisitor visitor(spdy_version_); @@ -3479,7 +3408,10 @@ TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) { reinterpret_cast<unsigned char*>(control_frame->data()), control_frame->size()); EXPECT_EQ(0, visitor.error_count_); - EXPECT_EQ(settings.size(), static_cast<unsigned>(visitor.setting_count_)); + EXPECT_EQ(3, visitor.setting_count_); + if (spdy_version_ > SPDY3) { + EXPECT_EQ(1, visitor.settings_ack_sent_); + } // Read data in small chunks. size_t framed_data = 0; @@ -3494,7 +3426,10 @@ TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) { framed_data += to_read; } EXPECT_EQ(0, visitor.error_count_); - EXPECT_EQ(settings.size() * 2, static_cast<unsigned>(visitor.setting_count_)); + EXPECT_EQ(3 * 2, visitor.setting_count_); + if (spdy_version_ > SPDY3) { + EXPECT_EQ(2, visitor.settings_ack_sent_); + } } // Tests handling of SETTINGS frame with duplicate entries. @@ -3524,14 +3459,13 @@ TEST_P(SpdyFramerTest, ReadDuplicateSettings) { 0x00, 0x00, 0x00, 0x03, }; const unsigned char kV4FrameData[] = { - 0x00, 0x24, 0x04, 0x00, + 0x00, 0x0f, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x01, // 1st Setting + 0x01, // 1st Setting 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x01, // 2nd (duplicate) Setting + 0x01, // 2nd (duplicate) Setting 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x03, // 3rd (unprocessed) Setting + 0x03, // 3rd (unprocessed) Setting 0x00, 0x00, 0x00, 0x03, }; @@ -3544,8 +3478,36 @@ TEST_P(SpdyFramerTest, ReadDuplicateSettings) { } else { visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData)); } - EXPECT_EQ(1, visitor.error_count_); + + if (!IsSpdy4()) { + EXPECT_EQ(1, visitor.setting_count_); + EXPECT_EQ(1, visitor.error_count_); + } else { + // In SPDY 4+, duplicate settings are allowed; + // each setting replaces the previous value for that setting. + EXPECT_EQ(3, visitor.setting_count_); + EXPECT_EQ(0, visitor.error_count_); + EXPECT_EQ(1, visitor.settings_ack_sent_); + } +} + +// Tests handling of SETTINGS_COMPRESS_DATA. +TEST_P(SpdyFramerTest, AcceptSettingsCompressData) { + if (!IsSpdy4()) { return; } + SpdyFramer framer(spdy_version_); + + const unsigned char kFrameData[] = { + 0x00, 0x05, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, + 0x01, + }; + + TestSpdyVisitor visitor(spdy_version_); + visitor.use_compression_ = false; + visitor.SimulateInFramer(kFrameData, sizeof(kFrameData)); EXPECT_EQ(1, visitor.setting_count_); + EXPECT_EQ(0, visitor.error_count_); } // Tests handling of SETTINGS frame with entries out of order. @@ -3575,14 +3537,13 @@ TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) { 0x00, 0x00, 0x00, 0x03, }; const unsigned char kV4FrameData[] = { - 0x00, 0x24, 0x04, 0x00, + 0x00, 0x0f, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x02, // 1st Setting + 0x02, // 1st Setting 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x01, // 2nd (out of order) Setting + 0x01, // 2nd (out of order) Setting 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x01, 0x03, // 3rd (unprocessed) Setting + 0x03, // 3rd (unprocessed) Setting 0x00, 0x00, 0x00, 0x03, }; @@ -3595,14 +3556,111 @@ TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) { } else { visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData)); } - EXPECT_EQ(1, visitor.error_count_); - EXPECT_EQ(1, visitor.setting_count_); + + if (!IsSpdy4()) { + EXPECT_EQ(1, visitor.setting_count_); + EXPECT_EQ(1, visitor.error_count_); + } else { + // In SPDY 4+, settings are allowed in any order. + EXPECT_EQ(3, visitor.setting_count_); + EXPECT_EQ(0, visitor.error_count_); + // EXPECT_EQ(1, visitor.settings_ack_count_); + } +} + +TEST_P(SpdyFramerTest, ProcessSettingsAckFrame) { + if (spdy_version_ <= SPDY3) { + 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_ <= SPDY3) { + return; + } + + const int kPaddingLen = 512; // So we get two bytes for padding length field. + 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; + + // Send the frame header. + 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; + + // Send the first byte of the padding length field. + 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; + + // Send the second byte of the padding length field. + 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; + + // Send the first two bytes of the data payload. + 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; + + // Send the rest three bytes of the data payload. + 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; + + // Send the first 100 bytes of the padding payload. + 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; + + // Send rest of the padding payload. + 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.CreateWindowUpdate(1, 2)); + framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 2))); TestSpdyVisitor visitor(spdy_version_); visitor.SimulateInFramer( reinterpret_cast<unsigned char*>(control_frame->data()), @@ -3611,180 +3669,446 @@ TEST_P(SpdyFramerTest, ReadWindowUpdate) { EXPECT_EQ(2u, visitor.last_window_update_delta_); } -TEST_P(SpdyFramerTest, ReadCredentialFrame) { - SpdyCredential credential; - credential.slot = 3; - credential.proof = "proof"; - credential.certs.push_back("a cert"); - credential.certs.push_back("another cert"); - credential.certs.push_back("final cert"); +TEST_P(SpdyFramerTest, ReceiveCredentialFrame) { + if (!IsSpdy3()) { + return; + } SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> control_frame( - framer.CreateCredentialFrame(credential)); - EXPECT_TRUE(control_frame.get() != NULL); + const unsigned char kV3FrameData[] = { // Also applies for V2. + 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[] = { // Also applies for V2. + 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(SpdyWindowUpdateIR(1, 2))); + multiple_frame_data.append(string(control_frame->data(), + control_frame->size())); visitor.SimulateInFramer( - reinterpret_cast<unsigned char*>(control_frame->data()), - control_frame->size()); + reinterpret_cast<unsigned const char*>(multiple_frame_data.data()), + multiple_frame_data.length()); EXPECT_EQ(0, visitor.error_count_); - EXPECT_EQ(control_frame->size() - framer.GetControlFrameHeaderSize(), - visitor.credential_buffer_length_); - EXPECT_EQ(credential.slot, visitor.credential_.slot); - EXPECT_EQ(credential.proof, visitor.credential_.proof); - EXPECT_EQ(credential.certs.size(), visitor.credential_.certs.size()); - for (size_t i = 0; i < credential.certs.size(); i++) { - EXPECT_EQ(credential.certs[i], visitor.credential_.certs[i]); + EXPECT_EQ(1u, visitor.last_window_update_stream_); + EXPECT_EQ(2u, visitor.last_window_update_delta_); +} + +TEST_P(SpdyFramerTest, CreateContinuationUncompressed) { + if (spdy_version_ <= SPDY3) { + return; } + + SpdyFramer framer(spdy_version_); + framer.set_enable_compression(false); + const char kDescription[] = "CONTINUATION frame"; + + const unsigned char kFrameData[] = { + 0x00, 0x12, 0x09, 0x00, // CONTINUATION + 0x00, 0x00, 0x00, 0x2a, // Stream 42 + 0x00, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x00, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x03, 0x62, // oo.b + 0x61, 0x72, // ar + }; + + 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, ReadCredentialFrameOneByteAtATime) { - SpdyCredential credential; - credential.slot = 3; - credential.proof = "proof"; - credential.certs.push_back("a cert"); - credential.certs.push_back("another cert"); - credential.certs.push_back("final cert"); +TEST_P(SpdyFramerTest, ReadCompressedPushPromise) { + if (spdy_version_ <= SPDY3) { + return; + } + SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> control_frame( - framer.CreateCredentialFrame(credential)); - EXPECT_TRUE(control_frame.get() != NULL); + 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_ = false; - // Read one byte at a time to make sure we handle edge cases - unsigned char* data = - reinterpret_cast<unsigned char*>(control_frame->data()); - for (size_t idx = 0; idx < control_frame->size(); ++idx) { - visitor.SimulateInFramer(data + idx, 1); - ASSERT_EQ(0, visitor.error_count_); + 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, ReadHeadersWithContinuationAndPadding) { + if (spdy_version_ <= SPDY3) { + return; } + + const unsigned char kInput[] = { + 0x00, 0x14, 0x01, 0x08, // HEADERS: PAD_LOW + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x03, // Padding of 3. + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + 0x00, 0x00, 0x00, + + 0x00, 0x1a, 0x09, 0x18, // CONTINUATION: PAD_LOW & PAD_HIGH + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x04, // Padding of 4. + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x08, 0x62, 0x61, 0x7a, + 0x3d, 0x62, 0x69, 0x6e, + 0x67, 0x00, 0x06, 0x63, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x13, 0x09, 0x0c, // CONTINUATION: PAD_LOW & END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, // Padding of 0. + 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x00, 0x00, 0x04, + 0x6e, 0x61, 0x6d, 0x65, + 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, + }; + + TestSpdyVisitor visitor(spdy_version_); + visitor.SimulateInFramer(kInput, sizeof(kInput)); + EXPECT_EQ(0, visitor.error_count_); - EXPECT_EQ(control_frame->size() - framer.GetControlFrameHeaderSize(), - visitor.credential_buffer_length_); - EXPECT_EQ(credential.slot, visitor.credential_.slot); - EXPECT_EQ(credential.proof, visitor.credential_.proof); - EXPECT_EQ(credential.certs.size(), visitor.credential_.certs.size()); - for (size_t i = 0; i < credential.certs.size(); i++) { - EXPECT_EQ(credential.certs[i], visitor.credential_.certs[i]); + 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_ <= SPDY3) { + return; } + + const unsigned char kInput[] = { + 0x00, 0x10, 0x01, 0x01, // HEADERS: FIN + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + + 0x00, 0x14, 0x09, 0x00, // CONTINUATION + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x08, 0x62, 0x61, 0x7a, + 0x3d, 0x62, 0x69, 0x6e, + 0x67, 0x00, 0x06, 0x63, + + 0x00, 0x12, 0x09, 0x04, // CONTINUATION: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x00, 0x00, 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, ReadCredentialFrameWithNoPayload) { - SpdyCredential credential; - credential.slot = 3; - credential.proof = "proof"; - credential.certs.push_back("a cert"); - credential.certs.push_back("another cert"); - credential.certs.push_back("final cert"); +TEST_P(SpdyFramerTest, ReadPushPromiseWithContinuationAndPadding) { + if (spdy_version_ <= SPDY3) { + return; + } + + const unsigned char kInput[] = { + 0x00, 0x18, 0x05, 0x18, // PUSH_PROMISE: PAD_LOW & PAD_HIGH + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x00, 0x00, 0x2A, // Promised stream 42 + 0x00, 0x02, // Padding of 2. + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + 0x00, 0x00, + + 0x00, 0x14, 0x09, 0x00, // CONTINUATION: + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x08, 0x62, 0x61, 0x7a, + 0x3d, 0x62, 0x69, 0x6e, + 0x67, 0x00, 0x06, 0x63, + + 0x00, 0x17, 0x09, 0x0c, // CONTINUATION: PAD_LOW & END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x04, // Padding of 4. + 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x00, 0x00, 0x04, + 0x6e, 0x61, 0x6d, 0x65, + 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x00, 0x00, + 0x00, 0x00, + }; + SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> control_frame( - framer.CreateCredentialFrame(credential)); - EXPECT_TRUE(control_frame.get() != NULL); TestSpdyVisitor visitor(spdy_version_); - visitor.use_compression_ = false; - SetFrameLength(control_frame.get(), 0, spdy_version_); - unsigned char* data = - reinterpret_cast<unsigned char*>(control_frame->data()); - visitor.SimulateInFramer(data, framer.GetControlFrameHeaderSize()); + 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_ <= SPDY3) { + return; + } + + const unsigned char kInput[] = { + 0x00, 0x10, 0x01, 0x00, // HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + + 0x00, 0x14, 0x09, 0x00, // CONTINUATION + 0x00, 0x00, 0x00, 0x02, // Stream 2 + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x08, 0x62, 0x61, 0x7a, + 0x3d, 0x62, 0x69, 0x6e, + 0x67, 0x00, 0x06, 0x63, + }; + + 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, ReadCredentialFrameWithCorruptProof) { - SpdyCredential credential; - credential.slot = 3; - credential.proof = "proof"; - credential.certs.push_back("a cert"); - credential.certs.push_back("another cert"); - credential.certs.push_back("final cert"); +TEST_P(SpdyFramerTest, ReadContinuationOutOfOrder) { + if (spdy_version_ <= SPDY3) { + return; + } + + const unsigned char kInput[] = { + 0x00, 0x10, 0x09, 0x00, // CONTINUATION + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + }; + SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> control_frame( - framer.CreateCredentialFrame(credential)); - EXPECT_TRUE(control_frame.get() != NULL); TestSpdyVisitor visitor(spdy_version_); - visitor.use_compression_ = false; - unsigned char* data = - reinterpret_cast<unsigned char*>(control_frame->data()); - size_t offset = framer.GetControlFrameHeaderSize() + 4; - data[offset] = 0xFF; // Proof length is past the end of the frame - visitor.SimulateInFramer( - data, control_frame->size()); + 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, ReadCredentialFrameWithCorruptCertificate) { - SpdyCredential credential; - credential.slot = 3; - credential.proof = "proof"; - credential.certs.push_back("a cert"); - credential.certs.push_back("another cert"); - credential.certs.push_back("final cert"); +TEST_P(SpdyFramerTest, ExpectContinuationReceiveData) { + if (spdy_version_ <= SPDY3) { + return; + } + + const unsigned char kInput[] = { + 0x00, 0x10, 0x01, 0x00, // HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + + 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1 + 0x00, 0x00, 0x00, 0x04, + 0xde, 0xad, 0xbe, 0xef, + }; + SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> control_frame( - framer.CreateCredentialFrame(credential)); - EXPECT_TRUE(control_frame.get() != NULL); TestSpdyVisitor visitor(spdy_version_); - visitor.use_compression_ = false; - unsigned char* data = - reinterpret_cast<unsigned char*>(control_frame->data()); - size_t offset = framer.GetCredentialMinimumSize() + 1; - data[offset] = 0xFF; // Proof length is past the end of the frame - visitor.SimulateInFramer( - data, control_frame->size()); + 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_); } -// Regression test for parsing issue found in b/8278897. -TEST_P(SpdyFramerTest, ReadCredentialFrameFollowedByAnotherFrame) { - SpdyCredential credential; - credential.slot = 3; - credential.proof = "proof"; - credential.certs.push_back("a cert"); - credential.certs.push_back("another cert"); - credential.certs.push_back("final cert"); +TEST_P(SpdyFramerTest, ExpectContinuationReceiveControlFrame) { + if (spdy_version_ <= SPDY3) { + return; + } + + const unsigned char kInput[] = { + 0x00, 0x10, 0x01, 0x00, // HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + + 0x00, 0x14, 0x08, 0x00, // HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x06, 0x63, 0x6f, // (Note this is a valid continued encoding). + 0x6f, 0x6b, 0x69, 0x65, + 0x08, 0x62, 0x61, 0x7a, + 0x3d, 0x62, 0x69, 0x6e, + 0x67, 0x00, 0x06, 0x63, + }; + SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> credential_frame( - framer.CreateCredentialFrame(credential)); - EXPECT_TRUE(credential_frame.get() != NULL); TestSpdyVisitor visitor(spdy_version_); - visitor.use_compression_ = false; - string multiple_frame_data(credential_frame->data(), - credential_frame->size()); - scoped_ptr<SpdyFrame> goaway_frame(framer.CreateGoAway(0, GOAWAY_OK)); - multiple_frame_data.append(string(goaway_frame->data(), - goaway_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(credential_frame->size() - framer.GetControlFrameHeaderSize(), - visitor.credential_buffer_length_); - EXPECT_EQ(credential.slot, visitor.credential_.slot); - EXPECT_EQ(credential.proof, visitor.credential_.proof); - EXPECT_EQ(credential.certs.size(), visitor.credential_.certs.size()); - for (size_t i = 0; i < credential.certs.size(); i++) { - EXPECT_EQ(credential.certs[i], visitor.credential_.certs[i]); + 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, EndSegmentOnDataFrame) { + if (spdy_version_ <= SPDY3) { + return; } + const unsigned char kInput[] = { + 0x00, 0x0c, 0x00, 0x02, // DATA: END_SEGMENT + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, + }; + + TestSpdyVisitor visitor(spdy_version_); + visitor.SimulateInFramer(kInput, sizeof(kInput)); + + // TODO(jgraettinger): Verify END_SEGMENT when support is added. + EXPECT_EQ(0, visitor.error_count_); + EXPECT_EQ(12, visitor.data_bytes_); + EXPECT_EQ(0, visitor.fin_frame_count_); + EXPECT_EQ(0, visitor.fin_flag_count_); } -TEST_P(SpdyFramerTest, ReadCompressedPushPromise) { - if (spdy_version_ < 4) { +TEST_P(SpdyFramerTest, EndSegmentOnHeadersFrame) { + if (spdy_version_ <= SPDY3) { return; } + const unsigned char kInput[] = { + 0x00, 0x10, 0x01, 0x06, // HEADERS: END_SEGMENT | END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x06, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + }; - SpdyHeaderBlock headers; - headers["foo"] = "bar"; - headers["bar"] = "foofoo"; - SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> frame(framer.CreatePushPromise(42, 57, &headers)); - 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_)); + visitor.SimulateInFramer(kInput, sizeof(kInput)); + + // TODO(jgraettinger): Verify END_SEGMENT when support is added. + EXPECT_EQ(0, visitor.error_count_); + EXPECT_EQ(1, visitor.headers_frame_count_); + EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); + + EXPECT_THAT(visitor.headers_, ElementsAre( + Pair("cookie", "foo=bar"))); } TEST_P(SpdyFramerTest, ReadGarbage) { @@ -3797,6 +4121,23 @@ TEST_P(SpdyFramerTest, ReadGarbage) { 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()) { // Not valid for SPDY 4 since there is no version field. @@ -3813,37 +4154,55 @@ TEST_P(SpdyFramerTest, ReadGarbageWithValidVersion) { EXPECT_EQ(1, visitor.error_count_); } +TEST_P(SpdyFramerTest, ReadGarbageHPACKEncoding) { + if (spdy_version_ <= SPDY3) { + return; + } + const unsigned char kInput[] = { + 0x00, 0x12, 0x01, 0x04, // HEADER: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0xef, 0xef, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, + }; + + TestSpdyVisitor visitor(spdy_version_); + visitor.SimulateInFramer(kInput, arraysize(kInput)); + EXPECT_EQ(1, visitor.error_count_); +} + TEST_P(SpdyFramerTest, SizesTest) { SpdyFramer framer(spdy_version_); EXPECT_EQ(8u, framer.GetDataFrameMinimumSize()); - if (IsSpdy4()) { + if (IsSpdy4() || IsSpdy5()) { EXPECT_EQ(8u, framer.GetSynReplyMinimumSize()); - EXPECT_EQ(12u, framer.GetRstStreamSize()); - EXPECT_EQ(12u, framer.GetSettingsMinimumSize()); - EXPECT_EQ(12u, framer.GetPingSize()); - EXPECT_EQ(16u, framer.GetGoAwaySize()); + 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(10u, framer.GetCredentialMinimumSize()); EXPECT_EQ(8u, framer.GetBlockedSize()); EXPECT_EQ(12u, framer.GetPushPromiseMinimumSize()); + EXPECT_EQ(17u, framer.GetAltSvcMinimumSize()); EXPECT_EQ(8u, framer.GetFrameMinimumSize()); - EXPECT_EQ(65535u, framer.GetFrameMaximumSize()); - EXPECT_EQ(65527u, framer.GetDataFrameMaximumPayload()); + 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.GetRstStreamSize()); + EXPECT_EQ(16u, framer.GetRstStreamMinimumSize()); EXPECT_EQ(12u, framer.GetSettingsMinimumSize()); EXPECT_EQ(12u, framer.GetPingSize()); - EXPECT_EQ(IsSpdy2() ? 12u : 16u, framer.GetGoAwaySize()); + EXPECT_EQ(IsSpdy2() ? 12u : 16u, framer.GetGoAwayMinimumSize()); EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetHeadersMinimumSize()); EXPECT_EQ(16u, framer.GetWindowUpdateSize()); - EXPECT_EQ(10u, framer.GetCredentialMinimumSize()); EXPECT_EQ(8u, framer.GetFrameMinimumSize()); - EXPECT_EQ(16777215u, framer.GetFrameMaximumSize()); - EXPECT_EQ(16777207u, framer.GetDataFrameMaximumPayload()); + EXPECT_EQ(16777223u, framer.GetFrameMaximumSize()); + EXPECT_EQ(16777215u, framer.GetDataFrameMaximumPayload()); } } @@ -3872,15 +4231,15 @@ TEST_P(SpdyFramerTest, StateToStringTest) { EXPECT_STREQ("SPDY_CONTROL_FRAME_HEADER_BLOCK", SpdyFramer::StateToString( SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK)); - EXPECT_STREQ("SPDY_CREDENTIAL_FRAME_PAYLOAD", - SpdyFramer::StateToString( - SpdyFramer::SPDY_CREDENTIAL_FRAME_PAYLOAD)); EXPECT_STREQ("SPDY_SETTINGS_FRAME_PAYLOAD", SpdyFramer::StateToString( SpdyFramer::SPDY_SETTINGS_FRAME_PAYLOAD)); + EXPECT_STREQ("SPDY_ALTSVC_FRAME_PAYLOAD", + SpdyFramer::StateToString( + SpdyFramer::SPDY_ALTSVC_FRAME_PAYLOAD)); EXPECT_STREQ("UNKNOWN_STATE", SpdyFramer::StateToString( - SpdyFramer::SPDY_SETTINGS_FRAME_PAYLOAD + 1)); + SpdyFramer::SPDY_ALTSVC_FRAME_PAYLOAD + 1)); } TEST_P(SpdyFramerTest, ErrorCodeToStringTest) { @@ -3932,7 +4291,7 @@ TEST_P(SpdyFramerTest, StatusCodeToStringTest) { EXPECT_STREQ("FLOW_CONTROL_ERROR", SpdyFramer::StatusCodeToString(RST_STREAM_FLOW_CONTROL_ERROR)); EXPECT_STREQ("UNKNOWN_STATUS", - SpdyFramer::StatusCodeToString(RST_STREAM_NUM_STATUS_CODES)); + SpdyFramer::StatusCodeToString(-1)); } TEST_P(SpdyFramerTest, FrameTypeToStringTest) { @@ -3960,6 +4319,8 @@ TEST_P(SpdyFramerTest, FrameTypeToStringTest) { SpdyFramer::FrameTypeToString(PUSH_PROMISE)); EXPECT_STREQ("CREDENTIAL", SpdyFramer::FrameTypeToString(CREDENTIAL)); + EXPECT_STREQ("CONTINUATION", + SpdyFramer::FrameTypeToString(CONTINUATION)); } TEST_P(SpdyFramerTest, CatchProbableHttpResponse) { @@ -3968,7 +4329,7 @@ TEST_P(SpdyFramerTest, CatchProbableHttpResponse) { return; } { - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3980,7 +4341,7 @@ TEST_P(SpdyFramerTest, CatchProbableHttpResponse) { << SpdyFramer::ErrorCodeToString(framer.error_code()); } { - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3993,16 +4354,20 @@ TEST_P(SpdyFramerTest, CatchProbableHttpResponse) { } } -TEST_P(SpdyFramerTest, DataFrameFlags) { +TEST_P(SpdyFramerTest, DataFrameFlagsV2V3) { + if (spdy_version_ > SPDY3) { + return; + } + for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - scoped_ptr<SpdyFrame> frame( - framer.CreateDataFrame(1, "hello", 5, DATA_FLAG_NONE)); + 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) { @@ -4029,11 +4394,60 @@ TEST_P(SpdyFramerTest, DataFrameFlags) { } } +TEST_P(SpdyFramerTest, DataFrameFlagsV4) { + if (spdy_version_ <= SPDY3) { + 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); + + 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 Error since we don't set pad_high and pad_low in payload. + 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::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; testing::StrictMock<test::MockDebugVisitor> debug_visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -4041,27 +4455,67 @@ TEST_P(SpdyFramerTest, SynStreamFrameFlags) { EXPECT_CALL(debug_visitor, OnSendCompressedFrame(8, SYN_STREAM, _, _)); - SpdyHeaderBlock headers; - headers["foo"] = "bar"; - scoped_ptr<SpdyFrame> frame( - framer.CreateSynStream(8, 3, 1, 0, CONTROL_FLAG_NONE, &headers)); - SetFrameFlags(frame.get(), flags, spdy_version_); + 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()) { + // PRIORITY required for SYN_STREAM simulation. + set_flags |= HEADERS_FLAG_PRIORITY; - if (flags & ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) { + // TODO(jgraettinger): Add padding to SynStreamIR, and implement framing. + set_flags &= ~HEADERS_FLAG_PAD_LOW; + set_flags &= ~HEADERS_FLAG_PAD_HIGH; + } + 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 | + HEADERS_FLAG_END_SEGMENT | + HEADERS_FLAG_PAD_LOW | + HEADERS_FLAG_PAD_HIGH)) { EXPECT_CALL(visitor, OnError(_)); } else { EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(8, SYN_STREAM, _)); - EXPECT_CALL(visitor, OnSynStream(8, 3, 1, 0, flags & CONTROL_FLAG_FIN, - flags & CONTROL_FLAG_UNIDIRECTIONAL)); + 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) { + if (flags & DATA_FLAG_FIN && (!IsSpdy4() || + flags & HEADERS_FLAG_END_HEADERS)) { EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true)); + } else { + // Do not close the stream if we are expecting a CONTINUATION frame. + EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true)).Times(0); } } framer.ProcessInput(frame->data(), frame->size()); - if (flags & ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) { + 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 | + HEADERS_FLAG_END_SEGMENT | + HEADERS_FLAG_PAD_LOW | + HEADERS_FLAG_PAD_HIGH)) { EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS, framer.error_code()) @@ -4075,17 +4529,20 @@ TEST_P(SpdyFramerTest, SynStreamFrameFlags) { } TEST_P(SpdyFramerTest, SynReplyFrameFlags) { + if (IsSpdy4()) { + // Covered by HEADERS case. + return; + } for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - SpdyHeaderBlock headers; - headers["foo"] = "bar"; - scoped_ptr<SpdyFrame> frame( - framer.CreateSynReply(37, CONTROL_FLAG_NONE, &headers)); + 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) { @@ -4117,11 +4574,12 @@ TEST_P(SpdyFramerTest, RstStreamFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(13, RST_STREAM_CANCEL)); + SpdyRstStreamIR rst_stream(13, RST_STREAM_CANCEL, ""); + scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream)); SetFrameFlags(frame.get(), flags, spdy_version_); if (flags != 0) { @@ -4144,18 +4602,21 @@ TEST_P(SpdyFramerTest, RstStreamFrameFlags) { } } -TEST_P(SpdyFramerTest, SettingsFrameFlags) { +TEST_P(SpdyFramerTest, SettingsFrameFlagsOldFormat) { + if (spdy_version_ > SPDY3) { return; } for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - SettingsMap settings; - settings[SETTINGS_UPLOAD_BANDWIDTH] = - std::make_pair(SETTINGS_FLAG_NONE, 54321); - scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings)); + 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) { @@ -4165,6 +4626,7 @@ TEST_P(SpdyFramerTest, SettingsFrameFlags) { 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()); @@ -4181,15 +4643,58 @@ TEST_P(SpdyFramerTest, SettingsFrameFlags) { } } +TEST_P(SpdyFramerTest, SettingsFrameFlags) { + if (spdy_version_ <= SPDY3) { 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) { + // The frame is invalid because ACK frames should have no payload. + 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::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(97, GOAWAY_OK)); + SpdyGoAwayIR goaway_ir(97, GOAWAY_OK, "test"); + scoped_ptr<SpdyFrame> frame(framer.SerializeGoAway(goaway_ir)); SetFrameFlags(frame.get(), flags, spdy_version_); if (flags != 0) { @@ -4214,35 +4719,71 @@ TEST_P(SpdyFramerTest, GoawayFrameFlags) { TEST_P(SpdyFramerTest, HeadersFrameFlags) { for (int flags = 0; flags < 256; ++flags) { + if (IsSpdy4() && flags & HEADERS_FLAG_PRIORITY) { + // Covered by SYN_STREAM case. + continue; + } SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - SpdyHeaderBlock headers; - headers["foo"] = "bar"; - scoped_ptr<SpdyFrame> frame( - framer.CreateHeaders(57, CONTROL_FLAG_NONE, &headers)); - SetFrameFlags(frame.get(), flags, spdy_version_); + SpdyHeadersIR headers_ir(57); + headers_ir.SetHeader("foo", "bar"); + scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir)); + int set_flags = flags; + if (IsSpdy4()) { + // TODO(jgraettinger): Add padding to SpdyHeadersIR, + // and implement framing. + set_flags &= ~HEADERS_FLAG_PAD_LOW; + set_flags &= ~HEADERS_FLAG_PAD_HIGH; + } + SetFrameFlags(frame.get(), set_flags, spdy_version_); - if (flags & ~CONTROL_FLAG_FIN) { + if (!IsSpdy4() && flags & ~CONTROL_FLAG_FIN) { + EXPECT_CALL(visitor, OnError(_)); + } else if (IsSpdy4() && flags & ~(CONTROL_FLAG_FIN | + HEADERS_FLAG_END_HEADERS | + HEADERS_FLAG_END_SEGMENT | + HEADERS_FLAG_PAD_LOW | + HEADERS_FLAG_PAD_HIGH)) { EXPECT_CALL(visitor, OnError(_)); } else { - EXPECT_CALL(visitor, OnHeaders(57, flags & CONTROL_FLAG_FIN)); + 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) { + if (flags & DATA_FLAG_FIN && (!IsSpdy4() || + flags & HEADERS_FLAG_END_HEADERS)) { EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true)); + } else { + // Do not close the stream if we are expecting a CONTINUATION frame. + EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true)).Times(0); } } framer.ProcessInput(frame->data(), frame->size()); - if (flags & ~CONTROL_FLAG_FIN) { + 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_END_HEADERS | + HEADERS_FLAG_END_SEGMENT | + HEADERS_FLAG_PAD_LOW | + HEADERS_FLAG_PAD_HIGH)) { + 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()) @@ -4255,29 +4796,33 @@ TEST_P(SpdyFramerTest, PingFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - scoped_ptr<SpdyFrame> frame(framer.CreatePingFrame(42)); + scoped_ptr<SpdyFrame> frame(framer.SerializePing(SpdyPingIR(42))); SetFrameFlags(frame.get(), flags, spdy_version_); - if (flags != 0) { - EXPECT_CALL(visitor, OnError(_)); + if (spdy_version_ > SPDY3 && + flags == PING_FLAG_ACK) { + EXPECT_CALL(visitor, OnPing(42, true)); + } else if (flags == 0) { + EXPECT_CALL(visitor, OnPing(42, false)); } else { - EXPECT_CALL(visitor, OnPing(42)); + EXPECT_CALL(visitor, OnError(_)); } framer.ProcessInput(frame->data(), frame->size()); - if (flags != 0) { + if ((spdy_version_ > SPDY3 && 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()); - } else { - EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state()); - EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) - << SpdyFramer::ErrorCodeToString(framer.error_code()); } } } @@ -4286,11 +4831,12 @@ TEST_P(SpdyFramerTest, WindowUpdateFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(4, 1024)); + scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate( + SpdyWindowUpdateIR(4, 1024))); SetFrameFlags(frame.get(), flags, spdy_version_); if (flags != 0) { @@ -4314,14 +4860,14 @@ TEST_P(SpdyFramerTest, WindowUpdateFrameFlags) { } TEST_P(SpdyFramerTest, PushPromiseFrameFlags) { - if (spdy_version_ < SPDY4) { + if (spdy_version_ <= SPDY3) { return; } for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<net::test::MockSpdyFramerVisitor> visitor; testing::StrictMock<net::test::MockDebugVisitor> debug_visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -4329,22 +4875,31 @@ TEST_P(SpdyFramerTest, PushPromiseFrameFlags) { EXPECT_CALL(debug_visitor, OnSendCompressedFrame(42, PUSH_PROMISE, _, _)); - SpdyHeaderBlock headers; - headers["foo"] = "bar"; - scoped_ptr<SpdyFrame> frame(framer.CreatePushPromise(42, 57, &headers)); - SetFrameFlags(frame.get(), flags, spdy_version_); - - if (flags != 0) { + SpdyPushPromiseIR push_promise(42, 57); + push_promise.SetHeader("foo", "bar"); + scoped_ptr<SpdySerializedFrame> frame( + framer.SerializePushPromise(push_promise)); + // TODO(jgraettinger): Add padding to SpdyPushPromiseIR, + // and implement framing. + int set_flags = flags & ~HEADERS_FLAG_PAD_LOW & ~HEADERS_FLAG_PAD_HIGH; + SetFrameFlags(frame.get(), set_flags, spdy_version_); + + if (flags & ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE | + HEADERS_FLAG_PAD_LOW | + HEADERS_FLAG_PAD_HIGH)) { EXPECT_CALL(visitor, OnError(_)); } else { EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, PUSH_PROMISE, _)); - EXPECT_CALL(visitor, OnPushPromise(42, 57)); + 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 != 0) { + if (flags & ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE | + HEADERS_FLAG_PAD_LOW | + HEADERS_FLAG_PAD_HIGH)) { EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS, framer.error_code()) @@ -4357,27 +4912,58 @@ TEST_P(SpdyFramerTest, PushPromiseFrameFlags) { } } -TEST_P(SpdyFramerTest, CredentialFrameFlags) { +TEST_P(SpdyFramerTest, ContinuationFrameFlags) { + if (spdy_version_ <= SPDY3) { + return; + } + for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<test::MockVisitor> visitor; + 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); - SpdyCredential credential; - scoped_ptr<SpdyFrame> frame(framer.CreateCredentialFrame(credential)); - SetFrameFlags(frame.get(), flags, spdy_version_); - - if (flags != 0) { + 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"); + scoped_ptr<SpdyFrame> frame0(framer.SerializeHeaders(headers_ir)); + SetFrameFlags(frame0.get(), 0, spdy_version_); + + SpdyContinuationIR continuation(42); + continuation.SetHeader("foo", "bar"); + scoped_ptr<SpdySerializedFrame> frame( + framer.SerializeContinuation(continuation)); + // TODO(jgraettinger): Add padding to the eventual continuation + // serialization implementation. + int set_flags = flags & ~HEADERS_FLAG_PAD_LOW & ~HEADERS_FLAG_PAD_HIGH; + SetFrameFlags(frame.get(), set_flags, spdy_version_); + + if (flags & ~(HEADERS_FLAG_END_HEADERS | + HEADERS_FLAG_PAD_LOW | + HEADERS_FLAG_PAD_HIGH)) { EXPECT_CALL(visitor, OnError(_)); } else { - EXPECT_CALL(visitor, OnCredentialFrameData(_, _)) + 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 != 0) { + if (flags & ~(HEADERS_FLAG_END_HEADERS | + HEADERS_FLAG_PAD_LOW | + HEADERS_FLAG_PAD_HIGH)) { EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS, framer.error_code()) @@ -4390,10 +4976,12 @@ TEST_P(SpdyFramerTest, CredentialFrameFlags) { } } -TEST_P(SpdyFramerTest, EmptySynStream) { - SpdyHeaderBlock headers; +// TODO(mlavan): Add TEST_P(SpdyFramerTest, AltSvcFrameFlags) - testing::StrictMock<test::MockVisitor> visitor; +// TODO(hkhalil): Add TEST_P(SpdyFramerTest, BlockedFrameFlags) + +TEST_P(SpdyFramerTest, EmptySynStream) { + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; testing::StrictMock<test::MockDebugVisitor> debug_visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -4401,23 +4989,17 @@ TEST_P(SpdyFramerTest, EmptySynStream) { EXPECT_CALL(debug_visitor, OnSendCompressedFrame(1, SYN_STREAM, _, _)); - scoped_ptr<SpdyFrame> - frame(framer.CreateSynStream(1, 0, 1, 0, CONTROL_FLAG_NONE, &headers)); + SpdySynStreamIR syn_stream(1); + syn_stream.set_priority(1); + scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream)); // Adjust size to remove the name/value block. - if (IsSpdy4()) { - SetFrameLength( - frame.get(), - framer.GetSynStreamMinimumSize(), - spdy_version_); - } else { - SetFrameLength( - frame.get(), - framer.GetSynStreamMinimumSize() - framer.GetControlFrameHeaderSize(), - spdy_version_); - } + SetFrameLength( + frame.get(), + framer.GetSynStreamMinimumSize() - framer.GetControlFrameHeaderSize(), + spdy_version_); EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(1, SYN_STREAM, _)); - EXPECT_CALL(visitor, OnSynStream(1, 0, 1, 0, false, false)); + EXPECT_CALL(visitor, OnSynStream(1, 0, 1, false, false)); EXPECT_CALL(visitor, OnControlFrameHeaderData(1, NULL, 0)); framer.ProcessInput(frame->data(), framer.GetSynStreamMinimumSize()); @@ -4440,61 +5022,72 @@ TEST_P(SpdyFramerTest, SettingsFlagsAndId) { // Test handling of a RST_STREAM with out-of-bounds status codes. TEST_P(SpdyFramerTest, RstStreamStatusBounds) { - DCHECK_GE(0xff, RST_STREAM_NUM_STATUS_CODES); - + const unsigned char kRstStreamStatusTooLow = 0x00; + const unsigned char kRstStreamStatusTooHigh = 0xff; const unsigned char kV3RstStreamInvalid[] = { 0x80, spdy_version_ch_, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, RST_STREAM_INVALID + 0x00, 0x00, 0x00, kRstStreamStatusTooLow }; const unsigned char kV4RstStreamInvalid[] = { - 0x00, 0x0c, 0x03, 0x00, + 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, RST_STREAM_INVALID + 0x00, 0x00, 0x00, kRstStreamStatusTooLow }; 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 + 0x00, 0x00, 0x00, kRstStreamStatusTooHigh }; const unsigned char kV4RstStreamNumStatusCodes[] = { - 0x00, 0x0c, 0x03, 0x00, + 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, RST_STREAM_NUM_STATUS_CODES + 0x00, 0x00, 0x00, kRstStreamStatusTooHigh }; - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); - EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID)); if (IsSpdy4()) { + EXPECT_CALL(visitor, OnError(_)); framer.ProcessInput(reinterpret_cast<const char*>(kV4RstStreamInvalid), arraysize(kV4RstStreamInvalid)); + EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code()) + << SpdyFramer::ErrorCodeToString(framer.error_code()); } else { + EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID)); 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()) + EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) << SpdyFramer::ErrorCodeToString(framer.error_code()); + } + + + framer.Reset(); - EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID)); if (IsSpdy4()) { + EXPECT_CALL(visitor, OnError(_)); framer.ProcessInput( reinterpret_cast<const char*>(kV4RstStreamNumStatusCodes), arraysize(kV4RstStreamNumStatusCodes)); + EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code()) + << SpdyFramer::ErrorCodeToString(framer.error_code()); } else { + EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID)); 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()) + EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) << SpdyFramer::ErrorCodeToString(framer.error_code()); + } } // Tests handling of a GOAWAY frame with out-of-bounds stream ID. @@ -4511,13 +5104,13 @@ TEST_P(SpdyFramerTest, GoAwayStreamIdBounds) { 0x00, 0x00, 0x00, 0x00, }; const unsigned char kV4FrameData[] = { - 0x00, 0x10, 0x07, 0x00, + 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, }; - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -4538,13 +5131,13 @@ TEST_P(SpdyFramerTest, GoAwayStreamIdBounds) { } TEST_P(SpdyFramerTest, OnBlocked) { - if (spdy_version_ < SPDY4) { + if (spdy_version_ <= SPDY3) { return; } const SpdyStreamId kStreamId = 0; - testing::StrictMock<test::MockVisitor> visitor; + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -4559,4 +5152,281 @@ TEST_P(SpdyFramerTest, OnBlocked) { << SpdyFramer::ErrorCodeToString(framer.error_code()); } +TEST_P(SpdyFramerTest, OnAltSvc) { + if (spdy_version_ <= SPDY3) { + return; + } + + const SpdyStreamId kStreamId = 1; + + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; + SpdyFramer framer(spdy_version_); + framer.set_visitor(&visitor); + + EXPECT_CALL(visitor, OnAltSvc(kStreamId, + 10, + 443, + StringPiece("pid"), + StringPiece("h1"), + StringPiece("o1"))); + + SpdyAltSvcIR altsvc_ir(1); + altsvc_ir.set_max_age(10); + altsvc_ir.set_port(443); + altsvc_ir.set_protocol_id("pid"); + altsvc_ir.set_host("h1"); + altsvc_ir.set_origin("o1"); + scoped_ptr<SpdySerializedFrame> frame(framer.SerializeFrame(altsvc_ir)); + framer.ProcessInput(frame->data(), framer.GetAltSvcMinimumSize() + + altsvc_ir.protocol_id().length() + + altsvc_ir.host().length() + + altsvc_ir.origin().length()); + + EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) + << SpdyFramer::ErrorCodeToString(framer.error_code()); +} + +TEST_P(SpdyFramerTest, OnAltSvcNoOrigin) { + if (spdy_version_ <= SPDY3) { + return; + } + + const SpdyStreamId kStreamId = 1; + + testing::StrictMock<test::MockSpdyFramerVisitor> visitor; + SpdyFramer framer(spdy_version_); + framer.set_visitor(&visitor); + + EXPECT_CALL(visitor, OnAltSvc(kStreamId, + 10, + 443, + StringPiece("pid"), + StringPiece("h1"), + StringPiece(""))); + + SpdyAltSvcIR altsvc_ir(1); + altsvc_ir.set_max_age(10); + altsvc_ir.set_port(443); + altsvc_ir.set_protocol_id("pid"); + altsvc_ir.set_host("h1"); + scoped_ptr<SpdySerializedFrame> frame(framer.SerializeFrame(altsvc_ir)); + framer.ProcessInput(frame->data(), framer.GetAltSvcMinimumSize() + + altsvc_ir.protocol_id().length() + + altsvc_ir.host().length()); + + EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()) + << SpdyFramer::ErrorCodeToString(framer.error_code()); +} + +TEST_P(SpdyFramerTest, OnAltSvcBadLengths) { + if (spdy_version_ <= SPDY3) { + return; + } + + const unsigned char kType = static_cast<unsigned char>( + SpdyConstants::SerializeFrameType(spdy_version_, ALTSVC)); + { + TestSpdyVisitor visitor(spdy_version_); + SpdyFramer framer(spdy_version_); + framer.set_visitor(&visitor); + + const unsigned char kFrameDataLargePIDLen[] = { + 0x00, 0x17, kType, 0x00, + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x05, + 0x01, 0xbb, 0x00, 0x05, // Port = 443 + 'p', 'i', 'd', '1', // Protocol-ID + 0x04, 'h', 'o', 's', + 't', 'o', 'r', 'i', + 'g', 'i', 'n', + }; + + visitor.SimulateInFramer(kFrameDataLargePIDLen, + sizeof(kFrameDataLargePIDLen)); + EXPECT_EQ(1, visitor.error_count_); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, + visitor.framer_.error_code()); + } + + { + TestSpdyVisitor visitor(spdy_version_); + SpdyFramer framer(spdy_version_); + framer.set_visitor(&visitor); + const unsigned char kFrameDataPIDLenLargerThanFrame[] = { + 0x00, 0x17, kType, 0x00, + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x05, + 0x01, 0xbb, 0x00, 0x99, // Port = 443 + 'p', 'i', 'd', '1', // Protocol-ID + 0x04, 'h', 'o', 's', + 't', 'o', 'r', 'i', + 'g', 'i', 'n', + }; + + visitor.SimulateInFramer(kFrameDataPIDLenLargerThanFrame, + sizeof(kFrameDataPIDLenLargerThanFrame)); + EXPECT_EQ(1, visitor.error_count_); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, + visitor.framer_.error_code()); + } + + { + TestSpdyVisitor visitor(spdy_version_); + SpdyFramer framer(spdy_version_); + framer.set_visitor(&visitor); + + const unsigned char kFrameDataLargeHostLen[] = { + 0x00, 0x17, kType, 0x00, + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x05, + 0x01, 0xbb, 0x00, 0x04, // Port = 443 + 'p', 'i', 'd', '1', // Protocol-ID + 0x0f, 'h', 'o', 's', + 't', 'o', 'r', 'i', + 'g', 'i', 'n', + }; + + visitor.SimulateInFramer(kFrameDataLargeHostLen, + sizeof(kFrameDataLargeHostLen)); + EXPECT_EQ(1, visitor.error_count_); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, + visitor.framer_.error_code()); + } + + { + TestSpdyVisitor visitor(spdy_version_); + SpdyFramer framer(spdy_version_); + framer.set_visitor(&visitor); + const unsigned char kFrameDataSmallPIDLen[] = { + 0x00, 0x17, kType, 0x00, + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x05, + 0x01, 0xbb, 0x00, 0x01, // Port = 443 + 'p', 'i', 'd', '1', // Protocol-ID + 0x04, 'h', 'o', 's', + 't', 'o', 'r', 'i', + 'g', 'i', 'n', + }; + + visitor.SimulateInFramer(kFrameDataSmallPIDLen, + sizeof(kFrameDataSmallPIDLen)); + EXPECT_EQ(1, visitor.error_count_); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, + visitor.framer_.error_code()); + } +} + +// Tests handling of ALTSVC frames delivered in small chunks. +TEST_P(SpdyFramerTest, ReadChunkedAltSvcFrame) { + if (spdy_version_ <= SPDY3) { + return; + } + SpdyFramer framer(spdy_version_); + SpdyAltSvcIR altsvc_ir(1); + altsvc_ir.set_max_age(20); + altsvc_ir.set_port(443); + altsvc_ir.set_protocol_id("protocolid"); + altsvc_ir.set_host("hostname"); + + scoped_ptr<SpdyFrame> control_frame(framer.SerializeAltSvc(altsvc_ir)); + TestSpdyVisitor visitor(spdy_version_); + visitor.use_compression_ = false; + + // Read data in small chunks. + size_t framed_data = 0; + size_t unframed_data = control_frame->size(); + size_t kReadChunkSize = 5; // Read five bytes at a time. + 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(1, visitor.altsvc_count_); + EXPECT_EQ(20u, visitor.test_altsvc_ir_.max_age()); + EXPECT_EQ(443u, visitor.test_altsvc_ir_.port()); + EXPECT_EQ("protocolid", visitor.test_altsvc_ir_.protocol_id()); + EXPECT_EQ("hostname", visitor.test_altsvc_ir_.host()); +} + +// Tests handling of PRIORITY frames. +TEST_P(SpdyFramerTest, ReadPriority) { + if (spdy_version_ <= SPDY3) { + return; + } + + const unsigned char kFrameData[] = { + 0x00, 0x05, 0x02, 0x00, // PRIORITY frame + 0x00, 0x00, 0x00, 0x03, // stream ID 3 + 0x00, 0x00, 0x00, 0x01, // dependent on stream id 1 + 0x00 // weight 0 + }; + + TestSpdyVisitor visitor(spdy_version_); + visitor.SimulateInFramer(kFrameData, sizeof(kFrameData)); + + EXPECT_EQ(SpdyFramer::SPDY_RESET, visitor.framer_.state()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, visitor.framer_.error_code()) + << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); +} + +// Tests handling of PRIORITY frame with incorrect size. +TEST_P(SpdyFramerTest, ReadIncorrectlySizedPriority) { + if (spdy_version_ <= SPDY3) { + return; + } + + // PRIORITY frame of size 4, which isn't correct. + const unsigned char kFrameData[] = { + 0x00, 0x04, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, + }; + + TestSpdyVisitor visitor(spdy_version_); + visitor.SimulateInFramer(kFrameData, sizeof(kFrameData)); + + EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, + visitor.framer_.error_code()) + << SpdyFramer::ErrorCodeToString(visitor.framer_.error_code()); +} + +TEST_P(SpdyFramerTest, PriorityWeightMapping) { + if (spdy_version_ <= SPDY3) { + return; + } + SpdyFramer framer(spdy_version_); + + EXPECT_EQ(255u, framer.MapPriorityToWeight(0)); + EXPECT_EQ(219u, framer.MapPriorityToWeight(1)); + EXPECT_EQ(182u, framer.MapPriorityToWeight(2)); + EXPECT_EQ(146u, framer.MapPriorityToWeight(3)); + EXPECT_EQ(109u, framer.MapPriorityToWeight(4)); + EXPECT_EQ(73u, framer.MapPriorityToWeight(5)); + EXPECT_EQ(36u, framer.MapPriorityToWeight(6)); + EXPECT_EQ(0u, framer.MapPriorityToWeight(7)); + + EXPECT_EQ(0u, framer.MapWeightToPriority(255)); + EXPECT_EQ(0u, framer.MapWeightToPriority(220)); + EXPECT_EQ(1u, framer.MapWeightToPriority(219)); + EXPECT_EQ(1u, framer.MapWeightToPriority(183)); + EXPECT_EQ(2u, framer.MapWeightToPriority(182)); + EXPECT_EQ(2u, framer.MapWeightToPriority(147)); + EXPECT_EQ(3u, framer.MapWeightToPriority(146)); + EXPECT_EQ(3u, framer.MapWeightToPriority(110)); + EXPECT_EQ(4u, framer.MapWeightToPriority(109)); + EXPECT_EQ(4u, framer.MapWeightToPriority(74)); + EXPECT_EQ(5u, framer.MapWeightToPriority(73)); + EXPECT_EQ(5u, framer.MapWeightToPriority(37)); + EXPECT_EQ(6u, framer.MapWeightToPriority(36)); + EXPECT_EQ(6u, framer.MapWeightToPriority(1)); + EXPECT_EQ(7u, framer.MapWeightToPriority(0)); +} + } // namespace net |