summaryrefslogtreecommitdiffstats
path: root/net/spdy
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-11 07:16:30 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-11 07:16:30 +0000
commitee098aae5963ed369f5be1e1433caa38001e408b (patch)
tree536c35e9d91e7280e8d240a6a1080771d707f153 /net/spdy
parent10994d13b799f7f5a6222c24a9de6ac994c6b84e (diff)
downloadchromium_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.cc185
-rw-r--r--net/spdy/spdy_framer.h10
-rw-r--r--net/spdy/spdy_framer_test.cc41
-rw-r--r--net/spdy/spdy_protocol.h12
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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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.