Index: net/spdy/spdy_framer.cc |
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc |
index 1c0ee752ff3f54f14434aea940fdb287c99332b2..2d18e0c56b4dd444b57c65fc893d4383e439f463 100644 |
--- a/net/spdy/spdy_framer.cc |
+++ b/net/spdy/spdy_framer.cc |
@@ -161,6 +161,8 @@ void SpdyFramer::Reset() { |
current_frame_length_ = 0; |
current_frame_stream_id_ = kInvalidStream; |
settings_scratch_.Reset(); |
+ remaining_padding_payload_length_ = 0; |
+ remaining_padding_length_fields_ = 0; |
} |
size_t SpdyFramer::GetDataFrameMinimumSize() const { |
@@ -349,6 +351,10 @@ const char* SpdyFramer::StateToString(int state) { |
return "READING_COMMON_HEADER"; |
case SPDY_CONTROL_FRAME_PAYLOAD: |
return "CONTROL_FRAME_PAYLOAD"; |
+ case SPDY_READ_PADDING_LENGTH: |
+ return "SPDY_READ_PADDING_LENGTH"; |
+ case SPDY_CONSUME_PADDING: |
+ return "SPDY_CONSUME_PADDING"; |
case SPDY_IGNORE_REMAINING_PAYLOAD: |
return "IGNORE_REMAINING_PAYLOAD"; |
case SPDY_FORWARD_STREAM_FRAME: |
@@ -552,6 +558,20 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { |
break; |
} |
+ case SPDY_READ_PADDING_LENGTH: { |
+ size_t bytes_read = ProcessFramePaddingLength(data, len); |
+ len -= bytes_read; |
+ data += bytes_read; |
+ break; |
+ } |
+ |
+ case SPDY_CONSUME_PADDING: { |
+ size_t bytes_read = ProcessFramePadding(data, len); |
+ len -= bytes_read; |
+ data += bytes_read; |
+ break; |
+ } |
+ |
case SPDY_IGNORE_REMAINING_PAYLOAD: |
// control frame has too-large payload |
// intentional fallthrough |
@@ -705,14 +725,22 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { |
<< "DATA frame too large for SPDY >= 4."; |
} |
- if (current_frame_flags_ & ~DATA_FLAG_FIN) { |
+ uint8 valid_data_flags = 0; |
+ if (protocol_version() >= 4) { |
+ valid_data_flags = DATA_FLAG_FIN | DATA_FLAG_END_SEGMENT | |
+ DATA_FLAG_PAD_LOW | DATA_FLAG_PAD_HIGH; |
+ } else { |
+ valid_data_flags = DATA_FLAG_FIN; |
+ } |
+ |
+ if (current_frame_flags_ & ~valid_data_flags) { |
set_error(SPDY_INVALID_DATA_FRAME_FLAGS); |
} else { |
visitor_->OnDataFrameHeader(current_frame_stream_id_, |
remaining_data_length_, |
current_frame_flags_ & DATA_FLAG_FIN); |
if (remaining_data_length_ > 0) { |
- CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); |
+ CHANGE_STATE(SPDY_READ_PADDING_LENGTH); |
} else { |
// Empty data frame. |
if (current_frame_flags_ & DATA_FLAG_FIN) { |
@@ -1815,11 +1843,87 @@ size_t SpdyFramer::ProcessRstStreamFramePayload(const char* data, size_t len) { |
return original_len; |
} |
-size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { |
+size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) { |
+ DCHECK_EQ(SPDY_READ_PADDING_LENGTH, state_); |
+ |
+ size_t original_len = len; |
+ if (remaining_padding_length_fields_ == 0) { |
+ DCHECK_EQ(remaining_padding_payload_length_, 0u); |
+ bool pad_low = false; |
+ bool pad_high = false; |
+ if (current_frame_flags_ & net::DATA_FLAG_PAD_LOW) { |
+ pad_low = true; |
+ ++remaining_padding_length_fields_; |
+ } |
+ if (current_frame_flags_ & net::DATA_FLAG_PAD_HIGH) { |
+ pad_high = true; |
+ ++remaining_padding_length_fields_; |
+ } |
+ if ((pad_high && !pad_low) || |
+ remaining_data_length_ < remaining_padding_length_fields_) { |
+ set_error(SPDY_INVALID_DATA_FRAME_FLAGS); |
+ return 0; |
+ } |
+ } |
+ |
+ // Parse the padding length. |
+ while (len != 0 && remaining_padding_length_fields_ != 0) { |
+ remaining_padding_payload_length_ = |
+ (remaining_padding_payload_length_ << 8) + |
+ *reinterpret_cast<const uint8*>(data); |
+ ++data; |
+ --len; |
+ --remaining_padding_length_fields_; |
+ --remaining_data_length_; |
+ } |
+ |
+ if (remaining_padding_length_fields_ == 0) { |
+ if (remaining_padding_payload_length_ > remaining_data_length_) { |
+ set_error(SPDY_INVALID_DATA_FRAME_FLAGS); |
+ return 0; |
+ } |
+ CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); |
+ } |
+ return original_len - len; |
+} |
+ |
+size_t SpdyFramer::ProcessFramePadding(const char* data, size_t len) { |
+ DCHECK_EQ(SPDY_CONSUME_PADDING, state_); |
+ |
size_t original_len = 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) { |
+ visitor_->OnStreamFrameData( |
+ current_frame_stream_id_, NULL, amount_to_discard, false); |
+ } |
+ data += amount_to_discard; |
+ len -= amount_to_discard; |
+ remaining_padding_payload_length_ -= amount_to_discard; |
+ remaining_data_length_ -= amount_to_discard; |
- if (remaining_data_length_ > 0) { |
- size_t amount_to_forward = std::min(remaining_data_length_, len); |
+ // If the FIN flag is set, and there is no more data in this data |
+ // frame, inform the visitor of EOF via a 0-length data frame. |
+ if (!remaining_data_length_ && current_frame_flags_ & DATA_FLAG_FIN) { |
+ visitor_->OnStreamFrameData(current_frame_stream_id_, NULL, 0, true); |
+ } |
+ } |
+ |
+ if (remaining_data_length_ == 0) { |
+ CHANGE_STATE(SPDY_AUTO_RESET); |
+ } |
+ return original_len - len; |
+} |
+ |
+size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { |
+ size_t original_len = len; |
+ if (remaining_data_length_ - remaining_padding_payload_length_ > 0) { |
+ size_t amount_to_forward = std::min( |
+ remaining_data_length_ - remaining_padding_payload_length_, len); |
if (amount_to_forward && state_ != SPDY_IGNORE_REMAINING_PAYLOAD) { |
// Only inform the visitor if there is data. |
if (amount_to_forward) { |
@@ -1838,8 +1942,8 @@ size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { |
} |
} |
- if (remaining_data_length_ == 0) { |
- CHANGE_STATE(SPDY_AUTO_RESET); |
+ if (remaining_data_length_ == remaining_padding_payload_length_) { |
+ CHANGE_STATE(SPDY_CONSUME_PADDING); |
} |
return original_len - len; |
} |
@@ -1900,36 +2004,74 @@ size_t SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data, |
return reader.GetBytesConsumed(); |
} |
-SpdySerializedFrame* SpdyFramer::SerializeData(const SpdyDataIR& data) const { |
- const size_t kSize = GetDataFrameMinimumSize() + data.data().length(); |
- |
- SpdyDataFlags flags = DATA_FLAG_NONE; |
- if (data.fin()) { |
+SpdySerializedFrame* SpdyFramer::SerializeData(const SpdyDataIR& datair) const { |
+ uint8 flags = DATA_FLAG_NONE; |
+ if (datair.fin()) { |
flags = DATA_FLAG_FIN; |
} |
- SpdyFrameBuilder builder(kSize); |
- builder.WriteDataFrameHeader(*this, data.stream_id(), flags); |
- builder.WriteBytes(data.data().data(), data.data().length()); |
- DCHECK_EQ(kSize, builder.length()); |
- return builder.take(); |
+ if (protocol_version() >= 4) { |
+ int num_padding_fields = 0; |
+ if (datair.pad_low()) { |
+ flags |= DATA_FLAG_PAD_LOW; |
+ ++num_padding_fields; |
+ } |
+ if (datair.pad_high()) { |
+ flags |= DATA_FLAG_PAD_HIGH; |
+ ++num_padding_fields; |
+ } |
+ |
+ const size_t size_with_padding = num_padding_fields + |
+ datair.data().length() + datair.padding_payload_len() + |
+ GetDataFrameMinimumSize(); |
+ SpdyFrameBuilder builder(size_with_padding); |
+ builder.WriteDataFrameHeader(*this, datair.stream_id(), flags); |
+ if (datair.pad_high()) { |
+ builder.WriteUInt8(datair.padding_payload_len() >> 8); |
+ } |
+ if (datair.pad_low()) { |
+ builder.WriteUInt8(datair.padding_payload_len() & 0xff); |
+ } |
+ builder.WriteBytes(datair.data().data(), datair.data().length()); |
+ if (datair.padding_payload_len() > 0) { |
+ string padding = string(datair.padding_payload_len(), '0'); |
+ builder.WriteBytes(padding.data(), padding.length()); |
+ } |
+ DCHECK_EQ(size_with_padding, builder.length()); |
+ return builder.take(); |
+ } else { |
+ const size_t size = GetDataFrameMinimumSize() + datair.data().length(); |
+ SpdyFrameBuilder builder(size); |
+ builder.WriteDataFrameHeader(*this, datair.stream_id(), flags); |
+ builder.WriteBytes(datair.data().data(), datair.data().length()); |
+ DCHECK_EQ(size, builder.length()); |
+ return builder.take(); |
+ } |
} |
SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeader( |
const SpdyDataIR& data) const { |
const size_t kSize = GetDataFrameMinimumSize(); |
- SpdyDataFlags flags = DATA_FLAG_NONE; |
+ uint8 flags = DATA_FLAG_NONE; |
if (data.fin()) { |
flags = DATA_FLAG_FIN; |
} |
+ if (protocol_version() >= 4) { |
+ if (data.pad_low()) { |
+ flags |= DATA_FLAG_PAD_LOW; |
+ } |
+ if (data.pad_high()) { |
+ flags |= DATA_FLAG_PAD_HIGH; |
+ } |
+ } |
SpdyFrameBuilder builder(kSize); |
builder.WriteDataFrameHeader(*this, data.stream_id(), flags); |
- if (protocol_version() < 4) { |
- builder.OverwriteLength(*this, data.data().length()); |
- } else { |
+ if (protocol_version() >= 4) { |
builder.OverwriteLength(*this, data.data().length() + kSize); |
+ } else { |
+ builder.OverwriteLength(*this, data.data().length()); |
} |
DCHECK_EQ(kSize, builder.length()); |
return builder.take(); |