Index: net/spdy/spdy_framer.cc |
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc |
index 23080accd994ad2c6982d3e639fdaab2a1c28981..a20d1ead1f83b1c433bddd0ce9ffe2b2a29db05b 100644 |
--- a/net/spdy/spdy_framer.cc |
+++ b/net/spdy/spdy_framer.cc |
@@ -2,10 +2,6 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-// TODO(rtenhove) clean up frame buffer size calculations so that we aren't |
-// constantly adding and subtracting header sizes; this is ugly and error- |
-// prone. |
- |
#include "net/spdy/spdy_framer.h" |
#include "base/lazy_instance.h" |
@@ -80,9 +76,9 @@ const size_t SpdyFramer::kControlFrameBufferSize = 18; |
} while (false) |
#endif |
-SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat(int version, |
- uint32 wire) { |
- if (version < 3) { |
+SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat( |
+ SpdyMajorVersion version, uint32 wire) { |
+ if (version < SPDY3) { |
ConvertFlagsAndIdForSpdy2(&wire); |
} |
return SettingsFlagsAndId(ntohl(wire) >> 24, ntohl(wire) & 0x00ffffff); |
@@ -93,9 +89,10 @@ SettingsFlagsAndId::SettingsFlagsAndId(uint8 flags, uint32 id) |
LOG_IF(DFATAL, id > (1u << 24)) << "SPDY setting ID too large: " << id; |
} |
-uint32 SettingsFlagsAndId::GetWireFormat(int version) const { |
+uint32 SettingsFlagsAndId::GetWireFormat(SpdyMajorVersion version) |
+ const { |
uint32 wire = htonl(id_ & 0x00ffffff) | htonl(flags_ << 24); |
- if (version < 3) { |
+ if (version < SPDY3) { |
ConvertFlagsAndIdForSpdy2(&wire); |
} |
return wire; |
@@ -126,6 +123,7 @@ bool SpdyFramerVisitorInterface::OnRstStreamFrameData( |
SpdyFramer::SpdyFramer(SpdyMajorVersion version) |
: current_frame_buffer_(new char[kControlFrameBufferSize]), |
enable_compression_(true), |
+ hpack_encoder_(ObtainHpackHuffmanTable()), |
hpack_decoder_(ObtainHpackHuffmanTable()), |
visitor_(NULL), |
debug_visitor_(NULL), |
@@ -166,22 +164,12 @@ void SpdyFramer::Reset() { |
} |
size_t SpdyFramer::GetDataFrameMinimumSize() const { |
- // Size, in bytes, of the data frame header. Future versions of SPDY |
- // will likely vary this, so we allow for the flexibility of a function call |
- // for this value as opposed to a constant. |
- return 8; |
+ return SpdyConstants::GetDataFrameMinimumSize(); |
} |
// Size, in bytes, of the control frame header. |
size_t SpdyFramer::GetControlFrameHeaderSize() const { |
- switch (protocol_version()) { |
- case SPDY2: |
- case SPDY3: |
- case SPDY4: |
- return 8; |
- } |
- LOG(DFATAL) << "Unhandled SPDY version."; |
- return 0; |
+ return SpdyConstants::GetControlFrameHeaderSize(protocol_version()); |
} |
size_t SpdyFramer::GetSynStreamMinimumSize() const { |
@@ -263,7 +251,7 @@ size_t SpdyFramer::GetGoAwayMinimumSize() const { |
size += 4; |
// 3. SPDY 3+ GOAWAY frames also contain a status (4 bytes) |
- if (protocol_version() >= 3) { |
+ if (protocol_version() >= SPDY3) { |
size += 4; |
} |
@@ -302,14 +290,14 @@ size_t SpdyFramer::GetWindowUpdateSize() const { |
} |
size_t SpdyFramer::GetBlockedSize() const { |
- DCHECK_LE(4, protocol_version()); |
+ DCHECK_LT(SPDY3, protocol_version()); |
// Size, in bytes, of a BLOCKED frame. |
// The BLOCKED frame has no payload beyond the control frame header. |
return GetControlFrameHeaderSize(); |
} |
size_t SpdyFramer::GetPushPromiseMinimumSize() const { |
- DCHECK_LE(4, protocol_version()); |
+ DCHECK_LT(SPDY3, protocol_version()); |
// Size, in bytes, of a PUSH_PROMISE frame, sans the embedded header block. |
// Calculated as frame prefix + 4 (promised stream id). |
return GetControlFrameHeaderSize() + 4; |
@@ -326,19 +314,17 @@ size_t SpdyFramer::GetFrameMinimumSize() const { |
} |
size_t SpdyFramer::GetFrameMaximumSize() const { |
- if (protocol_version() <= SPDY3) { |
- // 24-bit length field plus eight-byte frame header. |
- return ((1<<24) - 1) + 8; |
- } else { |
- // 14-bit length field. |
- return (1<<14) - 1; |
- } |
+ return SpdyConstants::GetFrameMaximumSize(protocol_version()); |
} |
size_t SpdyFramer::GetDataFrameMaximumPayload() const { |
return GetFrameMaximumSize() - GetDataFrameMinimumSize(); |
} |
+size_t SpdyFramer::GetPrefixLength(SpdyFrameType type) const { |
+ return SpdyConstants::GetPrefixLength(type, protocol_version()); |
+} |
+ |
const char* SpdyFramer::StateToString(int state) { |
switch (state) { |
case SPDY_ERROR: |
@@ -438,6 +424,10 @@ const char* SpdyFramer::StatusCodeToString(int status_code) { |
return "INVALID_CREDENTIALS"; |
case RST_STREAM_FRAME_TOO_LARGE: |
return "FRAME_TOO_LARGE"; |
+ case RST_STREAM_CONNECT_ERROR: |
+ return "CONNECT_ERROR"; |
+ case RST_STREAM_ENHANCE_YOUR_CALM: |
+ return "ENHANCE_YOUR_CALM"; |
} |
return "UNKNOWN_STATUS"; |
} |
@@ -572,15 +562,20 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { |
break; |
} |
- case SPDY_IGNORE_REMAINING_PAYLOAD: |
- // control frame has too-large payload |
- // intentional fallthrough |
+ case SPDY_IGNORE_REMAINING_PAYLOAD: { |
+ size_t bytes_read = ProcessIgnoredControlFramePayload(/*data,*/ len); |
+ len -= bytes_read; |
+ data += bytes_read; |
+ break; |
+ } |
+ |
case SPDY_FORWARD_STREAM_FRAME: { |
size_t bytes_read = ProcessDataFramePayload(data, len); |
len -= bytes_read; |
data += bytes_read; |
break; |
} |
+ |
default: |
LOG(DFATAL) << "Invalid value for " << display_protocol_ |
<< " framer state: " << state_; |
@@ -639,8 +634,22 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { |
DCHECK(successful_read); |
is_control_frame = (version & kControlFlagMask) != 0; |
version &= ~kControlFlagMask; // Only valid for control frames. |
- |
if (is_control_frame) { |
+ // We check version before we check validity: version can never be |
+ // 'invalid', it can only be unsupported. |
+ if (version < SpdyConstants::SerializeMajorVersion(SPDY_MIN_VERSION) || |
+ version > SpdyConstants::SerializeMajorVersion(SPDY_MAX_VERSION) || |
+ SpdyConstants::ParseMajorVersion(version) != protocol_version()) { |
+ // Version does not match the version the framer was initialized with. |
+ DVLOG(1) << "Unsupported SPDY version " |
+ << version |
+ << " (expected " << protocol_version() << ")"; |
+ set_error(SPDY_UNSUPPORTED_VERSION); |
+ return 0; |
+ } else { |
+ // Convert version from wire format to SpdyMajorVersion. |
+ version = SpdyConstants::ParseMajorVersion(version); |
+ } |
// We check control_frame_type_field's validity in |
// ProcessControlFrameHeader(). |
successful_read = reader->ReadUInt16(&control_frame_type_field); |
@@ -756,12 +765,6 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { |
CHANGE_STATE(SPDY_AUTO_RESET); |
} |
} |
- } else if (version != protocol_version()) { |
- // We check version before we check validity: version can never be |
- // 'invalid', it can only be unsupported. |
- DVLOG(1) << "Unsupported SPDY version " << version |
- << " (expected " << protocol_version() << ")"; |
- set_error(SPDY_UNSUPPORTED_VERSION); |
} else { |
ProcessControlFrameHeader(control_frame_type_field); |
} |
@@ -927,7 +930,8 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
} |
break; |
case CONTINUATION: |
- if (current_frame_length_ < GetContinuationMinimumSize()) { |
+ if (current_frame_length_ < GetContinuationMinimumSize() || |
+ protocol_version() <= SPDY3) { |
set_error(SPDY_INVALID_CONTROL_FRAME); |
} else if (current_frame_flags_ & ~HEADERS_FLAG_END_HEADERS) { |
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
@@ -1043,10 +1047,11 @@ size_t SpdyFramer::UpdateCurrentFrameBuffer(const char** data, size_t* len, |
return bytes_to_read; |
} |
-size_t SpdyFramer::GetSerializedLength(const int spdy_version, |
- const SpdyHeaderBlock* headers) { |
+size_t SpdyFramer::GetSerializedLength( |
+ const SpdyMajorVersion spdy_version, |
+ const SpdyHeaderBlock* headers) { |
const size_t num_name_value_pairs_size |
- = (spdy_version < 3) ? sizeof(uint16) : sizeof(uint32); |
+ = (spdy_version < SPDY3) ? sizeof(uint16) : sizeof(uint32); |
const size_t length_of_name_size = num_name_value_pairs_size; |
const size_t length_of_value_size = num_name_value_pairs_size; |
@@ -1063,16 +1068,16 @@ size_t SpdyFramer::GetSerializedLength(const int spdy_version, |
} |
void SpdyFramer::WriteHeaderBlock(SpdyFrameBuilder* frame, |
- const int spdy_version, |
+ const SpdyMajorVersion spdy_version, |
const SpdyHeaderBlock* headers) { |
- if (spdy_version < 3) { |
+ if (spdy_version < SPDY3) { |
frame->WriteUInt16(headers->size()); // Number of headers. |
} else { |
frame->WriteUInt32(headers->size()); // Number of headers. |
} |
SpdyHeaderBlock::const_iterator it; |
for (it = headers->begin(); it != headers->end(); ++it) { |
- if (spdy_version < 3) { |
+ if (spdy_version < SPDY3) { |
frame->WriteString(it->first); |
frame->WriteString(it->second); |
} else { |
@@ -1597,7 +1602,8 @@ void SpdyFramer::DeliverHpackBlockAsSpdy3Block() { |
return; |
} |
SpdyFrameBuilder builder( |
- GetSerializedLength(protocol_version(), &block)); |
+ GetSerializedLength(protocol_version(), &block), |
+ SPDY3); |
SerializeNameValueBlockWithoutCompression(&builder, block); |
scoped_ptr<SpdyFrame> frame(builder.take()); |
@@ -1750,15 +1756,19 @@ size_t SpdyFramer::ProcessGoAwayFramePayload(const char* data, size_t len) { |
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); |
+ if (SpdyConstants::IsValidGoAwayStatus(protocol_version(), |
+ status_raw)) { |
+ status = SpdyConstants::ParseGoAwayStatus(protocol_version(), |
+ 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); |
+ // 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; |
+ } |
} |
} |
// Finished parsing the GOAWAY header, call frame handler. |
@@ -1816,15 +1826,17 @@ size_t SpdyFramer::ProcessRstStreamFramePayload(const char* data, size_t len) { |
uint32 status_raw = status; |
bool 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 > RST_STREAM_INVALID && |
- status_raw < RST_STREAM_NUM_STATUS_CODES) { |
+ if (SpdyConstants::IsValidRstStreamStatus(protocol_version(), |
+ status_raw)) { |
status = static_cast<SpdyRstStreamStatus>(status_raw); |
} else { |
- // TODO(hkhalil): Probably best to OnError here, depending on |
- // our interpretation of the spec. Keeping with existing liberal |
- // behavior for now. |
+ // 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; |
+ } |
} |
// Finished parsing the RST_STREAM header, call frame handler. |
visitor_->OnRstStream(current_frame_stream_id_, status); |
@@ -1855,11 +1867,11 @@ size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) { |
DCHECK_EQ(remaining_padding_payload_length_, 0u); |
bool pad_low = false; |
bool pad_high = false; |
- if (current_frame_flags_ & net::DATA_FLAG_PAD_LOW) { |
+ if (current_frame_flags_ & DATA_FLAG_PAD_LOW) { |
pad_low = true; |
++remaining_padding_length_fields_; |
} |
- if (current_frame_flags_ & net::DATA_FLAG_PAD_HIGH) { |
+ if (current_frame_flags_ & DATA_FLAG_PAD_HIGH) { |
pad_high = true; |
++remaining_padding_length_fields_; |
} |
@@ -1909,15 +1921,15 @@ size_t SpdyFramer::ProcessFramePadding(const char* data, size_t len) { |
len -= amount_to_discard; |
remaining_padding_payload_length_ -= amount_to_discard; |
remaining_data_length_ -= amount_to_discard; |
+ } |
- // 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) { |
+ if (remaining_data_length_ == 0) { |
+ // 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 (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; |
@@ -1938,12 +1950,6 @@ size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { |
data += amount_to_forward; |
len -= amount_to_forward; |
remaining_data_length_ -= amount_to_forward; |
- |
- // 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_ == remaining_padding_payload_length_) { |
@@ -1952,6 +1958,21 @@ size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { |
return original_len - len; |
} |
+size_t SpdyFramer::ProcessIgnoredControlFramePayload(/*const char* data,*/ |
+ size_t len) { |
+ size_t original_len = len; |
+ if (remaining_data_length_ > 0) { |
+ size_t amount_to_ignore = std::min(remaining_data_length_, len); |
+ len -= amount_to_ignore; |
+ remaining_data_length_ -= amount_to_ignore; |
+ } |
+ |
+ if (remaining_data_length_ == 0) { |
+ CHANGE_STATE(SPDY_AUTO_RESET); |
+ } |
+ return original_len - len; |
+} |
+ |
size_t SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data, |
size_t header_length, |
SpdyHeaderBlock* block) const { |
@@ -2008,72 +2029,88 @@ size_t SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data, |
return reader.GetBytesConsumed(); |
} |
-SpdySerializedFrame* SpdyFramer::SerializeData(const SpdyDataIR& datair) const { |
+SpdySerializedFrame* SpdyFramer::SerializeData( |
+ const SpdyDataIR& data_ir) const { |
uint8 flags = DATA_FLAG_NONE; |
- if (datair.fin()) { |
+ if (data_ir.fin()) { |
flags = DATA_FLAG_FIN; |
} |
if (protocol_version() > SPDY3) { |
int num_padding_fields = 0; |
- if (datair.pad_low()) { |
+ if (data_ir.pad_low()) { |
flags |= DATA_FLAG_PAD_LOW; |
++num_padding_fields; |
} |
- if (datair.pad_high()) { |
+ if (data_ir.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() + |
+ data_ir.data().length() + data_ir.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); |
+ SpdyFrameBuilder builder(size_with_padding, protocol_version()); |
+ builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags); |
+ if (data_ir.pad_high()) { |
+ builder.WriteUInt8(data_ir.padding_payload_len() >> 8); |
} |
- if (datair.pad_low()) { |
- builder.WriteUInt8(datair.padding_payload_len() & 0xff); |
+ if (data_ir.pad_low()) { |
+ builder.WriteUInt8(data_ir.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(data_ir.data().data(), data_ir.data().length()); |
+ if (data_ir.padding_payload_len() > 0) { |
+ string padding = string(data_ir.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()); |
+ const size_t size = GetDataFrameMinimumSize() + data_ir.data().length(); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
+ builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags); |
+ builder.WriteBytes(data_ir.data().data(), data_ir.data().length()); |
DCHECK_EQ(size, builder.length()); |
return builder.take(); |
} |
} |
-SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeader( |
- const SpdyDataIR& data) const { |
- const size_t kSize = GetDataFrameMinimumSize(); |
- |
+SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField( |
+ const SpdyDataIR& data_ir) const { |
uint8 flags = DATA_FLAG_NONE; |
- if (data.fin()) { |
+ if (data_ir.fin()) { |
flags = DATA_FLAG_FIN; |
} |
+ |
+ size_t frame_size = GetDataFrameMinimumSize(); |
+ size_t num_padding_fields = 0; |
if (protocol_version() > SPDY3) { |
- if (data.pad_low()) { |
+ if (data_ir.pad_low()) { |
flags |= DATA_FLAG_PAD_LOW; |
+ ++num_padding_fields; |
} |
- if (data.pad_high()) { |
+ if (data_ir.pad_high()) { |
flags |= DATA_FLAG_PAD_HIGH; |
+ ++num_padding_fields; |
} |
+ frame_size += num_padding_fields; |
} |
- SpdyFrameBuilder builder(kSize); |
- builder.WriteDataFrameHeader(*this, data.stream_id(), flags); |
- builder.OverwriteLength(*this, data.data().length()); |
- DCHECK_EQ(kSize, builder.length()); |
+ SpdyFrameBuilder builder(frame_size, protocol_version()); |
+ builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags); |
+ if (protocol_version() > SPDY3) { |
+ if (data_ir.pad_high()) { |
+ builder.WriteUInt8(data_ir.padding_payload_len() >> 8); |
+ } |
+ if (data_ir.pad_low()) { |
+ builder.WriteUInt8(data_ir.padding_payload_len() & 0xff); |
+ } |
+ builder.OverwriteLength(*this, num_padding_fields + |
+ data_ir.data().length() + data_ir.padding_payload_len()); |
+ } else { |
+ builder.OverwriteLength(*this, data_ir.data().length()); |
+ } |
+ DCHECK_EQ(frame_size, builder.length()); |
return builder.take(); |
} |
@@ -2106,14 +2143,19 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream( |
string hpack_encoding; |
if (protocol_version() > SPDY3) { |
- hpack_encoder_.EncodeHeaderSet(syn_stream.name_value_block(), |
- &hpack_encoding); |
+ if (enable_compression_) { |
+ hpack_encoder_.EncodeHeaderSet( |
+ syn_stream.name_value_block(), &hpack_encoding); |
+ } else { |
+ hpack_encoder_.EncodeHeaderSetWithoutCompression( |
+ syn_stream.name_value_block(), &hpack_encoding); |
+ } |
size += hpack_encoding.size(); |
} else { |
size += GetSerializedLength(syn_stream.name_value_block()); |
} |
- SpdyFrameBuilder builder(size); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, SYN_STREAM, flags); |
builder.WriteUInt32(syn_stream.stream_id()); |
@@ -2121,10 +2163,10 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream( |
builder.WriteUInt8(priority << ((protocol_version() <= SPDY2) ? 6 : 5)); |
builder.WriteUInt8(0); // Unused byte where credential slot used to be. |
} else { |
- builder.WriteFramePrefix(*this, |
- HEADERS, |
- flags, |
- syn_stream.stream_id()); |
+ builder.BeginNewFrame(*this, |
+ HEADERS, |
+ flags, |
+ syn_stream.stream_id()); |
builder.WriteUInt32(priority); |
} |
DCHECK_EQ(GetSynStreamMinimumSize(), builder.length()); |
@@ -2166,22 +2208,27 @@ SpdySerializedFrame* SpdyFramer::SerializeSynReply( |
string hpack_encoding; |
if (protocol_version() > SPDY3) { |
- hpack_encoder_.EncodeHeaderSet(syn_reply.name_value_block(), |
- &hpack_encoding); |
+ if (enable_compression_) { |
+ hpack_encoder_.EncodeHeaderSet( |
+ syn_reply.name_value_block(), &hpack_encoding); |
+ } else { |
+ hpack_encoder_.EncodeHeaderSetWithoutCompression( |
+ syn_reply.name_value_block(), &hpack_encoding); |
+ } |
size += hpack_encoding.size(); |
} else { |
size += GetSerializedLength(syn_reply.name_value_block()); |
} |
- SpdyFrameBuilder builder(size); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, SYN_REPLY, flags); |
builder.WriteUInt32(syn_reply.stream_id()); |
} else { |
- builder.WriteFramePrefix(*this, |
- HEADERS, |
- flags, |
- syn_reply.stream_id()); |
+ builder.BeginNewFrame(*this, |
+ HEADERS, |
+ flags, |
+ syn_reply.stream_id()); |
} |
if (protocol_version() < SPDY3) { |
builder.WriteUInt16(0); // Unused. |
@@ -2218,14 +2265,14 @@ SpdySerializedFrame* SpdyFramer::SerializeRstStream( |
if (protocol_version() > SPDY3) { |
expected_length += rst_stream.description().size(); |
} |
- SpdyFrameBuilder builder(expected_length); |
+ SpdyFrameBuilder builder(expected_length, protocol_version()); |
// Serialize the RST_STREAM frame. |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, RST_STREAM, 0); |
builder.WriteUInt32(rst_stream.stream_id()); |
} else { |
- builder.WriteFramePrefix(*this, RST_STREAM, 0, rst_stream.stream_id()); |
+ builder.BeginNewFrame(*this, RST_STREAM, 0, rst_stream.stream_id()); |
} |
builder.WriteUInt32(rst_stream.status()); |
@@ -2259,11 +2306,11 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings( |
// Size, in bytes, of this SETTINGS frame. |
const size_t size = GetSettingsMinimumSize() + |
(values->size() * setting_size); |
- SpdyFrameBuilder builder(size); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, SETTINGS, flags); |
} else { |
- builder.WriteFramePrefix(*this, SETTINGS, flags, 0); |
+ builder.BeginNewFrame(*this, SETTINGS, flags, 0); |
} |
// If this is an ACK, payload should be empty. |
@@ -2301,15 +2348,8 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings( |
return builder.take(); |
} |
-SpdyFrame* SpdyFramer::SerializeBlocked(const SpdyBlockedIR& blocked) const { |
- DCHECK_LT(SPDY3, protocol_version()); |
- SpdyFrameBuilder builder(GetBlockedSize()); |
- builder.WriteFramePrefix(*this, BLOCKED, kNoFlags, blocked.stream_id()); |
- return builder.take(); |
-} |
- |
SpdySerializedFrame* SpdyFramer::SerializePing(const SpdyPingIR& ping) const { |
- SpdyFrameBuilder builder(GetPingSize()); |
+ SpdyFrameBuilder builder(GetPingSize(), protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, PING, kNoFlags); |
builder.WriteUInt32(static_cast<uint32>(ping.id())); |
@@ -2318,7 +2358,7 @@ SpdySerializedFrame* SpdyFramer::SerializePing(const SpdyPingIR& ping) const { |
if (ping.is_ack()) { |
flags |= PING_FLAG_ACK; |
} |
- builder.WriteFramePrefix(*this, PING, flags, 0); |
+ builder.BeginNewFrame(*this, PING, flags, 0); |
builder.WriteUInt64(ping.id()); |
} |
DCHECK_EQ(GetPingSize(), builder.length()); |
@@ -2333,13 +2373,13 @@ SpdySerializedFrame* SpdyFramer::SerializeGoAway( |
if (protocol_version() > SPDY3) { |
expected_length += goaway.description().size(); |
} |
- SpdyFrameBuilder builder(expected_length); |
+ SpdyFrameBuilder builder(expected_length, protocol_version()); |
// Serialize the GOAWAY frame. |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, GOAWAY, kNoFlags); |
} else { |
- builder.WriteFramePrefix(*this, GOAWAY, 0, 0); |
+ builder.BeginNewFrame(*this, GOAWAY, 0, 0); |
} |
// GOAWAY frames specify the last good stream id for all SPDY versions. |
@@ -2367,6 +2407,9 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( |
flags |= CONTROL_FLAG_FIN; |
} |
if (protocol_version() > SPDY3) { |
+ // TODO(mlavan): If we overflow into a CONTINUATION frame, this will |
+ // get overwritten below, so we should probably just get rid of the |
+ // end_headers field. |
if (headers.end_headers()) { |
flags |= HEADERS_FLAG_END_HEADERS; |
} |
@@ -2389,21 +2432,32 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( |
string hpack_encoding; |
if (protocol_version() > SPDY3) { |
- hpack_encoder_.EncodeHeaderSet(headers.name_value_block(), &hpack_encoding); |
+ if (enable_compression_) { |
+ hpack_encoder_.EncodeHeaderSet( |
+ headers.name_value_block(), &hpack_encoding); |
+ } else { |
+ hpack_encoder_.EncodeHeaderSetWithoutCompression( |
+ headers.name_value_block(), &hpack_encoding); |
+ } |
size += hpack_encoding.size(); |
+ if (size > GetControlFrameBufferMaxSize()) { |
+ size += GetNumberRequiredContinuationFrames(size) * |
+ GetContinuationMinimumSize(); |
+ flags &= ~HEADERS_FLAG_END_HEADERS; |
+ } |
} else { |
size += GetSerializedLength(headers.name_value_block()); |
} |
- SpdyFrameBuilder builder(size); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, HEADERS, flags); |
builder.WriteUInt32(headers.stream_id()); |
} else { |
- builder.WriteFramePrefix(*this, |
- HEADERS, |
- flags, |
- headers.stream_id()); |
+ builder.BeginNewFrame(*this, |
+ HEADERS, |
+ flags, |
+ headers.stream_id()); |
if (headers.has_priority()) { |
builder.WriteUInt32(priority); |
} |
@@ -2414,7 +2468,10 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( |
DCHECK_EQ(GetHeadersMinimumSize(), builder.length()); |
if (protocol_version() > SPDY3) { |
- builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size()); |
+ WritePayloadWithContinuation(&builder, |
+ hpack_encoding, |
+ headers.stream_id(), |
+ HEADERS); |
} else { |
SerializeNameValueBlock(&builder, headers); |
} |
@@ -2435,25 +2492,35 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( |
SpdySerializedFrame* SpdyFramer::SerializeWindowUpdate( |
const SpdyWindowUpdateIR& window_update) const { |
- SpdyFrameBuilder builder(GetWindowUpdateSize()); |
+ SpdyFrameBuilder builder(GetWindowUpdateSize(), protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, WINDOW_UPDATE, kNoFlags); |
builder.WriteUInt32(window_update.stream_id()); |
} else { |
- builder.WriteFramePrefix(*this, |
- WINDOW_UPDATE, |
- kNoFlags, |
- window_update.stream_id()); |
+ builder.BeginNewFrame(*this, |
+ WINDOW_UPDATE, |
+ kNoFlags, |
+ window_update.stream_id()); |
} |
builder.WriteUInt32(window_update.delta()); |
DCHECK_EQ(GetWindowUpdateSize(), builder.length()); |
return builder.take(); |
} |
+SpdyFrame* SpdyFramer::SerializeBlocked(const SpdyBlockedIR& blocked) const { |
+ DCHECK_LT(SPDY3, protocol_version()); |
+ SpdyFrameBuilder builder(GetBlockedSize(), protocol_version()); |
+ builder.BeginNewFrame(*this, BLOCKED, kNoFlags, blocked.stream_id()); |
+ return builder.take(); |
+} |
+ |
SpdyFrame* SpdyFramer::SerializePushPromise( |
const SpdyPushPromiseIR& push_promise) { |
DCHECK_LT(SPDY3, protocol_version()); |
uint8 flags = 0; |
+ // TODO(mlavan): If we overflow into a CONTINUATION frame, this will |
+ // get overwritten below, so we should probably just get rid of the |
+ // end_push_promise field. |
if (push_promise.end_push_promise()) { |
flags |= PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
} |
@@ -2462,21 +2529,36 @@ SpdyFrame* SpdyFramer::SerializePushPromise( |
string hpack_encoding; |
if (protocol_version() > SPDY3) { |
- hpack_encoder_.EncodeHeaderSet(push_promise.name_value_block(), |
- &hpack_encoding); |
+ if (enable_compression_) { |
+ hpack_encoder_.EncodeHeaderSet( |
+ push_promise.name_value_block(), &hpack_encoding); |
+ } else { |
+ hpack_encoder_.EncodeHeaderSetWithoutCompression( |
+ push_promise.name_value_block(), &hpack_encoding); |
+ } |
size += hpack_encoding.size(); |
+ if (size > GetControlFrameBufferMaxSize()) { |
+ size += GetNumberRequiredContinuationFrames(size) * |
+ GetContinuationMinimumSize(); |
+ flags &= ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
+ } |
} else { |
size += GetSerializedLength(push_promise.name_value_block()); |
} |
- SpdyFrameBuilder builder(size); |
- builder.WriteFramePrefix(*this, PUSH_PROMISE, flags, |
- push_promise.stream_id()); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
+ builder.BeginNewFrame(*this, |
+ PUSH_PROMISE, |
+ flags, |
+ push_promise.stream_id()); |
builder.WriteUInt32(push_promise.promised_stream_id()); |
DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length()); |
if (protocol_version() > SPDY3) { |
- builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size()); |
+ WritePayloadWithContinuation(&builder, |
+ hpack_encoding, |
+ push_promise.stream_id(), |
+ PUSH_PROMISE); |
} else { |
SerializeNameValueBlock(&builder, push_promise); |
} |
@@ -2507,12 +2589,17 @@ SpdyFrame* SpdyFramer::SerializeContinuation( |
// The size of this frame, including variable-length name-value block. |
size_t size = GetContinuationMinimumSize(); |
string hpack_encoding; |
- hpack_encoder_.EncodeHeaderSet(continuation.name_value_block(), |
- &hpack_encoding); |
+ if (enable_compression_) { |
+ hpack_encoder_.EncodeHeaderSet( |
+ continuation.name_value_block(), &hpack_encoding); |
+ } else { |
+ hpack_encoder_.EncodeHeaderSetWithoutCompression( |
+ continuation.name_value_block(), &hpack_encoding); |
+ } |
size += hpack_encoding.size(); |
- SpdyFrameBuilder builder(size); |
- builder.WriteFramePrefix(*this, CONTINUATION, flags, |
+ SpdyFrameBuilder builder(size, protocol_version()); |
+ builder.BeginNewFrame(*this, CONTINUATION, flags, |
continuation.stream_id()); |
DCHECK_EQ(GetContinuationMinimumSize(), builder.length()); |
@@ -2602,6 +2689,69 @@ size_t SpdyFramer::GetSerializedLength(const SpdyHeaderBlock& headers) { |
return 2 * deflateBound(compressor, uncompressed_length); |
} |
+size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) { |
+ const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize(); |
+ DCHECK_GT(protocol_version(), SPDY3); |
+ DCHECK_GT(size, kMaxControlFrameSize); |
+ size_t overflow = size - kMaxControlFrameSize; |
+ return overflow / (kMaxControlFrameSize - GetContinuationMinimumSize()) + 1; |
+} |
+ |
+void SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder, |
+ const string& hpack_encoding, |
+ SpdyStreamId stream_id, |
+ SpdyFrameType type) { |
+ const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize(); |
+ |
+ // In addition to the prefix, fixed_field_size includes the size of |
+ // any fields that come before the variable-length name/value block. |
+ size_t fixed_field_size = 0; |
+ uint8 end_flag = 0; |
+ uint8 flags = 0; |
+ if (type == HEADERS) { |
+ fixed_field_size = GetHeadersMinimumSize(); |
+ end_flag = HEADERS_FLAG_END_HEADERS; |
+ } else if (type == PUSH_PROMISE) { |
+ fixed_field_size = GetPushPromiseMinimumSize(); |
+ end_flag = PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
+ } else { |
+ DLOG(FATAL) << "CONTINUATION frames cannot be used with frame type " |
+ << FrameTypeToString(type); |
+ } |
+ |
+ // Write as much of the payload as possible into the initial frame. |
+ size_t bytes_remaining = hpack_encoding.size() - |
+ std::min(hpack_encoding.size(), |
+ kMaxControlFrameSize - fixed_field_size); |
+ builder->WriteBytes(&hpack_encoding[0], |
+ hpack_encoding.size() - bytes_remaining); |
+ |
+ if (bytes_remaining > 0) { |
+ builder->OverwriteLength(*this, |
+ kMaxControlFrameSize - GetControlFrameHeaderSize()); |
+ } |
+ |
+ // Tack on CONTINUATION frames for the overflow. |
+ while (bytes_remaining > 0) { |
+ size_t bytes_to_write = std::min(bytes_remaining, |
+ kMaxControlFrameSize - |
+ GetContinuationMinimumSize()); |
+ // Write CONTINUATION frame prefix. |
+ if (bytes_remaining == bytes_to_write) { |
+ flags |= end_flag; |
+ } |
+ builder->BeginNewFrame(*this, |
+ CONTINUATION, |
+ flags, |
+ stream_id); |
+ // Write payload fragment. |
+ builder->WriteBytes(&hpack_encoding[hpack_encoding.size() - |
+ bytes_remaining], |
+ bytes_to_write); |
+ bytes_remaining -= bytes_to_write; |
+ } |
+} |
+ |
// The following compression setting are based on Brian Olson's analysis. See |
// https://groups.google.com/group/spdy-dev/browse_thread/thread/dfaf498542fac792 |
// for more details. |
@@ -2789,7 +2939,7 @@ void SpdyFramer::SerializeNameValueBlock( |
// First build an uncompressed version to be fed into the compressor. |
const size_t uncompressed_len = GetSerializedLength( |
protocol_version(), &(frame.name_value_block())); |
- SpdyFrameBuilder uncompressed_builder(uncompressed_len); |
+ SpdyFrameBuilder uncompressed_builder(uncompressed_len, protocol_version()); |
SerializeNameValueBlockWithoutCompression(&uncompressed_builder, |
frame.name_value_block()); |
scoped_ptr<SpdyFrame> uncompressed_payload(uncompressed_builder.take()); |