summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-16 21:15:44 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-16 21:15:44 +0000
commita5f3bbdb16139b635d4328957efa0da1745e8d7c (patch)
treeebb422fd85880224f937ef4f57299b739d889cce
parent9a01143136ebef2586d03f923c9d63d2c54f5d45 (diff)
downloadchromium_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.cc2
-rw-r--r--net/spdy/buffered_spdy_framer.cc5
-rw-r--r--net/spdy/buffered_spdy_framer.h6
-rw-r--r--net/spdy/buffered_spdy_framer_unittest.cc5
-rw-r--r--net/spdy/spdy_framer.cc99
-rw-r--r--net/spdy/spdy_framer.h32
-rw-r--r--net/spdy/spdy_framer_test.cc168
-rw-r--r--net/spdy/spdy_protocol.cc7
-rw-r--r--net/spdy/spdy_protocol.h20
-rw-r--r--net/spdy/spdy_protocol_test.cc2
-rw-r--r--net/spdy/spdy_session.cc5
-rw-r--r--net/spdy/spdy_session.h2
-rw-r--r--net/spdy/spdy_test_util_common.cc2
-rw-r--r--net/tools/flip_server/spdy_interface.h4
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;