| 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();
|
| }
|
|
|