diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-11 07:16:30 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-11 07:16:30 +0000 |
commit | ee098aae5963ed369f5be1e1433caa38001e408b (patch) | |
tree | 536c35e9d91e7280e8d240a6a1080771d707f153 /net/spdy | |
parent | 10994d13b799f7f5a6222c24a9de6ac994c6b84e (diff) | |
download | chromium_src-ee098aae5963ed369f5be1e1433caa38001e408b.zip chromium_src-ee098aae5963ed369f5be1e1433caa38001e408b.tar.gz chromium_src-ee098aae5963ed369f5be1e1433caa38001e408b.tar.bz2 |
Add BLOCKED frame type to SPDY 4 for increased debugability/tunability.
Also some very simple logic cleanup to allow for zero-payload frame
processing.
This lands server change 47724806.
BUG=
R=rch@chromium.org
Review URL: https://codereview.chromium.org/16764002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@205450 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/spdy')
-rw-r--r-- | net/spdy/spdy_framer.cc | 185 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 10 | ||||
-rw-r--r-- | net/spdy/spdy_framer_test.cc | 41 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.h | 12 |
4 files changed, 169 insertions, 79 deletions
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 5d1d445..5cab815 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -279,6 +279,13 @@ size_t SpdyFramer::GetCredentialMinimumSize() const { return GetControlFrameHeaderSize() + 2; } +size_t SpdyFramer::GetBlockedSize() const { + DCHECK_LE(4, protocol_version()); + // Size, in bytes, of a BLOCKED frame. + // The BLOCKED frame has no payload beyond the control frame header. + return GetControlFrameHeaderSize(); +} + size_t SpdyFramer::GetFrameMinimumSize() const { return std::min(GetDataFrameMinimumSize(), GetControlFrameHeaderSize()); } @@ -404,6 +411,8 @@ const char* SpdyFramer::FrameTypeToString(SpdyFrameType type) { return "WINDOW_UPDATE"; case CREDENTIAL: return "CREDENTIAL"; + case BLOCKED: + return "BLOCKED"; } return "UNKNOWN_CONTROL_TYPE"; } @@ -735,6 +744,13 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); } break; + case BLOCKED: + if (current_frame_length_ != GetBlockedSize()) { + 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: " @@ -816,14 +832,16 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { size_t SpdyFramer::UpdateCurrentFrameBuffer(const char** data, size_t* len, size_t max_bytes) { size_t bytes_to_read = std::min(*len, max_bytes); - DCHECK_GE(kControlFrameBufferSize, - current_frame_buffer_length_ + bytes_to_read); - memcpy(current_frame_buffer_.get() + current_frame_buffer_length_, - *data, - bytes_to_read); - current_frame_buffer_length_ += bytes_to_read; - *data += bytes_to_read; - *len -= bytes_to_read; + if (bytes_to_read > 0) { + DCHECK_GE(kControlFrameBufferSize, + current_frame_buffer_length_ + bytes_to_read); + memcpy(current_frame_buffer_.get() + current_frame_buffer_length_, + *data, + bytes_to_read); + current_frame_buffer_length_ += bytes_to_read; + *data += bytes_to_read; + *len -= bytes_to_read; + } return bytes_to_read; } @@ -1284,89 +1302,93 @@ bool SpdyFramer::ProcessSetting(const char* data) { size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { size_t original_len = len; - if (remaining_data_length_) { - size_t bytes_read = - UpdateCurrentFrameBuffer(&data, &len, remaining_data_length_); - remaining_data_length_ -= bytes_read; - if (remaining_data_length_ == 0) { - SpdyFrameReader reader(current_frame_buffer_.get(), - current_frame_buffer_length_); - reader.Seek(GetControlFrameHeaderSize()); // Skip frame header. - - // Use frame-specific handlers. - switch (current_frame_type_) { - case PING: { - SpdyPingId id = 0; - bool successful_read = reader.ReadUInt32(&id); + size_t bytes_read = + UpdateCurrentFrameBuffer(&data, &len, remaining_data_length_); + remaining_data_length_ -= bytes_read; + if (remaining_data_length_ == 0) { + SpdyFrameReader reader(current_frame_buffer_.get(), + current_frame_buffer_length_); + reader.Seek(GetControlFrameHeaderSize()); // Skip frame header. + + // Use frame-specific handlers. + switch (current_frame_type_) { + case PING: { + SpdyPingId id = 0; + bool successful_read = reader.ReadUInt32(&id); + DCHECK(successful_read); + DCHECK(reader.IsDoneReading()); + visitor_->OnPing(id); + } + break; + case WINDOW_UPDATE: { + uint32 delta_window_size = 0; + bool successful_read = true; + if (spdy_version_ < 4) { + successful_read = reader.ReadUInt31(¤t_frame_stream_id_); DCHECK(successful_read); - DCHECK(reader.IsDoneReading()); - visitor_->OnPing(id); } - break; - case WINDOW_UPDATE: { - uint32 delta_window_size = 0; - bool successful_read = true; - if (spdy_version_ < 4) { - successful_read = reader.ReadUInt31(¤t_frame_stream_id_); - DCHECK(successful_read); - } - successful_read = reader.ReadUInt32(&delta_window_size); + successful_read = reader.ReadUInt32(&delta_window_size); + DCHECK(successful_read); + DCHECK(reader.IsDoneReading()); + visitor_->OnWindowUpdate(current_frame_stream_id_, + delta_window_size); + } + break; + case RST_STREAM: { + bool successful_read = true; + if (spdy_version_ < 4) { + successful_read = reader.ReadUInt31(¤t_frame_stream_id_); DCHECK(successful_read); - DCHECK(reader.IsDoneReading()); - visitor_->OnWindowUpdate(current_frame_stream_id_, - delta_window_size); } - break; - case RST_STREAM: { - bool successful_read = true; - if (spdy_version_ < 4) { - successful_read = reader.ReadUInt31(¤t_frame_stream_id_); - DCHECK(successful_read); - } - SpdyRstStreamStatus status = RST_STREAM_INVALID; - uint32 status_raw = status; + SpdyRstStreamStatus status = RST_STREAM_INVALID; + uint32 status_raw = status; + successful_read = reader.ReadUInt32(&status_raw); + DCHECK(successful_read); + if (status_raw > RST_STREAM_INVALID && + status_raw < RST_STREAM_NUM_STATUS_CODES) { + status = static_cast<SpdyRstStreamStatus>(status_raw); + } else { + // TODO(hkhalil): Probably best to OnError here, depending on + // our interpretation of the spec. Keeping with existing liberal + // behavior for now. + } + DCHECK(reader.IsDoneReading()); + visitor_->OnRstStream(current_frame_stream_id_, status); + } + break; + case GOAWAY: { + bool successful_read = reader.ReadUInt31(¤t_frame_stream_id_); + DCHECK(successful_read); + SpdyGoAwayStatus status = GOAWAY_OK; + if (spdy_version_ >= 3) { + uint32 status_raw = GOAWAY_OK; successful_read = reader.ReadUInt32(&status_raw); DCHECK(successful_read); - if (status_raw > RST_STREAM_INVALID && - status_raw < RST_STREAM_NUM_STATUS_CODES) { - status = static_cast<SpdyRstStreamStatus>(status_raw); + if (status_raw >= GOAWAY_OK && + status_raw < static_cast<uint32>(GOAWAY_NUM_STATUS_CODES)) { + status = static_cast<SpdyGoAwayStatus>(status_raw); } else { // TODO(hkhalil): Probably best to OnError here, depending on // our interpretation of the spec. Keeping with existing liberal // behavior for now. } - DCHECK(reader.IsDoneReading()); - visitor_->OnRstStream(current_frame_stream_id_, status); - } - break; - case GOAWAY: { - bool successful_read = reader.ReadUInt31(¤t_frame_stream_id_); - DCHECK(successful_read); - SpdyGoAwayStatus status = GOAWAY_OK; - if (spdy_version_ >= 3) { - uint32 status_raw = GOAWAY_OK; - successful_read = reader.ReadUInt32(&status_raw); - DCHECK(successful_read); - if (status_raw > static_cast<uint32>(GOAWAY_INVALID) && - status_raw < static_cast<uint32>(GOAWAY_NUM_STATUS_CODES)) { - status = static_cast<SpdyGoAwayStatus>(status_raw); - } else { - // TODO(hkhalil): Probably best to OnError here, depending on - // our interpretation of the spec. Keeping with existing liberal - // behavior for now. - } - } - DCHECK(reader.IsDoneReading()); - visitor_->OnGoAway(current_frame_stream_id_, status); } - break; - default: - // Unreachable. - LOG(FATAL) << "Unhandled control frame " << current_frame_type_; - } - - CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD); + DCHECK(reader.IsDoneReading()); + visitor_->OnGoAway(current_frame_stream_id_, status); + } + break; + case BLOCKED: { + DCHECK_LE(4, protocol_version()); + DCHECK(reader.IsDoneReading()); + visitor_->OnBlocked(current_frame_stream_id_); + } + break; + default: + // Unreachable. + LOG(FATAL) << "Unhandled control frame " << current_frame_type_; } + + CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD); } return original_len - len; } @@ -1683,6 +1705,13 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings( return builder.take(); } +SpdyFrame* SpdyFramer::SerializeBlocked(const SpdyBlockedIR& blocked) const { + DCHECK_LE(4, protocol_version()); + SpdyFrameBuilder builder(GetBlockedSize()); + builder.WriteFramePrefix(*this, BLOCKED, kNoFlags, blocked.stream_id()); + return builder.take(); +} + SpdyFrame* SpdyFramer::CreatePingFrame(uint32 unique_id) const { SpdyPingIR ping(unique_id); return SerializePing(ping); diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index 856f029..cdd8f43 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -226,6 +226,9 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface { virtual void OnSynStreamCompressed( size_t uncompressed_size, size_t compressed_size) = 0; + + // Called when a BLOCKED frame has been parsed. + virtual void OnBlocked(SpdyStreamId stream_id) {} }; // Optionally, and in addition to SpdyFramerVisitorInterface, a class supporting @@ -426,6 +429,12 @@ class NET_EXPORT_PRIVATE SpdyFramer { SpdySerializedFrame* SerializeCredential( const SpdyCredentialIR& credential) const; + // Serializes a BLOCKED frame. The BLOCKED frame is used to indicate to the + // remote endpoint that this endpoint believes itself to be flow-control + // blocked but otherwise ready to send data. The BLOCKED frame is purely + // advisory and optional. + SpdySerializedFrame* SerializeBlocked(const SpdyBlockedIR& blocked) const; + // Given a CREDENTIAL frame's payload, extract the credential. // Returns true on successful parse, false otherwise. // TODO(hkhalil): Implement CREDENTIAL frame parsing in SpdyFramer @@ -485,6 +494,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { size_t GetHeadersMinimumSize() const; size_t GetWindowUpdateSize() const; size_t GetCredentialMinimumSize() const; + size_t GetBlockedSize() 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 6773671..6a6ca84 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc @@ -65,6 +65,7 @@ class MockVisitor : public SpdyFramerVisitorInterface { MOCK_METHOD2(OnSynStreamCompressed, void(size_t uncompressed_length, size_t compressed_length)); + MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id)); }; class SpdyFramerTestUtil { @@ -2888,6 +2889,24 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) { } } +TEST_P(SpdyFramerTest, SerializeBlocked) { + if (spdy_version_ < SPDY4) { + return; + } + + SpdyFramer framer(spdy_version_); + + const char kDescription[] = "BLOCKED frame"; + const unsigned char kFrameData[] = { + 0x00, 0x08, 0x0b, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + SpdyBlockedIR blocked_ir(0); + scoped_ptr<SpdySerializedFrame> frame(framer.SerializeBlocked(blocked_ir)); + CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + +} + TEST_P(SpdyFramerTest, ReadCompressedSynStreamHeaderBlock) { SpdyHeaderBlock headers; headers["aa"] = "vv"; @@ -3532,6 +3551,7 @@ TEST_P(SpdyFramerTest, SizesTest) { EXPECT_EQ(8u, framer.GetHeadersMinimumSize()); EXPECT_EQ(12u, framer.GetWindowUpdateSize()); EXPECT_EQ(10u, framer.GetCredentialMinimumSize()); + EXPECT_EQ(8u, framer.GetBlockedSize()); EXPECT_EQ(8u, framer.GetFrameMinimumSize()); EXPECT_EQ(65535u, framer.GetFrameMaximumSize()); EXPECT_EQ(65527u, framer.GetDataFrameMaximumPayload()); @@ -4164,4 +4184,25 @@ TEST_P(SpdyFramerTest, GoAwayStreamIdBounds) { EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()); } +TEST_P(SpdyFramerTest, OnBlocked) { + if (spdy_version_ < SPDY4) { + return; + } + + const SpdyStreamId kStreamId = 0; + + testing::StrictMock<test::MockVisitor> visitor; + SpdyFramer framer(spdy_version_); + framer.set_visitor(&visitor); + + EXPECT_CALL(visitor, OnBlocked(kStreamId)); + + SpdyBlockedIR blocked_ir(0); + scoped_ptr<SpdySerializedFrame> frame(framer.SerializeBlocked(blocked_ir)); + framer.ProcessInput(frame->data(), framer.GetBlockedSize()); + + EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()); +} + } // namespace net diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index dd6f4d7..db04415 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h @@ -269,7 +269,8 @@ enum SpdyFrameType { HEADERS, WINDOW_UPDATE, CREDENTIAL, - LAST_CONTROL_TYPE = CREDENTIAL + BLOCKED, + LAST_CONTROL_TYPE = BLOCKED }; // Flags on data packets. @@ -663,6 +664,15 @@ class SpdyCredentialIR : public SpdyFrameIR { DISALLOW_COPY_AND_ASSIGN(SpdyCredentialIR); }; +class SpdyBlockedIR : public SpdyFrameWithStreamIdIR { + public: + explicit SpdyBlockedIR(SpdyStreamId stream_id) + : SpdyFrameWithStreamIdIR(stream_id) {} + + private: + DISALLOW_COPY_AND_ASSIGN(SpdyBlockedIR); +}; + // ------------------------------------------------------------------------- // Wrapper classes for various SPDY frames. |