summaryrefslogtreecommitdiffstats
path: root/net
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
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')
-rw-r--r--net/quic/quic_headers_stream_test.cc2
-rw-r--r--net/spdy/buffered_spdy_framer.cc2
-rw-r--r--net/spdy/spdy_framer.cc158
-rw-r--r--net/spdy/spdy_framer.h26
-rw-r--r--net/spdy/spdy_framer_test.cc55
-rw-r--r--net/spdy/spdy_protocol.cc14
-rw-r--r--net/spdy/spdy_protocol.h12
-rw-r--r--net/spdy/spdy_session.cc68
-rw-r--r--net/spdy/spdy_session.h89
-rw-r--r--net/spdy/spdy_session_unittest.cc21
-rw-r--r--net/spdy/spdy_test_util_common.cc3
11 files changed, 333 insertions, 117 deletions
diff --git a/net/quic/quic_headers_stream_test.cc b/net/quic/quic_headers_stream_test.cc
index cbe8f5d..d5aea09 100644
--- a/net/quic/quic_headers_stream_test.cc
+++ b/net/quic/quic_headers_stream_test.cc
@@ -279,7 +279,7 @@ TEST_P(QuicHeadersStreamTest, ProcessSpdyPingFrame) {
}
TEST_P(QuicHeadersStreamTest, ProcessSpdyGoAwayFrame) {
- SpdyGoAwayIR data(1, GOAWAY_PROTOCOL_ERROR);
+ SpdyGoAwayIR data(1, GOAWAY_PROTOCOL_ERROR, "go away");
scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
EXPECT_CALL(*connection_,
SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc
index fee2192..03a098d 100644
--- a/net/spdy/buffered_spdy_framer.cc
+++ b/net/spdy/buffered_spdy_framer.cc
@@ -283,7 +283,7 @@ SpdyFrame* BufferedSpdyFramer::CreatePingFrame(
SpdyFrame* BufferedSpdyFramer::CreateGoAway(
SpdyStreamId last_accepted_stream_id,
SpdyGoAwayStatus status) const {
- return spdy_framer_.CreateGoAway(last_accepted_stream_id, status);
+ return spdy_framer_.CreateGoAway(last_accepted_stream_id, status, "go away");
}
SpdyFrame* BufferedSpdyFramer::CreateHeaders(
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();
}
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index 07903f1..2fc2247 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -213,7 +213,7 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface {
uint32 delta_window_size) = 0;
// Called when a chunk of payload data for a credential frame is available.
- // |header_data| A buffer containing the header data chunk received.
+ // |credential_data| A buffer containing the header data chunk received.
// |len| The length of the header data buffer. A length of zero indicates
// that the header data block has been completely sent.
// When this function returns true the visitor indicates that it accepted
@@ -222,6 +222,15 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface {
virtual bool OnCredentialFrameData(const char* credential_data,
size_t len) = 0;
+ // Called when a goaway frame opaque data is available.
+ // |goaway_data| A buffer containing the opaque GOAWAY data chunk received.
+ // |len| The length of the header data buffer. A length of zero indicates
+ // that the header data block has been completely sent.
+ // When this function returns true the visitor indicates that it accepted
+ // all of the data. Returning false indicates that that an error has
+ // occurred while processing the data. Default implementation returns true.
+ virtual bool OnGoAwayFrameData(const char* goaway_data, size_t len);
+
// Called when a BLOCKED frame has been parsed.
virtual void OnBlocked(SpdyStreamId stream_id) {}
@@ -275,6 +284,7 @@ class NET_EXPORT_PRIVATE SpdyFramer {
SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK,
SPDY_CONTROL_FRAME_HEADER_BLOCK,
SPDY_CREDENTIAL_FRAME_PAYLOAD,
+ SPDY_GOAWAY_FRAME_PAYLOAD,
SPDY_SETTINGS_FRAME_PAYLOAD,
};
@@ -288,6 +298,7 @@ class NET_EXPORT_PRIVATE SpdyFramer {
SPDY_DECOMPRESS_FAILURE, // There was an error decompressing.
SPDY_COMPRESS_FAILURE, // There was an error compressing.
SPDY_CREDENTIAL_FRAME_CORRUPT, // CREDENTIAL frame could not be parsed.
+ SPDY_GOAWAY_FRAME_CORRUPT, // GOAWAY frame could not be parsed.
SPDY_INVALID_DATA_FRAME_FLAGS, // Data frame has invalid flags.
SPDY_INVALID_CONTROL_FRAME_FLAGS, // Control frame has invalid flags.
@@ -414,7 +425,8 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// stream_id of the last stream the sender of the frame is willing to process
// to completion.
SpdyFrame* CreateGoAway(SpdyStreamId last_accepted_stream_id,
- SpdyGoAwayStatus status) const;
+ SpdyGoAwayStatus status,
+ const base::StringPiece& description) const;
SpdySerializedFrame* SerializeGoAway(const SpdyGoAwayIR& goaway) const;
// Creates and serializes a HEADERS frame. The HEADERS frame is used
@@ -503,7 +515,7 @@ class NET_EXPORT_PRIVATE SpdyFramer {
size_t GetRstStreamSize() const;
size_t GetSettingsMinimumSize() const;
size_t GetPingSize() const;
- size_t GetGoAwaySize() const;
+ size_t GetGoAwayMinimumSize() const;
size_t GetHeadersMinimumSize() const;
size_t GetWindowUpdateSize() const;
size_t GetCredentialMinimumSize() const;
@@ -577,8 +589,10 @@ class NET_EXPORT_PRIVATE SpdyFramer {
size_t ProcessCredentialFramePayload(const char* data, size_t len);
size_t ProcessControlFrameBeforeHeaderBlock(const char* data, size_t len);
size_t ProcessControlFrameHeaderBlock(const char* data, size_t len);
- size_t ProcessSettingsFramePayload(const char* data, size_t len);
size_t ProcessDataFramePayload(const char* data, size_t len);
+ size_t ProcessGoAwayFramePayload(const char* data, size_t len);
+ size_t ProcessSettingsFramePayload(const char* data, size_t len);
+
// Helpers for above internal breakouts from ProcessInput.
void ProcessControlFrameHeader(uint16 control_frame_type_field);
@@ -697,8 +711,8 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// type SYN_STREAM or SYN_REPLY.
//
// If we ever get something which looks like a data frame before we've had a
- // SYN, we explicitly check to see if it looks like we got an HTTP response to
- // a SPDY request. This boolean lets us do that.
+ // SYN, we explicitly check to see if it looks like we got an HTTP response
+ // to a SPDY request. This boolean lets us do that.
bool syn_frame_processed_;
// If we ever get a data frame before a SYN frame, we check to see if it
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
index 71027d0..a9ed1c8 100644
--- a/net/spdy/spdy_framer_test.cc
+++ b/net/spdy/spdy_framer_test.cc
@@ -2647,21 +2647,22 @@ TEST_P(SpdyFramerTest, CreateGoAway) {
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, // Stream Id
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x08,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, // Stream Id
+ 0x00, 0x00, 0x00, 0x00, // Status
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x10, 0x07, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x12, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, // Stream id
+ 0x00, 0x00, 0x00, 0x00, // Status
+ 0x47, 0x41, // Opaque Description
};
- scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0, GOAWAY_OK));
+ scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0, GOAWAY_OK, "GA"));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -2676,22 +2677,24 @@ TEST_P(SpdyFramerTest, CreateGoAway) {
const unsigned char kV2FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x04,
- 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff, // Stream Id
};
const unsigned char kV3FrameData[] = {
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, 0x08,
- 0x7f, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x02,
+ 0x7f, 0xff, 0xff, 0xff, // Stream Id
+ 0x00, 0x00, 0x00, 0x02, // Status
};
const unsigned char kV4FrameData[] = {
- 0x00, 0x10, 0x07, 0x00,
+ 0x00, 0x12, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00,
- 0x7f, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x02,
+ 0x7f, 0xff, 0xff, 0xff, // Stream Id
+ 0x00, 0x00, 0x00, 0x02, // Status
+ 0x47, 0x41, // Opaque Description
};
scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0x7FFFFFFF,
- GOAWAY_INTERNAL_ERROR));
+ GOAWAY_INTERNAL_ERROR,
+ "GA"));
if (IsSpdy2()) {
CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
} else if (IsSpdy3()) {
@@ -3379,6 +3382,7 @@ TEST_P(SpdyFramerTest, DecompressCorruptHeaderBlock) {
}
TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) {
+ SpdyFramer framer(spdy_version_);
// Create a GoAway frame that has a few extra bytes at the end.
// We create enough overhead to overflow the framer's control frame buffer.
ASSERT_GE(250u, SpdyFramer::kControlFrameBufferSize);
@@ -3386,15 +3390,19 @@ TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) {
const unsigned char kV3FrameData[] = { // Also applies for V2.
0x80, spdy_version_ch_, 0x00, 0x07,
0x00, 0x00, 0x00, length,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, // Stream ID
+ 0x00, 0x00, 0x00, 0x00, // Status
};
+
+ // SPDY version 4 and up GOAWAY frames are only bound to a minimal length,
+ // since it may carry opaque data. Verify that minimal length is tested.
+ const unsigned char less_than_min_length = framer.GetGoAwayMinimumSize() - 1;
const unsigned char kV4FrameData[] = {
- 0x00, static_cast<uint8>(length + 4), 0x07, 0x00,
- 0x00, 0x00, 0x00, 0x00,
+ 0x00, static_cast<uint8>(less_than_min_length), 0x07, 0x00,
0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, // Stream Id
+ 0x00, 0x00, 0x00, 0x00, // Status
};
- SpdyFramer framer(spdy_version_);
const size_t pad_length =
length + framer.GetControlFrameHeaderSize() -
(IsSpdy4() ? sizeof(kV4FrameData) : sizeof(kV3FrameData));
@@ -3772,7 +3780,8 @@ TEST_P(SpdyFramerTest, ReadCredentialFrameFollowedByAnotherFrame) {
visitor.use_compression_ = false;
string multiple_frame_data(credential_frame->data(),
credential_frame->size());
- scoped_ptr<SpdyFrame> goaway_frame(framer.CreateGoAway(0, GOAWAY_OK));
+ scoped_ptr<SpdyFrame> goaway_frame(
+ framer.CreateGoAway(0, GOAWAY_OK, "test"));
multiple_frame_data.append(string(goaway_frame->data(),
goaway_frame->size()));
visitor.SimulateInFramer(
@@ -3844,7 +3853,7 @@ TEST_P(SpdyFramerTest, SizesTest) {
EXPECT_EQ(12u, framer.GetRstStreamSize());
EXPECT_EQ(12u, framer.GetSettingsMinimumSize());
EXPECT_EQ(12u, framer.GetPingSize());
- EXPECT_EQ(16u, framer.GetGoAwaySize());
+ EXPECT_EQ(16u, framer.GetGoAwayMinimumSize());
EXPECT_EQ(8u, framer.GetHeadersMinimumSize());
EXPECT_EQ(12u, framer.GetWindowUpdateSize());
EXPECT_EQ(10u, framer.GetCredentialMinimumSize());
@@ -3860,7 +3869,7 @@ TEST_P(SpdyFramerTest, SizesTest) {
EXPECT_EQ(16u, framer.GetRstStreamSize());
EXPECT_EQ(12u, framer.GetSettingsMinimumSize());
EXPECT_EQ(12u, framer.GetPingSize());
- EXPECT_EQ(IsSpdy2() ? 12u : 16u, framer.GetGoAwaySize());
+ EXPECT_EQ(IsSpdy2() ? 12u : 16u, framer.GetGoAwayMinimumSize());
EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetHeadersMinimumSize());
EXPECT_EQ(16u, framer.GetWindowUpdateSize());
EXPECT_EQ(10u, framer.GetCredentialMinimumSize());
@@ -4237,7 +4246,7 @@ TEST_P(SpdyFramerTest, GoawayFrameFlags) {
SpdyFramer framer(spdy_version_);
framer.set_visitor(&visitor);
- scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(97, GOAWAY_OK));
+ scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(97, GOAWAY_OK, "test"));
SetFrameFlags(frame.get(), flags, spdy_version_);
if (flags != 0) {
diff --git a/net/spdy/spdy_protocol.cc b/net/spdy/spdy_protocol.cc
index 39031eb..13bd84f 100644
--- a/net/spdy/spdy_protocol.cc
+++ b/net/spdy/spdy_protocol.cc
@@ -49,6 +49,20 @@ void SpdyPingIR::Visit(SpdyFrameVisitor* visitor) const {
return visitor->VisitPing(*this);
}
+SpdyGoAwayIR::SpdyGoAwayIR(SpdyStreamId last_good_stream_id,
+ SpdyGoAwayStatus status,
+ const base::StringPiece& description)
+ : description_(description) {
+ set_last_good_stream_id(last_good_stream_id);
+ set_status(status);
+}
+
+SpdyGoAwayIR::~SpdyGoAwayIR() {}
+
+const base::StringPiece& SpdyGoAwayIR::description() const {
+ return description_;
+}
+
void SpdyGoAwayIR::Visit(SpdyFrameVisitor* visitor) const {
return visitor->VisitGoAway(*this);
}
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h
index 13d1c7d..bc58742 100644
--- a/net/spdy/spdy_protocol.h
+++ b/net/spdy/spdy_protocol.h
@@ -355,7 +355,7 @@ enum SpdyGoAwayStatus {
GOAWAY_OK = 0,
GOAWAY_PROTOCOL_ERROR = 1,
GOAWAY_INTERNAL_ERROR = 2,
- GOAWAY_NUM_STATUS_CODES = 3
+ GOAWAY_NUM_STATUS_CODES = 3 // Must be last.
};
// A SPDY priority is a number between 0 and 7 (inclusive).
@@ -617,10 +617,9 @@ class NET_EXPORT_PRIVATE SpdyPingIR : public SpdyFrameIR {
class NET_EXPORT_PRIVATE SpdyGoAwayIR : public SpdyFrameIR {
public:
- SpdyGoAwayIR(SpdyStreamId last_good_stream_id, SpdyGoAwayStatus status) {
- set_last_good_stream_id(last_good_stream_id);
- set_status(status);
- }
+ SpdyGoAwayIR(SpdyStreamId last_good_stream_id, SpdyGoAwayStatus status,
+ const base::StringPiece& description);
+ virtual ~SpdyGoAwayIR();
SpdyStreamId last_good_stream_id() const { return last_good_stream_id_; }
void set_last_good_stream_id(SpdyStreamId last_good_stream_id) {
DCHECK_LE(0u, last_good_stream_id);
@@ -633,11 +632,14 @@ class NET_EXPORT_PRIVATE SpdyGoAwayIR : public SpdyFrameIR {
status_ = status;
}
+ const base::StringPiece& description() const;
+
virtual void Visit(SpdyFrameVisitor* visitor) const OVERRIDE;
private:
SpdyStreamId last_good_stream_id_;
SpdyGoAwayStatus status_;
+ const base::StringPiece description_;
DISALLOW_COPY_AND_ASSIGN(SpdyGoAwayIR);
};
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 656e851..8d7a4a8 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -259,6 +259,68 @@ const size_t kMaxConcurrentStreamLimit = 256;
} // namespace
+SpdyProtocolErrorDetails MapFramerErrorToProtocolError(
+ SpdyFramer::SpdyError err) {
+ switch(err) {
+ case SpdyFramer::SPDY_NO_ERROR:
+ return SPDY_ERROR_NO_ERROR;
+ case SpdyFramer::SPDY_INVALID_CONTROL_FRAME:
+ return SPDY_ERROR_INVALID_CONTROL_FRAME;
+ case SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE:
+ return SPDY_ERROR_CONTROL_PAYLOAD_TOO_LARGE;
+ case SpdyFramer::SPDY_ZLIB_INIT_FAILURE:
+ return SPDY_ERROR_ZLIB_INIT_FAILURE;
+ case SpdyFramer::SPDY_UNSUPPORTED_VERSION:
+ return SPDY_ERROR_UNSUPPORTED_VERSION;
+ case SpdyFramer::SPDY_DECOMPRESS_FAILURE:
+ return SPDY_ERROR_DECOMPRESS_FAILURE;
+ case SpdyFramer::SPDY_COMPRESS_FAILURE:
+ return SPDY_ERROR_COMPRESS_FAILURE;
+ case SpdyFramer::SPDY_CREDENTIAL_FRAME_CORRUPT:
+ return SPDY_ERROR_CREDENTIAL_FRAME_CORRUPT;
+ case SpdyFramer::SPDY_GOAWAY_FRAME_CORRUPT:
+ return SPDY_ERROR_GOAWAY_FRAME_CORRUPT;
+ case SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS:
+ return SPDY_ERROR_INVALID_DATA_FRAME_FLAGS;
+ case SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS:
+ return SPDY_ERROR_INVALID_CONTROL_FRAME_FLAGS;
+ default:
+ NOTREACHED();
+ return static_cast<SpdyProtocolErrorDetails>(-1);
+ }
+}
+
+SpdyProtocolErrorDetails MapRstStreamStatusToProtocolError(
+ SpdyRstStreamStatus status) {
+ switch(status) {
+ case RST_STREAM_PROTOCOL_ERROR:
+ return STATUS_CODE_PROTOCOL_ERROR;
+ case RST_STREAM_INVALID_STREAM:
+ return STATUS_CODE_INVALID_STREAM;
+ case RST_STREAM_REFUSED_STREAM:
+ return STATUS_CODE_REFUSED_STREAM;
+ case RST_STREAM_UNSUPPORTED_VERSION:
+ return STATUS_CODE_UNSUPPORTED_VERSION;
+ case RST_STREAM_CANCEL:
+ return STATUS_CODE_CANCEL;
+ case RST_STREAM_INTERNAL_ERROR:
+ return STATUS_CODE_INTERNAL_ERROR;
+ case RST_STREAM_FLOW_CONTROL_ERROR:
+ return STATUS_CODE_FLOW_CONTROL_ERROR;
+ case RST_STREAM_STREAM_IN_USE:
+ return STATUS_CODE_STREAM_IN_USE;
+ case RST_STREAM_STREAM_ALREADY_CLOSED:
+ return STATUS_CODE_STREAM_ALREADY_CLOSED;
+ case RST_STREAM_INVALID_CREDENTIALS:
+ return STATUS_CODE_INVALID_CREDENTIALS;
+ case RST_STREAM_FRAME_TOO_LARGE:
+ return STATUS_CODE_FRAME_TOO_LARGE;
+ default:
+ NOTREACHED();
+ return static_cast<SpdyProtocolErrorDetails>(-1);
+ }
+}
+
SpdyStreamRequest::SpdyStreamRequest() : weak_ptr_factory_(this) {
Reset();
}
@@ -1082,8 +1144,7 @@ void SpdySession::EnqueueResetStreamFrame(SpdyStreamId stream_id,
buffered_spdy_framer_->CreateRstStream(stream_id, status));
EnqueueSessionWrite(priority, RST_STREAM, rst_frame.Pass());
- RecordProtocolErrorHistogram(
- static_cast<SpdyProtocolErrorDetails>(status + STATUS_CODE_INVALID));
+ RecordProtocolErrorHistogram(MapRstStreamStatusToProtocolError(status));
}
void SpdySession::PumpReadLoop(ReadState expected_read_state, int result) {
@@ -1779,8 +1840,7 @@ void SpdySession::OnError(SpdyFramer::SpdyError error_code) {
if (availability_state_ == STATE_CLOSED)
return;
- RecordProtocolErrorHistogram(
- static_cast<SpdyProtocolErrorDetails>(error_code));
+ RecordProtocolErrorHistogram(MapFramerErrorToProtocolError(error_code));
std::string description = base::StringPrintf(
"SPDY_ERROR error_code: %d.", error_code);
CloseSessionResult result =
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index fde42024b..e829369 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -67,53 +67,56 @@ class SpdyStream;
class SSLInfo;
// NOTE: There's an enum of the same name (also with numeric suffixes)
-// in histograms.xml.
-//
-// WARNING: DO NOT INSERT ENUMS INTO THIS LIST! Add only to the end.
+// in histograms.xml. Be sure to add new values there also.
enum SpdyProtocolErrorDetails {
- // SpdyFramer::SpdyErrors
- SPDY_ERROR_NO_ERROR,
- SPDY_ERROR_INVALID_CONTROL_FRAME,
- SPDY_ERROR_CONTROL_PAYLOAD_TOO_LARGE,
- SPDY_ERROR_ZLIB_INIT_FAILURE,
- SPDY_ERROR_UNSUPPORTED_VERSION,
- SPDY_ERROR_DECOMPRESS_FAILURE,
- SPDY_ERROR_COMPRESS_FAILURE,
- SPDY_ERROR_CREDENTIAL_FRAME_CORRUPT,
- SPDY_ERROR_INVALID_DATA_FRAME_FLAGS,
- SPDY_ERROR_INVALID_CONTROL_FRAME_FLAGS,
- // SpdyRstStreamStatus
- STATUS_CODE_INVALID,
- STATUS_CODE_PROTOCOL_ERROR,
- STATUS_CODE_INVALID_STREAM,
- STATUS_CODE_REFUSED_STREAM,
- STATUS_CODE_UNSUPPORTED_VERSION,
- STATUS_CODE_CANCEL,
- STATUS_CODE_INTERNAL_ERROR,
- STATUS_CODE_FLOW_CONTROL_ERROR,
- STATUS_CODE_STREAM_IN_USE,
- STATUS_CODE_STREAM_ALREADY_CLOSED,
- STATUS_CODE_INVALID_CREDENTIALS,
- STATUS_CODE_FRAME_TOO_LARGE,
+ // SpdyFramer::SpdyError mappings.
+ SPDY_ERROR_NO_ERROR = 0,
+ SPDY_ERROR_INVALID_CONTROL_FRAME = 1,
+ SPDY_ERROR_CONTROL_PAYLOAD_TOO_LARGE = 2,
+ SPDY_ERROR_ZLIB_INIT_FAILURE = 3,
+ SPDY_ERROR_UNSUPPORTED_VERSION = 4,
+ SPDY_ERROR_DECOMPRESS_FAILURE = 5,
+ SPDY_ERROR_COMPRESS_FAILURE = 6,
+ SPDY_ERROR_CREDENTIAL_FRAME_CORRUPT = 7,
+ SPDY_ERROR_GOAWAY_FRAME_CORRUPT = 29,
+ SPDY_ERROR_INVALID_DATA_FRAME_FLAGS = 8,
+ SPDY_ERROR_INVALID_CONTROL_FRAME_FLAGS = 9,
+ // SpdyRstStreamStatus mappings.
+ // RST_STREAM_INVALID not mapped.
+ STATUS_CODE_PROTOCOL_ERROR = 11,
+ STATUS_CODE_INVALID_STREAM = 12,
+ STATUS_CODE_REFUSED_STREAM = 13,
+ STATUS_CODE_UNSUPPORTED_VERSION = 14,
+ STATUS_CODE_CANCEL = 15,
+ STATUS_CODE_INTERNAL_ERROR = 16,
+ STATUS_CODE_FLOW_CONTROL_ERROR = 17,
+ STATUS_CODE_STREAM_IN_USE = 18,
+ STATUS_CODE_STREAM_ALREADY_CLOSED = 19,
+ STATUS_CODE_INVALID_CREDENTIALS = 20,
+ STATUS_CODE_FRAME_TOO_LARGE = 21,
// SpdySession errors
- PROTOCOL_ERROR_UNEXPECTED_PING,
- PROTOCOL_ERROR_RST_STREAM_FOR_NON_ACTIVE_STREAM,
- PROTOCOL_ERROR_SPDY_COMPRESSION_FAILURE,
- PROTOCOL_ERROR_REQUEST_FOR_SECURE_CONTENT_OVER_INSECURE_SESSION,
- PROTOCOL_ERROR_SYN_REPLY_NOT_RECEIVED,
- PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE,
- PROTOCOL_ERROR_RECEIVE_WINDOW_VIOLATION,
- NUM_SPDY_PROTOCOL_ERROR_DETAILS
+ PROTOCOL_ERROR_UNEXPECTED_PING = 22,
+ PROTOCOL_ERROR_RST_STREAM_FOR_NON_ACTIVE_STREAM = 23,
+ PROTOCOL_ERROR_SPDY_COMPRESSION_FAILURE = 24,
+ PROTOCOL_ERROR_REQUEST_FOR_SECURE_CONTENT_OVER_INSECURE_SESSION = 25,
+ PROTOCOL_ERROR_SYN_REPLY_NOT_RECEIVED = 26,
+ PROTOCOL_ERROR_INVALID_WINDOW_UPDATE_SIZE = 27,
+ PROTOCOL_ERROR_RECEIVE_WINDOW_VIOLATION = 28,
+
+ // Next free value.
+ NUM_SPDY_PROTOCOL_ERROR_DETAILS = 30,
};
-
-COMPILE_ASSERT(STATUS_CODE_INVALID ==
- static_cast<SpdyProtocolErrorDetails>(SpdyFramer::LAST_ERROR),
- SpdyProtocolErrorDetails_SpdyErrors_mismatch);
-
-COMPILE_ASSERT(PROTOCOL_ERROR_UNEXPECTED_PING ==
- static_cast<SpdyProtocolErrorDetails>(
- RST_STREAM_NUM_STATUS_CODES + STATUS_CODE_INVALID),
+SpdyProtocolErrorDetails NET_EXPORT_PRIVATE MapFramerErrorToProtocolError(
+ SpdyFramer::SpdyError);
+SpdyProtocolErrorDetails NET_EXPORT_PRIVATE MapRstStreamStatusToProtocolError(
+ SpdyRstStreamStatus);
+
+// If these compile asserts fail then SpdyProtocolErrorDetails needs
+// to be updated with new values, as do the mapping functions above.
+COMPILE_ASSERT(11 == SpdyFramer::LAST_ERROR,
SpdyProtocolErrorDetails_SpdyErrors_mismatch);
+COMPILE_ASSERT(12 == RST_STREAM_NUM_STATUS_CODES,
+ SpdyProtocolErrorDetails_RstStreamStatus_mismatch);
// A helper class used to manage a request to create a stream.
class NET_EXPORT_PRIVATE SpdyStreamRequest {
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index 2f185c7..32c94a0 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -4141,4 +4141,25 @@ TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedSession) {
EXPECT_TRUE(data.at_write_eof());
}
+TEST(MapFramerErrorToProtocolError, MapsValues) {
+ CHECK_EQ(SPDY_ERROR_INVALID_CONTROL_FRAME,
+ MapFramerErrorToProtocolError(
+ SpdyFramer::SPDY_INVALID_CONTROL_FRAME));
+ CHECK_EQ(SPDY_ERROR_INVALID_DATA_FRAME_FLAGS,
+ MapFramerErrorToProtocolError(
+ SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS));
+ CHECK_EQ(SPDY_ERROR_GOAWAY_FRAME_CORRUPT,
+ MapFramerErrorToProtocolError(
+ SpdyFramer::SPDY_GOAWAY_FRAME_CORRUPT));
+}
+
+TEST(MapRstStreamStatusToProtocolError, MapsValues) {
+ CHECK_EQ(STATUS_CODE_PROTOCOL_ERROR,
+ MapRstStreamStatusToProtocolError(
+ RST_STREAM_PROTOCOL_ERROR));
+ CHECK_EQ(STATUS_CODE_FRAME_TOO_LARGE,
+ MapRstStreamStatusToProtocolError(
+ RST_STREAM_FRAME_TOO_LARGE));
+}
+
} // namespace net
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 89d1882..bc6d5ac 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -874,7 +874,8 @@ SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway() const {
SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway(
SpdyStreamId last_good_stream_id) const {
- return CreateFramer()->CreateGoAway(last_good_stream_id, GOAWAY_OK);
+ return CreateFramer()->CreateGoAway(last_good_stream_id, GOAWAY_OK,
+ "go away");
}
SpdyFrame* SpdyTestUtil::ConstructSpdyWindowUpdate(