Index: net/spdy/spdy_framer.cc |
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc |
index f7b52831d7912d9bf166f3bbfb55f35d2559c948..7153ad491dab20cb42ac2e8882d93eac4956e980 100644 |
--- a/net/spdy/spdy_framer.cc |
+++ b/net/spdy/spdy_framer.cc |
@@ -929,9 +929,11 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
} else if (protocol_version() <= SPDY3 && |
current_frame_flags_ & ~CONTROL_FLAG_FIN) { |
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } else if (protocol_version() > SPDY3 && current_frame_flags_ & |
- ~(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY | |
- HEADERS_FLAG_END_HEADERS)) { |
+ } else if (protocol_version() > SPDY3 && |
+ current_frame_flags_ & |
+ ~(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY | |
+ HEADERS_FLAG_END_HEADERS | HEADERS_FLAG_END_SEGMENT | |
+ HEADERS_FLAG_PAD_LOW | HEADERS_FLAG_PAD_HIGH)) { |
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
} |
} |
@@ -957,8 +959,10 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
set_error(SPDY_INVALID_CONTROL_FRAME); |
} else if (protocol_version() <= SPDY3 && current_frame_flags_ != 0) { |
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } else if (protocol_version() > SPDY3 && current_frame_flags_ & |
- ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE) { |
+ } else if (protocol_version() > SPDY3 && |
+ current_frame_flags_ & |
+ ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE | |
+ HEADERS_FLAG_PAD_LOW | HEADERS_FLAG_PAD_HIGH)) { |
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
} |
break; |
@@ -966,7 +970,9 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
if (current_frame_length_ < GetContinuationMinimumSize() || |
protocol_version() <= SPDY3) { |
set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ & ~HEADERS_FLAG_END_HEADERS) { |
+ } else if (current_frame_flags_ & |
+ ~(HEADERS_FLAG_END_HEADERS | HEADERS_FLAG_PAD_LOW | |
+ HEADERS_FLAG_PAD_HIGH)) { |
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
} |
break; |
@@ -1444,7 +1450,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, |
expect_continuation_ == 0); |
} |
} |
- CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); |
+ CHANGE_STATE(SPDY_READ_PADDING_LENGTH); |
break; |
case PUSH_PROMISE: |
{ |
@@ -1475,7 +1481,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, |
(current_frame_flags_ & |
PUSH_PROMISE_FLAG_END_PUSH_PROMISE) != 0); |
} |
- CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); |
+ CHANGE_STATE(SPDY_READ_PADDING_LENGTH); |
break; |
case CONTINUATION: |
{ |
@@ -1501,7 +1507,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, |
(current_frame_flags_ & |
HEADERS_FLAG_END_HEADERS) != 0); |
} |
- CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); |
+ CHANGE_STATE(SPDY_READ_PADDING_LENGTH); |
break; |
default: |
DCHECK(false); |
@@ -1527,7 +1533,8 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, |
current_frame_type_ != CONTINUATION) { |
LOG(DFATAL) << "Unhandled frame type in ProcessControlFrameHeaderBlock."; |
} |
- size_t process_bytes = std::min(data_len, remaining_data_length_); |
+ size_t process_bytes = std::min( |
+ data_len, remaining_data_length_ - remaining_padding_payload_length_); |
if (is_hpack_header_block) { |
if (!GetHpackDecoder()->HandleControlFrameHeadersData( |
current_frame_stream_id_, data, process_bytes)) { |
@@ -1547,7 +1554,8 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, |
remaining_data_length_ -= process_bytes; |
// Handle the case that there is no futher data in this frame. |
- if (remaining_data_length_ == 0 && processed_successfully) { |
+ if (remaining_data_length_ == remaining_padding_payload_length_ && |
+ processed_successfully) { |
if (expect_continuation_ == 0) { |
if (is_hpack_header_block) { |
if (!GetHpackDecoder()->HandleControlFrameHeadersComplete( |
@@ -1567,14 +1575,10 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, |
// OnControlFrameHeaderData() to indicate this. |
visitor_->OnControlFrameHeaderData(current_frame_stream_id_, NULL, 0); |
} |
- // If this is a FIN, tell the caller. |
- if ((current_frame_flags_ & CONTROL_FLAG_FIN) || end_stream_when_done_) { |
- end_stream_when_done_ = false; |
- visitor_->OnStreamFrameData(current_frame_stream_id_, NULL, 0, true); |
- } |
} |
- if (processed_successfully) |
- CHANGE_STATE(SPDY_AUTO_RESET); |
+ if (processed_successfully) { |
+ CHANGE_STATE(SPDY_CONSUME_PADDING); |
+ } |
} |
// Handle error. |
@@ -1644,7 +1648,7 @@ size_t SpdyFramer::ProcessSettingsFramePayload(const char* data, |
void SpdyFramer::DeliverHpackBlockAsSpdy3Block() { |
DCHECK_LT(SPDY3, protocol_version()); |
- DCHECK_EQ(0u, remaining_data_length_); |
+ DCHECK_EQ(remaining_padding_payload_length_, remaining_data_length_); |
const SpdyNameValueBlock& block = GetHpackDecoder()->decoded_block(); |
if (block.empty()) { |
@@ -1659,8 +1663,16 @@ void SpdyFramer::DeliverHpackBlockAsSpdy3Block() { |
SerializeNameValueBlockWithoutCompression(&builder, block); |
scoped_ptr<SpdyFrame> frame(builder.take()); |
+ // Preserve padding length, and reset it after the re-entrant call. |
+ size_t remaining_padding = remaining_padding_payload_length_; |
+ |
+ remaining_padding_payload_length_ = 0; |
remaining_data_length_ = frame->size(); |
+ |
ProcessControlFrameHeaderBlock(frame->data(), frame->size(), false); |
+ |
+ remaining_padding_payload_length_ = remaining_padding; |
+ remaining_data_length_ = remaining_padding; |
} |
bool SpdyFramer::ProcessSetting(const char* data) { |
@@ -2083,7 +2095,17 @@ size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) { |
set_error(SPDY_INVALID_DATA_FRAME_FLAGS); |
return 0; |
} |
- CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); |
+ if (current_frame_type_ == DATA) { |
+ CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); |
+ } else { |
+ DCHECK(current_frame_type_ == HEADERS || |
+ current_frame_type_ == PUSH_PROMISE || |
+ current_frame_type_ == CONTINUATION || |
+ current_frame_type_ == SYN_STREAM || |
+ current_frame_type_ == SYN_REPLY) |
+ << current_frame_type_; |
+ CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); |
+ } |
} |
return original_len - len; |
} |
@@ -2095,10 +2117,10 @@ size_t SpdyFramer::ProcessFramePadding(const char* data, size_t len) { |
if (remaining_padding_payload_length_ > 0) { |
DCHECK_EQ(remaining_padding_payload_length_, remaining_data_length_); |
size_t amount_to_discard = std::min(remaining_padding_payload_length_, len); |
- // The visitor needs to know about padding so it can send window updates. |
- // Communicate the padding to the visitor through a NULL data pointer, with |
- // a nonzero size. |
- if (amount_to_discard) { |
+ if (current_frame_type_ == DATA && amount_to_discard > 0) { |
+ // The visitor needs to know about padding so it can send window updates. |
+ // Communicate the padding to the visitor through a NULL data pointer, |
+ // with a nonzero size. |
visitor_->OnStreamFrameData( |
current_frame_stream_id_, NULL, amount_to_discard, false); |
} |
@@ -2109,12 +2131,14 @@ size_t SpdyFramer::ProcessFramePadding(const char* data, size_t len) { |
} |
if (remaining_data_length_ == 0) { |
- // If the FIN flag is set, and there is no more data in this data frame, |
+ // If the FIN flag is set, or this ends a header block which set FIN, |
// inform the visitor of EOF via a 0-length data frame. |
- if (current_frame_flags_ & DATA_FLAG_FIN) { |
+ if (expect_continuation_ == 0 && |
+ ((current_frame_flags_ & CONTROL_FLAG_FIN) != 0 || |
+ end_stream_when_done_)) { |
+ end_stream_when_done_ = false; |
visitor_->OnStreamFrameData(current_frame_stream_id_, NULL, 0, true); |
} |
- |
CHANGE_STATE(SPDY_AUTO_RESET); |
} |
return original_len - len; |