Index: net/spdy/spdy_framer.cc |
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc |
index 04ace4017fb6a56d2ec34b918f693fda03bfc0d1..edcc82e5fa021a3e68a7cd7ee206b8f2c92a2d97 100644 |
--- a/net/spdy/spdy_framer.cc |
+++ b/net/spdy/spdy_framer.cc |
@@ -74,9 +74,10 @@ const size_t kPriorityWeightPayloadSize = 1; |
const SpdyStreamId SpdyFramer::kInvalidStream = static_cast<SpdyStreamId>(-1); |
const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024; |
+// The size of the control frame buffer. Must be >= the minimum size of the |
// largest control frame, which is SYN_STREAM. See GetSynStreamMinimumSize() for |
// calculation details. |
-const size_t SpdyFramer::kControlFrameBufferSize = 18; |
+const size_t SpdyFramer::kControlFrameBufferSize = 19; |
#ifdef DEBUG_SPDY_STATE_CHANGES |
#define CHANGE_STATE(newstate) \ |
@@ -188,7 +189,7 @@ void SpdyFramer::Reset() { |
} |
size_t SpdyFramer::GetDataFrameMinimumSize() const { |
- return SpdyConstants::GetDataFrameMinimumSize(); |
+ return SpdyConstants::GetDataFrameMinimumSize(protocol_version()); |
} |
// Size, in bytes, of the control frame header. |
@@ -497,16 +498,16 @@ const char* SpdyFramer::FrameTypeToString(SpdyFrameType type) { |
return "WINDOW_UPDATE"; |
case CREDENTIAL: |
return "CREDENTIAL"; |
- case BLOCKED: |
- return "BLOCKED"; |
case PUSH_PROMISE: |
return "PUSH_PROMISE"; |
case CONTINUATION: |
return "CONTINUATION"; |
- case ALTSVC: |
- return "ALTSVC"; |
case PRIORITY: |
return "PRIORITY"; |
+ case ALTSVC: |
+ return "ALTSVC"; |
+ case BLOCKED: |
+ return "BLOCKED"; |
} |
return "UNKNOWN_CONTROL_TYPE"; |
} |
@@ -722,8 +723,8 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { |
current_frame_length_ = remaining_data_length_ + reader->GetBytesConsumed(); |
} else { |
version = protocol_version(); |
- uint16 length_field = 0; |
- bool successful_read = reader->ReadUInt16(&length_field); |
+ uint32 length_field = 0; |
+ bool successful_read = reader->ReadUInt24(&length_field); |
DCHECK(successful_read); |
uint8 control_frame_type_field_uint8 = |
@@ -734,9 +735,9 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { |
// ProcessControlFrameHeader(). |
control_frame_type_field = control_frame_type_field_uint8; |
is_control_frame = (protocol_version() > SPDY3) ? |
- control_frame_type_field != |
- SpdyConstants::SerializeFrameType(protocol_version(), DATA) : |
- control_frame_type_field != 0; |
+ control_frame_type_field != |
+ SpdyConstants::SerializeFrameType(protocol_version(), DATA) : |
+ control_frame_type_field != 0; |
if (is_control_frame) { |
current_frame_length_ = length_field + GetControlFrameHeaderSize(); |
@@ -837,7 +838,6 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
// or add them to parsing + serialization methods for SPDY3. |
// Early detection of deprecated frames that we ignore. |
if (protocol_version() <= SPDY3) { |
- |
if (control_frame_type_field == CREDENTIAL) { |
current_frame_type_ = CREDENTIAL; |
DCHECK_EQ(SPDY3, protocol_version()); |
@@ -849,10 +849,31 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
if (!SpdyConstants::IsValidFrameType(protocol_version(), |
control_frame_type_field)) { |
- DLOG(WARNING) << "Invalid control frame type " << control_frame_type_field |
- << " (protocol version: " << protocol_version() << ")"; |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- return; |
+ if (protocol_version() <= SPDY3) { |
+ DLOG(WARNING) << "Invalid control frame type " << control_frame_type_field |
+ << " (protocol version: " << protocol_version() << ")"; |
+ set_error(SPDY_INVALID_CONTROL_FRAME); |
+ return; |
+ } else { |
+ // In HTTP2 we ignore unknown frame types for extensibility, as long as |
+ // the rest of the control frame header is valid. |
+ // We rely on the visitor to check validity of current_frame_stream_id_. |
+ bool valid_stream = visitor_->OnUnknownFrame(current_frame_stream_id_, |
+ control_frame_type_field); |
+ if (valid_stream) { |
+ DVLOG(1) << "Ignoring unknown frame type."; |
+ CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD); |
+ } else { |
+ // Report an invalid frame error and close the stream if the |
+ // stream_id is not valid. |
+ DLOG(WARNING) << "Unknown control frame type " |
+ << control_frame_type_field |
+ << " received on invalid stream " |
+ << current_frame_stream_id_; |
+ set_error(SPDY_INVALID_CONTROL_FRAME); |
+ } |
+ return; |
+ } |
} |
current_frame_type_ = SpdyConstants::ParseFrameType(protocol_version(), |
@@ -947,6 +968,8 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
min_size += 4; |
} |
if (current_frame_length_ < min_size) { |
+ // TODO(mlavan): check here for HEADERS with no payload? |
+ // (not allowed in SPDY4) |
set_error(SPDY_INVALID_CONTROL_FRAME); |
} else if (protocol_version() <= SPDY3 && |
current_frame_flags_ & ~CONTROL_FLAG_FIN) { |
@@ -970,7 +993,6 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
case BLOCKED: |
if (current_frame_length_ != GetBlockedSize() || |
protocol_version() <= SPDY3) { |
- // TODO(mlavan): BLOCKED frames are no longer part of SPDY4. |
set_error(SPDY_INVALID_CONTROL_FRAME); |
} else if (current_frame_flags_ != 0) { |
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
@@ -1732,7 +1754,12 @@ bool SpdyFramer::ProcessSetting(const char* data) { |
// Validate id. |
if (!SpdyConstants::IsValidSettingId(protocol_version(), id_field)) { |
DLOG(WARNING) << "Unknown SETTINGS ID: " << id_field; |
- return false; |
+ if (protocol_version() <= SPDY3) { |
+ return false; |
+ } else { |
+ // In HTTP2 we ignore unknown settings for extensibility. |
+ return true; |
+ } |
} |
id = SpdyConstants::ParseSettingId(protocol_version(), id_field); |
@@ -1877,13 +1904,10 @@ size_t SpdyFramer::ProcessGoAwayFramePayload(const char* data, size_t len) { |
status = SpdyConstants::ParseGoAwayStatus(protocol_version(), |
status_raw); |
} else { |
- DCHECK(false); |
- // Throw an error for SPDY4+, keep liberal behavior |
- // for earlier versions. |
if (protocol_version() > SPDY3) { |
- DLOG(WARNING) << "Invalid GO_AWAY status " << status_raw; |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- return 0; |
+ // Treat unrecognized status codes as INTERNAL_ERROR as |
+ // recommended by the HTTP/2 spec. |
+ status = GOAWAY_INTERNAL_ERROR; |
} |
} |
} |
@@ -1946,12 +1970,10 @@ size_t SpdyFramer::ProcessRstStreamFramePayload(const char* data, size_t len) { |
status_raw)) { |
status = static_cast<SpdyRstStreamStatus>(status_raw); |
} else { |
- // Throw an error for SPDY4+, keep liberal behavior |
- // for earlier versions. |
if (protocol_version() > SPDY3) { |
- DLOG(WARNING) << "Invalid RST_STREAM status " << status_raw; |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- return 0; |
+ // Treat unrecognized status codes as INTERNAL_ERROR as |
+ // recommended by the HTTP/2 spec. |
+ status = RST_STREAM_INTERNAL_ERROR; |
} |
} |
// Finished parsing the RST_STREAM header, call frame handler. |