diff options
author | jgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-21 00:57:37 +0000 |
---|---|---|
committer | jgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-21 00:57:37 +0000 |
commit | cd9c5f67f7f3de52c9e170cc163fa2af45b188f8 (patch) | |
tree | 8b589367c9125795f6156d111fe25ee6d1139c3e /net | |
parent | 900349daf01d24b268ac33873b41e4af80733faf (diff) | |
download | chromium_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.cc | 2 | ||||
-rw-r--r-- | net/spdy/buffered_spdy_framer.cc | 2 | ||||
-rw-r--r-- | net/spdy/spdy_framer.cc | 158 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 26 | ||||
-rw-r--r-- | net/spdy/spdy_framer_test.cc | 55 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.cc | 14 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.h | 12 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 68 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 89 | ||||
-rw-r--r-- | net/spdy/spdy_session_unittest.cc | 21 | ||||
-rw-r--r-- | net/spdy/spdy_test_util_common.cc | 3 |
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(¤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 >= 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(¤t_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( |