summaryrefslogtreecommitdiffstats
path: root/net/spdy/spdy_framer.cc
diff options
context:
space:
mode:
authorjgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-21 00:57:37 +0000
committerjgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-21 00:57:37 +0000
commitcd9c5f67f7f3de52c9e170cc163fa2af45b188f8 (patch)
tree8b589367c9125795f6156d111fe25ee6d1139c3e /net/spdy/spdy_framer.cc
parent900349daf01d24b268ac33873b41e4af80733faf (diff)
downloadchromium_src-cd9c5f67f7f3de52c9e170cc163fa2af45b188f8.zip
chromium_src-cd9c5f67f7f3de52c9e170cc163fa2af45b188f8.tar.gz
chromium_src-cd9c5f67f7f3de52c9e170cc163fa2af45b188f8.tar.bz2
Added a reason field and a description to GOAWAY.
This lands server change 57373540 by ygi. BufferedSpdyFramer and above currently ignore GOAWAY frame payloads. Review URL: https://codereview.chromium.org/137163004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245962 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/spdy/spdy_framer.cc')
-rw-r--r--net/spdy/spdy_framer.cc158
1 files changed, 125 insertions, 33 deletions
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
index 68f7fe7..0652418 100644
--- a/net/spdy/spdy_framer.cc
+++ b/net/spdy/spdy_framer.cc
@@ -114,6 +114,11 @@ void SettingsFlagsAndId::ConvertFlagsAndIdForSpdy2(uint32* val) {
SpdyCredential::SpdyCredential() : slot(0) {}
SpdyCredential::~SpdyCredential() {}
+bool SpdyFramerVisitorInterface::OnGoAwayFrameData(const char* goaway_data,
+ size_t len) {
+ return true;
+}
+
SpdyFramer::SpdyFramer(SpdyMajorVersion version)
: current_frame_buffer_(new char[kControlFrameBufferSize]),
enable_compression_(true),
@@ -228,12 +233,15 @@ size_t SpdyFramer::GetPingSize() const {
return GetControlFrameHeaderSize() + 4;
}
-size_t SpdyFramer::GetGoAwaySize() const {
+size_t SpdyFramer::GetGoAwayMinimumSize() const {
// Size, in bytes, of this GOAWAY frame. Calculated as:
- // control frame header + 4 (last good stream id)
- size_t size = GetControlFrameHeaderSize() + 4;
+ // 1. Control frame header size
+ size_t size = GetControlFrameHeaderSize();
+
+ // 2. Last good stream id (4 bytes)
+ size += 4;
- // SPDY 3+ GOAWAY frames also contain a status.
+ // 3. SPDY 3+ GOAWAY frames also contain a status (4 bytes)
if (protocol_version() >= 3) {
size += 4;
}
@@ -327,6 +335,8 @@ const char* SpdyFramer::StateToString(int state) {
return "SPDY_CONTROL_FRAME_HEADER_BLOCK";
case SPDY_CREDENTIAL_FRAME_PAYLOAD:
return "SPDY_CREDENTIAL_FRAME_PAYLOAD";
+ case SPDY_GOAWAY_FRAME_PAYLOAD:
+ return "SPDY_GOAWAY_FRAME_PAYLOAD";
case SPDY_SETTINGS_FRAME_PAYLOAD:
return "SPDY_SETTINGS_FRAME_PAYLOAD";
}
@@ -493,6 +503,13 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
break;
}
+ case SPDY_GOAWAY_FRAME_PAYLOAD: {
+ size_t bytes_read = ProcessGoAwayFramePayload(data, len);
+ len -= bytes_read;
+ data += bytes_read;
+ break;
+ }
+
case SPDY_CONTROL_FRAME_PAYLOAD: {
size_t bytes_read = ProcessControlFramePayload(data, len);
len -= bytes_read;
@@ -727,7 +744,14 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
break;
case GOAWAY:
{
- if (current_frame_length_ != GetGoAwaySize()) {
+ // For SPDY version < 4, there are only mandatory fields and the header
+ // has a fixed length. For SPDY version >= 4, optional opaque data may
+ // be appended to the GOAWAY frame, thus there is only a minimal length
+ // restriction.
+ if ((current_frame_length_ != GetGoAwayMinimumSize() &&
+ protocol_version() < 4) ||
+ (current_frame_length_ < GetGoAwayMinimumSize() &&
+ protocol_version() >= 4)) {
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ != 0) {
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
@@ -811,6 +835,11 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
return;
}
+ if (current_frame_type_ == GOAWAY) {
+ CHANGE_STATE(SPDY_GOAWAY_FRAME_PAYLOAD);
+ return;
+ }
+
// Determine the frame size without variable-length data.
int32 frame_size_without_variable_data;
switch (current_frame_type_) {
@@ -1413,8 +1442,8 @@ bool SpdyFramer::ProcessSetting(const char* data) {
size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
size_t original_len = len;
- size_t bytes_read =
- UpdateCurrentFrameBuffer(&data, &len, 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(),
@@ -1453,27 +1482,6 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
visitor_->OnPing(id);
}
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 >= 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_->OnGoAway(current_frame_stream_id_, status);
- }
- break;
case WINDOW_UPDATE: {
uint32 delta_window_size = 0;
bool successful_read = true;
@@ -1522,6 +1530,71 @@ size_t SpdyFramer::ProcessCredentialFramePayload(const char* data, size_t len) {
return len;
}
+size_t SpdyFramer::ProcessGoAwayFramePayload(const char* data, size_t len) {
+ if (len == 0) {
+ return 0;
+ }
+ // Clamp to the actual remaining payload.
+ if (len > remaining_data_length_) {
+ len = remaining_data_length_;
+ }
+ size_t original_len = len;
+
+ // Check if we had already read enough bytes to parse the GOAWAY header.
+ const size_t header_size = GetGoAwayMinimumSize();
+ size_t unread_header_bytes = header_size - current_frame_buffer_length_;
+ bool already_parsed_header = (unread_header_bytes == 0);
+ if (!already_parsed_header) {
+ // Buffer the new GOAWAY header bytes we got.
+ UpdateCurrentFrameBuffer(&data, &len, unread_header_bytes);
+
+ // Do we have enough to parse the constant size GOAWAY header?
+ if (current_frame_buffer_length_ == header_size) {
+ // Parse out the last good stream id.
+ SpdyFrameReader reader(current_frame_buffer_.get(),
+ current_frame_buffer_length_);
+ reader.Seek(GetControlFrameHeaderSize()); // Seek past frame header.
+ bool successful_read = reader.ReadUInt31(&current_frame_stream_id_);
+ DCHECK(successful_read);
+
+ // In SPDYv3 and up, frames also specify a status code - parse it out.
+ SpdyGoAwayStatus status = GOAWAY_OK;
+ if (spdy_version_ >= 3) {
+ uint32 status_raw = GOAWAY_OK;
+ successful_read = reader.ReadUInt32(&status_raw);
+ DCHECK(successful_read);
+ // We've read an unsigned integer, so it's enough to only check
+ // upper bound to ensure the value is in valid range.
+ if (status_raw < 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(false);
+ }
+ }
+ // Finished parsing the GOAWAY header, call frame handler.
+ visitor_->OnGoAway(current_frame_stream_id_, status);
+ }
+ }
+
+ // Handle remaining data as opaque.
+ bool processed_succesfully = true;
+ if (len > 0) {
+ processed_succesfully = visitor_->OnGoAwayFrameData(data, len);
+ }
+ remaining_data_length_ -= original_len;
+ if (!processed_succesfully) {
+ set_error(SPDY_CREDENTIAL_FRAME_CORRUPT);
+ } else if (remaining_data_length_ == 0) {
+ // Signal that there is not more opaque data.
+ visitor_->OnGoAwayFrameData(NULL, 0);
+ CHANGE_STATE(SPDY_AUTO_RESET);
+ }
+ return original_len;
+}
+
size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) {
size_t original_len = len;
@@ -1912,25 +1985,44 @@ SpdySerializedFrame* SpdyFramer::SerializePing(const SpdyPingIR& ping) const {
}
SpdyFrame* SpdyFramer::CreateGoAway(
- SpdyStreamId last_accepted_stream_id,
- SpdyGoAwayStatus status) const {
- SpdyGoAwayIR goaway(last_accepted_stream_id, status);
+ SpdyStreamId last_accepted_stream_id, SpdyGoAwayStatus status,
+ const base::StringPiece& description) const {
+ SpdyGoAwayIR goaway(last_accepted_stream_id, status, description);
return SerializeGoAway(goaway);
}
SpdySerializedFrame* SpdyFramer::SerializeGoAway(
const SpdyGoAwayIR& goaway) const {
- SpdyFrameBuilder builder(GetGoAwaySize());
+
+ // Compute the output buffer size, take opaque data into account.
+ uint16 expected_length = GetGoAwayMinimumSize();
+ if (protocol_version() >= 4) {
+ expected_length += goaway.description().size();
+ }
+ SpdyFrameBuilder builder(expected_length);
+
+ // Serialize the GOAWAY frame.
if (spdy_version_ < 4) {
builder.WriteControlFrameHeader(*this, GOAWAY, kNoFlags);
} else {
builder.WriteFramePrefix(*this, GOAWAY, 0, 0);
}
+
+ // GOAWAY frames specify the last good stream id for all SPDY versions.
builder.WriteUInt32(goaway.last_good_stream_id());
+
+ // In SPDY3 and up, GOAWAY frames also specify the error status code.
if (protocol_version() >= 3) {
builder.WriteUInt32(goaway.status());
}
- DCHECK_EQ(GetGoAwaySize(), builder.length());
+
+ // In SPDY4 and up, GOAWAY frames may also specify opaque data.
+ if ((protocol_version() >= 4) && (goaway.description().size() > 0)) {
+ builder.WriteBytes(goaway.description().data(),
+ goaway.description().size());
+ }
+
+ DCHECK_EQ(expected_length, builder.length());
return builder.take();
}