diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-16 21:15:44 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-16 21:15:44 +0000 |
commit | a5f3bbdb16139b635d4328957efa0da1745e8d7c (patch) | |
tree | ebb422fd85880224f937ef4f57299b739d889cce | |
parent | 9a01143136ebef2586d03f923c9d63d2c54f5d45 (diff) | |
download | chromium_src-a5f3bbdb16139b635d4328957efa0da1745e8d7c.zip chromium_src-a5f3bbdb16139b635d4328957efa0da1745e8d7c.tar.gz chromium_src-a5f3bbdb16139b635d4328957efa0da1745e8d7c.tar.bz2 |
Declaration of SPDY 4 PUSH_PROMISE; not yet used.
This lands server change 48855179.
R=rtenneti@chromium.org
Review URL: https://codereview.chromium.org/19269003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@211843 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/quic/quic_spdy_decompressor.cc | 2 | ||||
-rw-r--r-- | net/spdy/buffered_spdy_framer.cc | 5 | ||||
-rw-r--r-- | net/spdy/buffered_spdy_framer.h | 6 | ||||
-rw-r--r-- | net/spdy/buffered_spdy_framer_unittest.cc | 5 | ||||
-rw-r--r-- | net/spdy/spdy_framer.cc | 99 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 32 | ||||
-rw-r--r-- | net/spdy/spdy_framer_test.cc | 168 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.cc | 7 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.h | 20 | ||||
-rw-r--r-- | net/spdy/spdy_protocol_test.cc | 2 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 5 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 2 | ||||
-rw-r--r-- | net/spdy/spdy_test_util_common.cc | 2 | ||||
-rw-r--r-- | net/tools/flip_server/spdy_interface.h | 4 |
14 files changed, 331 insertions, 28 deletions
diff --git a/net/quic/quic_spdy_decompressor.cc b/net/quic/quic_spdy_decompressor.cc index a083ca3..23d4633 100644 --- a/net/quic/quic_spdy_decompressor.cc +++ b/net/quic/quic_spdy_decompressor.cc @@ -55,6 +55,8 @@ class SpdyFramerVisitor : public SpdyFramerVisitorInterface { SpdyGoAwayStatus status) OVERRIDE {} virtual void OnWindowUpdate(SpdyStreamId stream_id, uint32 delta_window_size) OVERRIDE {} + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) OVERRIDE {} void set_visitor(QuicSpdyDecompressor::Visitor* visitor) { DCHECK(visitor); visitor_ = visitor; diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc index 8111925..14afaa3 100644 --- a/net/spdy/buffered_spdy_framer.cc +++ b/net/spdy/buffered_spdy_framer.cc @@ -192,6 +192,11 @@ void BufferedSpdyFramer::OnWindowUpdate(SpdyStreamId stream_id, visitor_->OnWindowUpdate(stream_id, delta_window_size); } +void BufferedSpdyFramer::OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) { + visitor_->OnPushPromise(stream_id, promised_stream_id); +} + int BufferedSpdyFramer::protocol_version() { return spdy_framer_.protocol_version(); } diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h index 91de594..30466b97 100644 --- a/net/spdy/buffered_spdy_framer.h +++ b/net/spdy/buffered_spdy_framer.h @@ -82,6 +82,10 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramerVisitorInterface { virtual void OnWindowUpdate(SpdyStreamId stream_id, uint32 delta_window_size) = 0; + // Called when a PUSH_PROMISE frame has been parsed. + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) = 0; + protected: virtual ~BufferedSpdyFramerVisitorInterface() {} @@ -136,6 +140,8 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer SpdyGoAwayStatus status) OVERRIDE; virtual void OnWindowUpdate(SpdyStreamId stream_id, uint32 delta_window_size) OVERRIDE; + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) OVERRIDE; virtual void OnDataFrameHeader(SpdyStreamId stream_id, size_t length, bool fin) OVERRIDE; diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc index 4a7bd97..0932c64 100644 --- a/net/spdy/buffered_spdy_framer_unittest.cc +++ b/net/spdy/buffered_spdy_framer_unittest.cc @@ -105,8 +105,9 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface { void OnGoAway(const SpdyFrame& frame) {} void OnPing(const SpdyFrame& frame) {} virtual void OnWindowUpdate(SpdyStreamId stream_id, - uint32 delta_window_size) OVERRIDE { - } + uint32 delta_window_size) OVERRIDE {} + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) OVERRIDE {} void OnCredential(const SpdyFrame& frame) {} // Convenience function which runs a framer simulation with particular input. diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index aeb42893..e081595 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -242,7 +242,7 @@ size_t SpdyFramer::GetGoAwaySize() const { } size_t SpdyFramer::GetHeadersMinimumSize() const { - // Size, in bytes, of a SYN_REPLY frame not including the variable-length + // Size, in bytes, of a HEADERS frame not including the variable-length // name-value block. size_t size = GetControlFrameHeaderSize(); if (spdy_version_ < 4) { @@ -286,6 +286,13 @@ size_t SpdyFramer::GetBlockedSize() const { return GetControlFrameHeaderSize(); } +size_t SpdyFramer::GetPushPromiseMinimumSize() const { + DCHECK_LE(4, protocol_version()); + // Size, in bytes, of a PUSH_PROMISE frame, sans the embedded header block. + // Calculated as frame prefix + 4 (promised stream id). + return GetControlFrameHeaderSize() + 4; +} + size_t SpdyFramer::GetFrameMinimumSize() const { return std::min(GetDataFrameMinimumSize(), GetControlFrameHeaderSize()); } @@ -413,6 +420,8 @@ const char* SpdyFramer::FrameTypeToString(SpdyFrameType type) { return "CREDENTIAL"; case BLOCKED: return "BLOCKED"; + case PUSH_PROMISE: + return "PUSH_PROMISE"; } return "UNKNOWN_CONTROL_TYPE"; } @@ -444,8 +453,9 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { } case SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK: { - // Control frames that contain header blocks (SYN_STREAM, SYN_REPLY, - // HEADERS) take a different path through the state machine - they + // Control frames that contain header blocks + // (SYN_STREAM, SYN_REPLY, HEADERS, PUSH_PROMISE) + // take a different path through the state machine - they // will go: // 1. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK // 2. SPDY_CONTROL_FRAME_HEADER_BLOCK @@ -751,6 +761,13 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); } break; + case PUSH_PROMISE: + if (current_frame_length_ < GetPushPromiseMinimumSize()) { + set_error(SPDY_INVALID_CONTROL_FRAME); + } else if (current_frame_flags_ != 0) { + set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); + } + break; default: LOG(WARNING) << "Valid " << display_protocol_ << " control frame with unhandled type: " @@ -797,6 +814,9 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { case SETTINGS: frame_size_without_variable_data = GetSettingsMinimumSize(); break; + case PUSH_PROMISE: + frame_size_without_variable_data = GetPushPromiseMinimumSize(); + break; default: frame_size_without_variable_data = -1; break; @@ -1117,6 +1137,23 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, } CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); break; + case PUSH_PROMISE: + { + DCHECK_LE(4, protocol_version()); + SpdyStreamId promised_stream_id = kInvalidStream; + bool successful_read = reader.ReadUInt31(&promised_stream_id); + DCHECK(successful_read); + DCHECK(reader.IsDoneReading()); + if (debug_visitor_) { + debug_visitor_->OnReceiveCompressedFrame( + current_frame_stream_id_, + current_frame_type_, + current_frame_length_); + } + visitor_->OnPushPromise(current_frame_stream_id_, promised_stream_id); + } + CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); + break; case SYN_REPLY: case HEADERS: // SYN_REPLY and HEADERS are the same, save for the visitor call. @@ -1172,6 +1209,7 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, bool processed_successfully = true; if (current_frame_type_ != SYN_STREAM && current_frame_type_ != SYN_REPLY && + current_frame_type_ != PUSH_PROMISE && current_frame_type_ != HEADERS) { LOG(DFATAL) << "Unhandled frame type in ProcessControlFrameHeaderBlock."; } @@ -1550,8 +1588,7 @@ SpdyFrame* SpdyFramer::CreateSynStream( // TODO(hkhalil): Avoid copy here. *(syn_stream.GetMutableNameValueBlock()) = *headers; - scoped_ptr<SpdyFrame> syn_frame(SerializeSynStream(syn_stream)); - return syn_frame.release(); + return SerializeSynStream(syn_stream); } SpdySerializedFrame* SpdyFramer::SerializeSynStream( @@ -1614,8 +1651,7 @@ SpdyFrame* SpdyFramer::CreateSynReply( // TODO(hkhalil): Avoid copy here. *(syn_reply.GetMutableNameValueBlock()) = *headers; - scoped_ptr<SpdyFrame> reply_frame(SerializeSynReply(syn_reply)); - return reply_frame.release(); + return SerializeSynReply(syn_reply); } SpdySerializedFrame* SpdyFramer::SerializeSynReply( @@ -1794,8 +1830,7 @@ SpdyFrame* SpdyFramer::CreateHeaders( // TODO(hkhalil): Avoid copy here. *(headers.GetMutableNameValueBlock()) = *header_block; - scoped_ptr<SpdyFrame> headers_frame(SerializeHeaders(headers)); - return headers_frame.release(); + return SerializeHeaders(headers); } SpdySerializedFrame* SpdyFramer::SerializeHeaders( @@ -1905,9 +1940,45 @@ SpdySerializedFrame* SpdyFramer::SerializeCredential( return builder.take(); } -SpdyFrame* SpdyFramer::CreateDataFrame( - SpdyStreamId stream_id, const char* data, - uint32 len, SpdyDataFlags flags) const { +SpdyFrame* SpdyFramer::CreatePushPromise( + SpdyStreamId stream_id, + SpdyStreamId promised_stream_id, + const SpdyHeaderBlock* header_block) { + SpdyPushPromiseIR push_promise(stream_id, promised_stream_id); + // TODO(hkhalil): Avoid copy here. + *(push_promise.GetMutableNameValueBlock()) = *header_block; + + return SerializePushPromise(push_promise); +} + +SpdyFrame* SpdyFramer::SerializePushPromise( + const SpdyPushPromiseIR& push_promise) { + DCHECK_LE(4, protocol_version()); + // The size of this frame, including variable-length name-value block. + size_t size = GetPushPromiseMinimumSize() + + GetSerializedLength(push_promise.name_value_block()); + + SpdyFrameBuilder builder(size); + builder.WriteFramePrefix(*this, PUSH_PROMISE, kNoFlags, + push_promise.stream_id()); + builder.WriteUInt32(push_promise.promised_stream_id()); + DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length()); + + SerializeNameValueBlock(&builder, push_promise); + + if (debug_visitor_) { + const size_t payload_len = GetSerializedLength( + protocol_version(), &(push_promise.name_value_block())); + debug_visitor_->OnSendCompressedFrame(push_promise.stream_id(), + PUSH_PROMISE, payload_len, builder.length()); + } + + return builder.take(); +} + +SpdyFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id, + const char* data, + uint32 len, SpdyDataFlags flags) const { DCHECK_EQ(0, flags & (!DATA_FLAG_FIN)); SpdyDataIR data_ir(stream_id, base::StringPiece(data, len)); @@ -1990,6 +2061,10 @@ class FrameSerializationVisitor : public SpdyFrameVisitor { virtual void VisitBlocked(const SpdyBlockedIR& blocked) OVERRIDE { frame_.reset(framer_->SerializeBlocked(blocked)); } + virtual void VisitPushPromise( + const SpdyPushPromiseIR& push_promise) OVERRIDE { + frame_.reset(framer_->SerializePushPromise(push_promise)); + } virtual void VisitData(const SpdyDataIR& data) OVERRIDE { frame_.reset(framer_->SerializeData(data)); } diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index 783dd7c..78fb2bc 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -44,8 +44,8 @@ class TestSpdyVisitor; } // namespace test -// A datastructure for holding a set of headers from either a -// SYN_STREAM or SYN_REPLY frame. +// A datastructure for holding a set of headers from a HEADERS, PUSH_PROMISE, +// SYN_STREAM, or SYN_REPLY frame. typedef std::map<std::string, std::string> SpdyHeaderBlock; // A datastructure for holding the ID and flag fields for SETTINGS. @@ -111,10 +111,11 @@ struct NET_EXPORT_PRIVATE SpdySettingsScratch { // Implement this interface to receive event callbacks as frames are // decoded from the framer. // -// Control frames that contain SPDY header blocks (SYN_STREAM, SYN_REPLY, and -// HEADER) are processed in fashion that allows the decompressed header block -// to be delivered in chunks to the visitor. The following steps are followed: -// 1. OnSynStream, OnSynReply or OnHeaders is called. +// Control frames that contain SPDY header blocks (SYN_STREAM, SYN_REPLY, +// HEADER, and PUSH_PROMISE) are processed in fashion that allows the +// decompressed header block to be delivered in chunks to the visitor. +// The following steps are followed: +// 1. OnSynStream, OnSynReply, OnHeaders, or OnPushPromise is called. // 2. Repeated: OnControlFrameHeaderData is called with chunks of the // decompressed header block. In each call the len parameter is greater // than zero. @@ -156,7 +157,7 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface { virtual void OnHeaders(SpdyStreamId stream_id, bool fin) = 0; // Called when a chunk of header data is available. This is called - // after OnSynStream, OnSynReply or OnHeaders(). + // after OnSynStream, OnPushPromise, OnSynReply, or OnHeaders(). // |stream_id| The stream receiving the header data. // |header_data| A buffer containing the header data chunk received. // |len| The length of the header data buffer. A length of zero indicates @@ -221,6 +222,12 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface { // Called when a BLOCKED frame has been parsed. virtual void OnBlocked(SpdyStreamId stream_id) {} + + // Called when a PUSH_PROMISE frame is received. + // Note that header block data is not included. See + // OnControlFrameHeaderData(). + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) = 0; }; // Optionally, and in addition to SpdyFramerVisitorInterface, a class supporting @@ -428,6 +435,16 @@ class NET_EXPORT_PRIVATE SpdyFramer { // advisory and optional. SpdySerializedFrame* SerializeBlocked(const SpdyBlockedIR& blocked) const; + // Creates and serializes a PUSH_PROMISE frame. The PUSH_PROMISE frame is used + // to inform the client that it will be receiving an additional stream + // in response to the original request. The frame includes synthesized + // headers to explain the upcoming data. + SpdyFrame* CreatePushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id, + const SpdyHeaderBlock* headers); + SpdySerializedFrame* SerializePushPromise( + const SpdyPushPromiseIR& push_promise); + // Given a CREDENTIAL frame's payload, extract the credential. // Returns true on successful parse, false otherwise. // TODO(hkhalil): Implement CREDENTIAL frame parsing in SpdyFramer @@ -491,6 +508,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { size_t GetWindowUpdateSize() const; size_t GetCredentialMinimumSize() const; size_t GetBlockedSize() const; + size_t GetPushPromiseMinimumSize() const; // Returns the minimum size a frame can be (data or control). size_t GetFrameMinimumSize() const; diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index 0260210..48f76366 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc @@ -63,6 +63,8 @@ class MockVisitor : public SpdyFramerVisitorInterface { MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id, uint32 delta_window_size)); MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id)); + MOCK_METHOD2(OnPushPromise, void(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id)); }; class MockDebugVisitor : public SpdyFramerDebugVisitorInterface { @@ -182,7 +184,20 @@ class SpdyFramerTestUtil { &null_headers)); ResetBuffer(); memcpy(buffer_.get(), frame->data(), framer.GetHeadersMinimumSize()); - size_ += framer.GetSynStreamMinimumSize(); + size_ += framer.GetHeadersMinimumSize(); + } + + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) 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)); + ResetBuffer(); + memcpy(buffer_.get(), frame->data(), framer.GetPushPromiseMinimumSize()); + size_ += framer.GetPushPromiseMinimumSize(); } virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id, @@ -280,6 +295,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, setting_count_(0), last_window_update_stream_(0), last_window_update_delta_(0), + last_push_promise_stream_(0), + last_push_promise_promised_stream_(0), data_bytes_(0), fin_frame_count_(0), fin_flag_count_(0), @@ -346,7 +363,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE { syn_reply_frame_count_++; - InitHeaderStreaming(HEADERS, stream_id); + InitHeaderStreaming(SYN_REPLY, stream_id); if (fin) { fin_flag_count_++; } @@ -354,7 +371,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE { headers_frame_count_++; - InitHeaderStreaming(SYN_REPLY, stream_id); + InitHeaderStreaming(HEADERS, stream_id); if (fin) { fin_flag_count_++; } @@ -386,6 +403,13 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, last_window_update_delta_ = delta_window_size; } + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) OVERRIDE { + InitHeaderStreaming(PUSH_PROMISE, stream_id); + last_push_promise_stream_ = stream_id; + last_push_promise_promised_stream_ = promised_stream_id; + } + virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id, const char* header_data, size_t len) OVERRIDE { @@ -499,6 +523,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, int setting_count_; SpdyStreamId last_window_update_stream_; uint32 last_window_update_delta_; + SpdyStreamId last_push_promise_stream_; + SpdyStreamId last_push_promise_promised_stream_; int data_bytes_; int fin_frame_count_; // The count of RST_STREAM type frames received. int fin_flag_count_; // The count of frames with the FIN flag set. @@ -622,7 +648,7 @@ class SpdyFramerTest : public ::testing::TestWithParam<SpdyMajorVersion> { unsigned char spdy_version_ch_; }; -// All tests are run with two different SPDY versions: SPDY/2 and SPDY/3. +// All tests are run with 3 different SPDY versions: SPDY/2, SPDY/3, SPDY/4. INSTANTIATE_TEST_CASE_P(SpdyFramerTests, SpdyFramerTest, ::testing::Values(SPDY2, SPDY3, SPDY4)); @@ -2923,6 +2949,74 @@ TEST_P(SpdyFramerTest, SerializeBlocked) { } +TEST_P(SpdyFramerTest, CreatePushPromiseUncompressed) { + if (spdy_version_ < SPDY4) { + 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 + }; + + scoped_ptr<SpdySerializedFrame> frame(framer.CreatePushPromise( + 42, 57, &headers)); + CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); +} + +TEST_P(SpdyFramerTest, CreatePushPromiseCompressed) { + if (spdy_version_ < SPDY4) { + 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 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 + }; + + scoped_ptr<SpdySerializedFrame> frame(framer.CreatePushPromise( + 42, 57, &headers)); + CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); +} + TEST_P(SpdyFramerTest, ReadCompressedSynStreamHeaderBlock) { SpdyHeaderBlock headers; headers["aa"] = "vv"; @@ -3529,6 +3623,27 @@ TEST_P(SpdyFramerTest, ReadCredentialFrameWithCorruptCertificate) { EXPECT_EQ(1, visitor.error_count_); } +TEST_P(SpdyFramerTest, ReadCompressedPushPromise) { + if (spdy_version_ < 4) { + return; + } + + 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_)); +} + TEST_P(SpdyFramerTest, ReadGarbage) { SpdyFramer framer(spdy_version_); unsigned char garbage_frame[256]; @@ -3568,6 +3683,7 @@ TEST_P(SpdyFramerTest, SizesTest) { EXPECT_EQ(12u, framer.GetWindowUpdateSize()); EXPECT_EQ(10u, framer.GetCredentialMinimumSize()); EXPECT_EQ(8u, framer.GetBlockedSize()); + EXPECT_EQ(12u, framer.GetPushPromiseMinimumSize()); EXPECT_EQ(8u, framer.GetFrameMinimumSize()); EXPECT_EQ(65535u, framer.GetFrameMaximumSize()); EXPECT_EQ(65527u, framer.GetDataFrameMaximumPayload()); @@ -3697,6 +3813,8 @@ TEST_P(SpdyFramerTest, FrameTypeToStringTest) { SpdyFramer::FrameTypeToString(HEADERS)); EXPECT_STREQ("WINDOW_UPDATE", SpdyFramer::FrameTypeToString(WINDOW_UPDATE)); + EXPECT_STREQ("PUSH_PROMISE", + SpdyFramer::FrameTypeToString(PUSH_PROMISE)); EXPECT_STREQ("CREDENTIAL", SpdyFramer::FrameTypeToString(CREDENTIAL)); } @@ -4032,6 +4150,48 @@ TEST_P(SpdyFramerTest, WindowUpdateFrameFlags) { } } +TEST_P(SpdyFramerTest, PushPromiseFrameFlags) { + if (spdy_version_ < SPDY4) { + return; + } + + for (int flags = 0; flags < 256; ++flags) { + SCOPED_TRACE(testing::Message() << "Flags " << flags); + + testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<net::test::MockDebugVisitor> debug_visitor; + SpdyFramer framer(spdy_version_); + framer.set_visitor(&visitor); + framer.set_debug_visitor(&debug_visitor); + + EXPECT_CALL(debug_visitor, OnSendCompressedFrame(42, PUSH_PROMISE, _, _)); + + SpdyHeaderBlock headers; + headers["foo"] = "bar"; + scoped_ptr<SpdyFrame> frame(framer.CreatePushPromise(42, 57, &headers)); + SetFrameFlags(frame.get(), flags, spdy_version_); + + if (flags != 0) { + EXPECT_CALL(visitor, OnError(_)); + } else { + EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, PUSH_PROMISE, _)); + EXPECT_CALL(visitor, OnPushPromise(42, 57)); + EXPECT_CALL(visitor, OnControlFrameHeaderData(42, _, _)) + .WillRepeatedly(testing::Return(true)); + } + + framer.ProcessInput(frame->data(), frame->size()); + if (flags != 0) { + EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); + EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS, + framer.error_code()); + } else { + EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()); + } + } +} + TEST_P(SpdyFramerTest, CredentialFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); diff --git a/net/spdy/spdy_protocol.cc b/net/spdy/spdy_protocol.cc index ecd8594..39031eb 100644 --- a/net/spdy/spdy_protocol.cc +++ b/net/spdy/spdy_protocol.cc @@ -67,7 +67,6 @@ SpdyCredentialIR::SpdyCredentialIR(int16 slot) { SpdyCredentialIR::~SpdyCredentialIR() {} - void SpdyCredentialIR::Visit(SpdyFrameVisitor* visitor) const { return visitor->VisitCredential(*this); } @@ -76,4 +75,8 @@ void SpdyBlockedIR::Visit(SpdyFrameVisitor* visitor) const { return visitor->VisitBlocked(*this); } -} // namespace +void SpdyPushPromiseIR::Visit(SpdyFrameVisitor* visitor) const { + return visitor->VisitPushPromise(*this); +} + +} // namespace net diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index 2bf8c68..97d0eca 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h @@ -270,7 +270,8 @@ enum SpdyFrameType { WINDOW_UPDATE, CREDENTIAL, BLOCKED, - LAST_CONTROL_TYPE = BLOCKED + PUSH_PROMISE, + LAST_CONTROL_TYPE = PUSH_PROMISE }; // Flags on data packets. @@ -698,6 +699,22 @@ class NET_EXPORT_PRIVATE SpdyBlockedIR DISALLOW_COPY_AND_ASSIGN(SpdyBlockedIR); }; +class SpdyPushPromiseIR : public SpdyFrameWithNameValueBlockIR { + public: + SpdyPushPromiseIR(SpdyStreamId stream_id, SpdyStreamId promised_stream_id) + : SpdyFrameWithNameValueBlockIR(stream_id), + promised_stream_id_(promised_stream_id) {} + SpdyStreamId promised_stream_id() const { return promised_stream_id_; } + void set_promised_stream_id(SpdyStreamId id) { promised_stream_id_ = id; } + + virtual void Visit(SpdyFrameVisitor* visitor) const OVERRIDE; + + private: + SpdyStreamId promised_stream_id_; + DISALLOW_COPY_AND_ASSIGN(SpdyPushPromiseIR); +}; + + // ------------------------------------------------------------------------- // Wrapper classes for various SPDY frames. @@ -757,6 +774,7 @@ class SpdyFrameVisitor { virtual void VisitWindowUpdate(const SpdyWindowUpdateIR& window_update) = 0; virtual void VisitCredential(const SpdyCredentialIR& credential) = 0; virtual void VisitBlocked(const SpdyBlockedIR& blocked) = 0; + virtual void VisitPushPromise(const SpdyPushPromiseIR& push_promise) = 0; virtual void VisitData(const SpdyDataIR& data) = 0; protected: diff --git a/net/spdy/spdy_protocol_test.cc b/net/spdy/spdy_protocol_test.cc index c01c022..006dfe1 100644 --- a/net/spdy/spdy_protocol_test.cc +++ b/net/spdy/spdy_protocol_test.cc @@ -54,6 +54,8 @@ TEST_P(SpdyProtocolTest, ProtocolConstants) { EXPECT_EQ(9, WINDOW_UPDATE); EXPECT_EQ(10, CREDENTIAL); EXPECT_EQ(11, BLOCKED); + EXPECT_EQ(12, PUSH_PROMISE); + EXPECT_EQ(12, LAST_CONTROL_TYPE); EXPECT_EQ(std::numeric_limits<int32>::max(), kSpdyMaximumWindowSize); } diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index d63ab86..b607796 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -2087,6 +2087,11 @@ void SpdySession::OnWindowUpdate(SpdyStreamId stream_id, } } +void SpdySession::OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) { + // TODO(akalin): Handle PUSH_PROMISE frames. +} + void SpdySession::SendStreamWindowUpdate(SpdyStreamId stream_id, uint32 delta_window_size) { CHECK_GE(flow_control_state_, FLOW_CONTROL_STREAM); diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 7cabac0..c2c6cfd 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -699,6 +699,8 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, SpdySettingsIds id, uint8 flags, uint32 value) OVERRIDE; virtual void OnWindowUpdate(SpdyStreamId stream_id, uint32 delta_window_size) OVERRIDE; + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) OVERRIDE; virtual void OnSynStream(SpdyStreamId stream_id, SpdyStreamId associated_stream_id, SpdyPriority priority, diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc index 1c0322d..ea333c9 100644 --- a/net/spdy/spdy_test_util_common.cc +++ b/net/spdy/spdy_test_util_common.cc @@ -244,6 +244,8 @@ class PriorityGetter : public BufferedSpdyFramerVisitorInterface { SpdyGoAwayStatus status) OVERRIDE {} virtual void OnWindowUpdate(SpdyStreamId stream_id, uint32 delta_window_size) OVERRIDE {} + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) OVERRIDE {} private: SpdyPriority priority_; diff --git a/net/tools/flip_server/spdy_interface.h b/net/tools/flip_server/spdy_interface.h index 184907d..a83dab7 100644 --- a/net/tools/flip_server/spdy_interface.h +++ b/net/tools/flip_server/spdy_interface.h @@ -115,6 +115,10 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, virtual void OnWindowUpdate(SpdyStreamId stream_id, uint32 delta_window_size) OVERRIDE {} + // Called when a PUSH_PROMISE frame has been parsed. + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) OVERRIDE {} + public: virtual size_t ProcessReadInput(const char* data, size_t len) OVERRIDE; virtual size_t ProcessWriteInput(const char* data, size_t len) OVERRIDE; |