Index: net/spdy/spdy_framer.cc |
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc |
index 89a8309ea44c22aa0d19e927f8551b0106fdcb3e..f48a46c307c813676e169d281ea311be581c1c13 100644 |
--- a/net/spdy/spdy_framer.cc |
+++ b/net/spdy/spdy_framer.cc |
@@ -171,17 +171,15 @@ size_t SpdyFramer::GetControlFrameHeaderSize() const { |
} |
size_t SpdyFramer::GetSynStreamMinimumSize() const { |
+ if (spdy_version_ >= SPDY4) |
+ return GetSynReplyMinimumSize(); |
+ |
+ DCHECK_LT(spdy_version_, SPDY4); |
// Size, in bytes, of a SYN_STREAM frame not including the variable-length |
// name-value block. |
- if (spdy_version_ < 4) { |
- // Calculated as: |
- // control frame header + 2 * 4 (stream IDs) + 1 (priority) + 1 (slot) |
- return GetControlFrameHeaderSize() + 10; |
- } else { |
- // Calculated as: |
- // frame prefix + 4 (associated stream ID) + 1 (priority) + 1 (slot) |
- return GetControlFrameHeaderSize() + 6; |
- } |
+ // Calculated as: |
+ // control frame header + 2 * 4 (stream IDs) + 1 (priority) + 1 (slot) |
+ return GetControlFrameHeaderSize() + 10; |
} |
size_t SpdyFramer::GetSynReplyMinimumSize() const { |
@@ -216,16 +214,24 @@ size_t SpdyFramer::GetRstStreamSize() const { |
} |
size_t SpdyFramer::GetSettingsMinimumSize() const { |
- // Size, in bytes, of a SETTINGS frame not including the IDs and values |
- // from the variable-length value block. Calculated as: |
- // control frame header + 4 (number of ID/value pairs) |
- return GetControlFrameHeaderSize() + 4; |
+ if (spdy_version_ < 4) { |
+ // Size, in bytes, of a SETTINGS frame not including the IDs and values |
+ // from the variable-length value block. Calculated as: |
+ // control frame header + 4 (number of ID/value pairs) |
+ return GetControlFrameHeaderSize() + 4; |
+ } else { |
+ return GetControlFrameHeaderSize(); |
+ } |
} |
size_t SpdyFramer::GetPingSize() const { |
// Size, in bytes, of this PING frame. Calculated as: |
- // control frame header + 4 (id) |
- return GetControlFrameHeaderSize() + 4; |
+ // control frame header + 4 (id) (or 8 for SPDY4) |
+ if (spdy_version_ < SPDY4) { |
+ return GetControlFrameHeaderSize() + 4; |
+ } else { |
+ return GetControlFrameHeaderSize() + 8; |
+ } |
} |
size_t SpdyFramer::GetGoAwaySize() const { |
@@ -591,7 +597,6 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { |
uint16 length_field = 0; |
bool successful_read = reader->ReadUInt16(&length_field); |
DCHECK(successful_read); |
- current_frame_length_ = length_field; |
uint8 control_frame_type_field_uint8 = DATA; |
successful_read = reader->ReadUInt8(&control_frame_type_field_uint8); |
@@ -607,7 +612,8 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { |
successful_read = reader->ReadUInt31(¤t_frame_stream_id_); |
DCHECK(successful_read); |
- remaining_data_length_ = current_frame_length_ - reader->GetBytesConsumed(); |
+ remaining_data_length_ = length_field; |
+ current_frame_length_ = remaining_data_length_ + reader->GetBytesConsumed(); |
} |
DCHECK_EQ(is_control_frame ? GetControlFrameHeaderSize() |
: GetDataFrameMinimumSize(), |
@@ -674,6 +680,10 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
current_frame_type_ = static_cast<SpdyFrameType>(control_frame_type_field); |
+ if (spdy_version_ >= SPDY4 && current_frame_type_ == SYN_STREAM) { |
+ current_frame_type_ = HEADERS; |
+ } |
+ |
if (current_frame_type_ == NOOP) { |
DLOG(INFO) << "NOOP control frame found. Ignoring."; |
CHANGE_STATE(SPDY_AUTO_RESET); |
@@ -685,7 +695,7 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
case SYN_STREAM: |
if (current_frame_length_ < GetSynStreamMinimumSize()) { |
set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ & |
+ } else if (spdy_version_ < SPDY4 && current_frame_flags_ & |
~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) { |
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
} |
@@ -693,7 +703,8 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
case SYN_REPLY: |
if (current_frame_length_ < GetSynReplyMinimumSize()) { |
set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ & ~CONTROL_FLAG_FIN) { |
+ } else if (spdy_version_ < SPDY4 && current_frame_flags_ & |
+ ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) { |
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
} |
break; |
@@ -708,7 +719,9 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
// Make sure that we have an integral number of 8-byte key/value pairs, |
// plus a 4-byte length field. |
if (current_frame_length_ < GetSettingsMinimumSize() || |
- (current_frame_length_ - GetControlFrameHeaderSize()) % 8 != 4) { |
+ ((spdy_version_ < SPDY4) && |
+ ((current_frame_length_ - GetControlFrameHeaderSize()) % 8 != 4)) || |
+ ((spdy_version_ >= SPDY4) && (current_frame_length_ % 8 != 0))) { |
DLOG(WARNING) << "Invalid length for SETTINGS frame: " |
<< current_frame_length_; |
set_error(SPDY_INVALID_CONTROL_FRAME); |
@@ -720,8 +733,11 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
case PING: |
if (current_frame_length_ != GetPingSize()) { |
set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ != 0) { |
+ } else if (spdy_version_ < SPDY4 && current_frame_flags_ != 0) { |
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
+ } else if (spdy_version_ >= SPDY4 && (current_frame_flags_ & ~0x1) != 0) { |
+ // TODO(akalin): Unignore this. |
+ // set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
} |
break; |
case GOAWAY: |
@@ -734,10 +750,21 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
break; |
} |
case HEADERS: |
- if (current_frame_length_ < GetHeadersMinimumSize()) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ & ~CONTROL_FLAG_FIN) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
+ { |
+ size_t min_size = GetHeadersMinimumSize(); |
+ if (spdy_version_ > 3 && current_frame_flags_ & HEADERS_FLAG_PRIORITY) { |
+ min_size += 4; |
+ } |
+ if (current_frame_length_ < min_size) { |
+ set_error(SPDY_INVALID_CONTROL_FRAME); |
+ } else if (spdy_version_ < 4 && |
+ current_frame_flags_ & ~CONTROL_FLAG_FIN) { |
+ set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
+ } else if (spdy_version_ > 3 && current_frame_flags_ & |
+ ~(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY | |
+ HEADERS_FLAG_END_HEADERS)) { |
+ set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
+ } |
} |
break; |
case WINDOW_UPDATE: |
@@ -813,6 +840,9 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { |
break; |
case HEADERS: |
frame_size_without_variable_data = GetHeadersMinimumSize(); |
+ if (spdy_version_ > 3 && current_frame_flags_ & HEADERS_FLAG_PRIORITY) { |
+ frame_size_without_variable_data += 4; // priority |
+ } |
break; |
case PUSH_PROMISE: |
frame_size_without_variable_data = GetPushPromiseMinimumSize(); |
@@ -1075,7 +1105,7 @@ void SpdyFramer::WriteHeaderBlockToZ(const SpdyHeaderBlock* headers, |
size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, |
size_t len) { |
DCHECK_EQ(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, state_); |
- size_t original_len = len; |
+ const size_t original_len = len; |
if (remaining_control_header_ > 0) { |
size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, |
@@ -1092,6 +1122,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, |
switch (current_frame_type_) { |
case SYN_STREAM: |
{ |
+ DCHECK_GT(4, spdy_version_); |
bool successful_read = true; |
if (spdy_version_ < 4) { |
successful_read = reader.ReadUInt31(¤t_frame_stream_id_); |
@@ -1163,17 +1194,42 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, |
// SPDY 2 had two unused bytes here. Seek past them. |
reader.Seek(2); |
} |
+ const bool has_priority = |
+ (current_frame_flags_ & HEADERS_FLAG_PRIORITY) != 0; |
+ uint32 priority = 0; |
+ if (protocol_version() > 3 && has_priority) { |
+ successful_read = reader.ReadUInt31(&priority); |
+ DCHECK(successful_read); |
+ } |
DCHECK(reader.IsDoneReading()); |
if (debug_visitor_) { |
+ // SPDY 4 reports HEADERS with PRIORITY as SYN_STREAM. |
+ SpdyFrameType reported_type = current_frame_type_; |
+ if (protocol_version() > 3 && has_priority) { |
+ reported_type = SYN_STREAM; |
+ } |
debug_visitor_->OnReceiveCompressedFrame( |
current_frame_stream_id_, |
- current_frame_type_, |
+ reported_type, |
current_frame_length_); |
} |
- if (current_frame_type_ == SYN_REPLY) { |
+ // TODO(akalin): Fix this hack. |
+ if (current_frame_type_ == SYN_REPLY || |
+ (spdy_version_ >= SPDY4 && current_frame_type_ == HEADERS)) { |
visitor_->OnSynReply( |
current_frame_stream_id_, |
(current_frame_flags_ & CONTROL_FLAG_FIN) != 0); |
+ } else if (spdy_version_ > 3 && |
+ current_frame_flags_ & HEADERS_FLAG_PRIORITY) { |
+ // SPDY 4+ is missing SYN_STREAM. Simulate it so that API changes |
+ // can be made independent of wire changes. |
+ visitor_->OnSynStream( |
+ current_frame_stream_id_, |
+ 0, // associated_to_stream_id |
+ priority, |
+ 0, // TODO(hkhalil): handle slot for SPDY 4+? |
+ current_frame_flags_ & CONTROL_FLAG_FIN, |
+ false); // unidirectional |
} else { |
visitor_->OnHeaders( |
current_frame_stream_id_, |
@@ -1403,10 +1459,18 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { |
break; |
case PING: { |
SpdyPingId id = 0; |
- bool successful_read = reader.ReadUInt32(&id); |
- DCHECK(successful_read); |
- DCHECK(reader.IsDoneReading()); |
- visitor_->OnPing(id); |
+ uint32 zero_expected = 0x01; |
+ bool successful_read = |
+ reader.ReadUInt32(&id) && reader.ReadUInt32(&zero_expected); |
+ if (successful_read && reader.IsDoneReading() && zero_expected == 0) { |
+ visitor_->OnPing(id); |
+ } else { |
+ // Can't set_error() here, so just warn, I guess. But this |
+ // breaks spec. |
+ // |
+ // TODO(akalin): Fix this. |
+ LOG(WARNING) << "Dropping invalid ping frame"; |
+ } |
} |
break; |
case GOAWAY: { |
@@ -1662,33 +1726,44 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream( |
if (syn_stream.fin()) { |
flags |= CONTROL_FLAG_FIN; |
} |
- if (syn_stream.unidirectional()) { |
+ if (spdy_version_ <= SPDY4 && syn_stream.unidirectional()) { |
flags |= CONTROL_FLAG_UNIDIRECTIONAL; |
} |
+ if (spdy_version_ >= SPDY4) { |
+ flags |= HEADERS_FLAG_END_HEADERS; |
+ flags |= HEADERS_FLAG_PRIORITY; |
+ } |
+ |
+ // Sanitize priority. |
+ uint8 priority = syn_stream.priority(); |
+ if (priority > GetLowestPriority()) { |
+ DLOG(DFATAL) << "Priority out-of-bounds."; |
+ priority = GetLowestPriority(); |
+ } |
// The size of this frame, including variable-length name-value block. |
- const size_t size = GetSynStreamMinimumSize() |
+ size_t size = GetSynStreamMinimumSize() |
+ GetSerializedLength(syn_stream.name_value_block()); |
+ if (spdy_version_ >= SPDY4 && (flags & HEADERS_FLAG_PRIORITY)) { |
+ size += 4; |
+ } |
+ |
SpdyFrameBuilder builder(size); |
if (spdy_version_ < 4) { |
builder.WriteControlFrameHeader(*this, SYN_STREAM, flags); |
builder.WriteUInt32(syn_stream.stream_id()); |
+ builder.WriteUInt32(syn_stream.associated_to_stream_id()); |
+ builder.WriteUInt8(priority << ((spdy_version_ < 3) ? 6 : 5)); |
+ builder.WriteUInt8(syn_stream.slot()); |
+ DCHECK_EQ(GetSynStreamMinimumSize(), builder.length()); |
} else { |
builder.WriteFramePrefix(*this, |
- SYN_STREAM, |
+ HTTP2_HEADERS, |
flags, |
syn_stream.stream_id()); |
+ builder.WriteUInt32(priority); |
} |
- builder.WriteUInt32(syn_stream.associated_to_stream_id()); |
- uint8 priority = syn_stream.priority(); |
- if (priority > GetLowestPriority()) { |
- DLOG(DFATAL) << "Priority out-of-bounds."; |
- priority = GetLowestPriority(); |
- } |
- builder.WriteUInt8(priority << ((spdy_version_ < 3) ? 6 : 5)); |
- builder.WriteUInt8(syn_stream.slot()); |
- DCHECK_EQ(GetSynStreamMinimumSize(), builder.length()); |
SerializeNameValueBlock(&builder, syn_stream); |
if (debug_visitor_) { |
@@ -1736,7 +1811,7 @@ SpdySerializedFrame* SpdyFramer::SerializeSynReply( |
builder.WriteUInt32(syn_reply.stream_id()); |
} else { |
builder.WriteFramePrefix(*this, |
- SYN_REPLY, |
+ HEADERS, |
flags, |
syn_reply.stream_id()); |
} |
@@ -1810,24 +1885,29 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings( |
SpdyFrameBuilder builder(size); |
if (spdy_version_ < 4) { |
builder.WriteControlFrameHeader(*this, SETTINGS, flags); |
+ builder.WriteUInt32(values->size()); |
} else { |
builder.WriteFramePrefix(*this, SETTINGS, flags, 0); |
} |
- builder.WriteUInt32(values->size()); |
DCHECK_EQ(GetSettingsMinimumSize(), builder.length()); |
for (SpdySettingsIR::ValueMap::const_iterator it = values->begin(); |
it != values->end(); |
++it) { |
uint8 setting_flags = 0; |
- if (it->second.persist_value) { |
- setting_flags |= SETTINGS_FLAG_PLEASE_PERSIST; |
- } |
- if (it->second.persisted) { |
- setting_flags |= SETTINGS_FLAG_PERSISTED; |
+ if (spdy_version_ < SPDY4) { |
+ if (it->second.persist_value) { |
+ setting_flags |= SETTINGS_FLAG_PLEASE_PERSIST; |
+ } |
+ if (it->second.persisted) { |
+ setting_flags |= SETTINGS_FLAG_PERSISTED; |
+ } |
+ SettingsFlagsAndId flags_and_id(setting_flags, it->first); |
+ uint32 id_and_flags_wire = flags_and_id.GetWireFormat(protocol_version()); |
+ builder.WriteBytes(&id_and_flags_wire, 4); |
+ } else { |
+ builder.WriteUInt8(0); |
+ builder.WriteUInt24(it->first); |
} |
- SettingsFlagsAndId flags_and_id(setting_flags, it->first); |
- uint32 id_and_flags_wire = flags_and_id.GetWireFormat(protocol_version()); |
- builder.WriteBytes(&id_and_flags_wire, 4); |
builder.WriteUInt32(it->second.value); |
} |
DCHECK_EQ(size, builder.length()); |
@@ -1848,12 +1928,15 @@ SpdyFrame* SpdyFramer::CreatePingFrame(uint32 unique_id) const { |
SpdySerializedFrame* SpdyFramer::SerializePing(const SpdyPingIR& ping) const { |
SpdyFrameBuilder builder(GetPingSize()); |
- if (spdy_version_ < 4) { |
+ if (spdy_version_ < SPDY4) { |
builder.WriteControlFrameHeader(*this, PING, kNoFlags); |
} else { |
builder.WriteFramePrefix(*this, PING, 0, 0); |
} |
builder.WriteUInt32(ping.id()); |
+ if (spdy_version_ >= SPDY4) { |
+ builder.WriteUInt32(0); |
+ } |
DCHECK_EQ(GetPingSize(), builder.length()); |
return builder.take(); |
} |