| Index: net/spdy/spdy_framer.cc
|
| diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
|
| index 73aa53299ce8bc418cd8a4f8909e7e4d861217ee..79a529f5724f172ffb4d9e07718dbd3087f8582c 100644
|
| --- a/net/spdy/spdy_framer.cc
|
| +++ b/net/spdy/spdy_framer.cc
|
| @@ -13,6 +13,7 @@
|
| #include "net/spdy/spdy_bitmasks.h"
|
| #include "third_party/zlib/zlib.h"
|
|
|
| +using base::StringPiece;
|
| using std::string;
|
| using std::vector;
|
|
|
| @@ -50,7 +51,6 @@ const uint8 kNoFlags = 0;
|
|
|
| const SpdyStreamId SpdyFramer::kInvalidStream = -1;
|
| const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024;
|
| -// The size of the control frame buffer. Must be >= the minimum size of the
|
| // largest control frame, which is SYN_STREAM. See GetSynStreamMinimumSize() for
|
| // calculation details.
|
| const size_t SpdyFramer::kControlFrameBufferSize = 18;
|
| @@ -109,6 +109,9 @@ void SettingsFlagsAndId::ConvertFlagsAndIdForSpdy2(uint32* val) {
|
| std::swap(wire_array[1], wire_array[2]);
|
| }
|
|
|
| +SpdyAltSvcScratch::SpdyAltSvcScratch() { Reset(); }
|
| +SpdyAltSvcScratch::~SpdyAltSvcScratch() {}
|
| +
|
| bool SpdyFramerVisitorInterface::OnGoAwayFrameData(const char* goaway_data,
|
| size_t len) {
|
| return true;
|
| @@ -134,12 +137,6 @@ SpdyFramer::SpdyFramer(SpdyMajorVersion version)
|
| DCHECK_GE(spdy_version_, SPDY_MIN_VERSION);
|
| DCHECK_LE(spdy_version_, SPDY_MAX_VERSION);
|
| Reset();
|
| -
|
| - // SPDY4 and up use HPACK. Allocate instances for these protocol versions.
|
| - if (spdy_version_ > SPDY3) {
|
| - hpack_encoder_.reset(new HpackEncoder(ObtainHpackHuffmanTable()));
|
| - hpack_decoder_.reset(new HpackDecoder(ObtainHpackHuffmanTable()));
|
| - }
|
| }
|
|
|
| SpdyFramer::~SpdyFramer() {
|
| @@ -163,6 +160,7 @@ void SpdyFramer::Reset() {
|
| current_frame_length_ = 0;
|
| current_frame_stream_id_ = kInvalidStream;
|
| settings_scratch_.Reset();
|
| + altsvc_scratch_.Reset();
|
| remaining_padding_payload_length_ = 0;
|
| remaining_padding_length_fields_ = 0;
|
| }
|
| @@ -307,12 +305,28 @@ size_t SpdyFramer::GetPushPromiseMinimumSize() const {
|
| return GetControlFrameHeaderSize() + 4;
|
| }
|
|
|
| -size_t SpdyFramer::GetContinuationMinimumSize() const {
|
| +size_t SpdyFramer::GetContinuationMinimumSize() const {
|
| // Size, in bytes, of a CONTINUATION frame not including the variable-length
|
| // headers fragments.
|
| return GetControlFrameHeaderSize();
|
| }
|
|
|
| +size_t SpdyFramer::GetAltSvcMinimumSize() const {
|
| + // Size, in bytes, of an ALTSVC frame not including the Protocol-ID, Host, and
|
| + // (optional) Origin fields, all of which can vary in length.
|
| + // Note that this gives a lower bound on the frame size rather than a true
|
| + // minimum; the actual frame should always be larger than this.
|
| + // Calculated as frame prefix + 4 (max-age) + 2 (port) + 1 (reserved byte)
|
| + // + 1 (pid_len) + 1 (host_len).
|
| + return GetControlFrameHeaderSize() + 9;
|
| +}
|
| +
|
| +size_t SpdyFramer::GetPrioritySize() const {
|
| + // Size, in bytes, of a PRIORITY frame.
|
| + // Calculated as frame prefix + 4 (stream dependency) + 1 (weight)
|
| + return GetControlFrameHeaderSize() + 5;
|
| +}
|
| +
|
| size_t SpdyFramer::GetFrameMinimumSize() const {
|
| return std::min(GetDataFrameMinimumSize(), GetControlFrameHeaderSize());
|
| }
|
| @@ -359,6 +373,8 @@ const char* SpdyFramer::StateToString(int state) {
|
| return "SPDY_RST_STREAM_FRAME_PAYLOAD";
|
| case SPDY_SETTINGS_FRAME_PAYLOAD:
|
| return "SPDY_SETTINGS_FRAME_PAYLOAD";
|
| + case SPDY_ALTSVC_FRAME_PAYLOAD:
|
| + return "SPDY_ALTSVC_FRAME_PAYLOAD";
|
| }
|
| return "UNKNOWN_STATE";
|
| }
|
| @@ -466,6 +482,10 @@ const char* SpdyFramer::FrameTypeToString(SpdyFrameType type) {
|
| return "PUSH_PROMISE";
|
| case CONTINUATION:
|
| return "CONTINUATION";
|
| + case ALTSVC:
|
| + return "ALTSVC";
|
| + case PRIORITY:
|
| + return "PRIORITY";
|
| }
|
| return "UNKNOWN_CONTROL_TYPE";
|
| }
|
| @@ -545,6 +565,13 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
|
| break;
|
| }
|
|
|
| + case SPDY_ALTSVC_FRAME_PAYLOAD: {
|
| + size_t bytes_read = ProcessAltSvcFramePayload(data, len);
|
| + len -= bytes_read;
|
| + data += bytes_read;
|
| + break;
|
| + }
|
| +
|
| case SPDY_CONTROL_FRAME_PAYLOAD: {
|
| size_t bytes_read = ProcessControlFramePayload(data, len);
|
| len -= bytes_read;
|
| @@ -902,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);
|
| }
|
| }
|
| @@ -917,7 +946,9 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
|
| }
|
| break;
|
| case BLOCKED:
|
| - if (current_frame_length_ != GetBlockedSize()) {
|
| + if (current_frame_length_ != GetBlockedSize() ||
|
| + protocol_version() <= SPDY3) {
|
| + // TODO(mlavan): BLOCKED frames are no longer part of SPDY4.
|
| set_error(SPDY_INVALID_CONTROL_FRAME);
|
| } else if (current_frame_flags_ != 0) {
|
| set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
|
| @@ -928,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;
|
| @@ -937,7 +970,24 @@ 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;
|
| + case ALTSVC:
|
| + if (current_frame_length_ <= GetAltSvcMinimumSize()) {
|
| + set_error(SPDY_INVALID_CONTROL_FRAME);
|
| + } else if (current_frame_flags_ != 0) {
|
| + set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
|
| + }
|
| + break;
|
| + case PRIORITY:
|
| + if (current_frame_length_ != GetPrioritySize() ||
|
| + protocol_version() <= SPDY3) {
|
| + set_error(SPDY_INVALID_CONTROL_FRAME);
|
| + } else if (current_frame_flags_ != 0) {
|
| set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
|
| }
|
| break;
|
| @@ -975,6 +1025,10 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
|
| return;
|
| }
|
|
|
| + if (current_frame_type_ == ALTSVC) {
|
| + CHANGE_STATE(SPDY_ALTSVC_FRAME_PAYLOAD);
|
| + return;
|
| + }
|
| // Determine the frame size without variable-length data.
|
| int32 frame_size_without_variable_data;
|
| switch (current_frame_type_) {
|
| @@ -1396,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:
|
| {
|
| @@ -1427,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:
|
| {
|
| @@ -1453,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);
|
| @@ -1479,11 +1533,11 @@ 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 (!hpack_decoder_->HandleControlFrameHeadersData(current_frame_stream_id_,
|
| - data,
|
| - process_bytes)) {
|
| + if (!GetHpackDecoder()->HandleControlFrameHeadersData(
|
| + current_frame_stream_id_, data, process_bytes)) {
|
| // TODO(jgraettinger): Finer-grained HPACK error codes.
|
| set_error(SPDY_DECOMPRESS_FAILURE);
|
| processed_successfully = false;
|
| @@ -1500,11 +1554,12 @@ 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 (!hpack_decoder_->HandleControlFrameHeadersComplete(
|
| - current_frame_stream_id_)) {
|
| + if (!GetHpackDecoder()->HandleControlFrameHeadersComplete(
|
| + current_frame_stream_id_)) {
|
| set_error(SPDY_DECOMPRESS_FAILURE);
|
| processed_successfully = false;
|
| } else {
|
| @@ -1520,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.
|
| @@ -1597,9 +1648,9 @@ 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 = hpack_decoder_->decoded_block();
|
| + const SpdyNameValueBlock& block = GetHpackDecoder()->decoded_block();
|
| if (block.empty()) {
|
| // Special-case this to make tests happy.
|
| ProcessControlFrameHeaderBlock(NULL, 0, false);
|
| @@ -1612,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) {
|
| @@ -1717,6 +1776,13 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
|
| visitor_->OnBlocked(current_frame_stream_id_);
|
| }
|
| break;
|
| + case PRIORITY: {
|
| + DCHECK_LT(SPDY3, protocol_version());
|
| + // TODO(hkhalil): Process PRIORITY frames rather than ignore them.
|
| + reader.Seek(5);
|
| + DCHECK(reader.IsDoneReading());
|
| + }
|
| + break;
|
| default:
|
| // Unreachable.
|
| LOG(FATAL) << "Unhandled control frame " << current_frame_type_;
|
| @@ -1863,6 +1929,133 @@ size_t SpdyFramer::ProcessRstStreamFramePayload(const char* data, size_t len) {
|
| return original_len;
|
| }
|
|
|
| +size_t SpdyFramer::ProcessAltSvcFramePayload(const char* data, size_t len) {
|
| + if (len == 0) {
|
| + return 0;
|
| + }
|
| +
|
| + // Clamp to the actual remaining payload.
|
| + len = std::min(len, remaining_data_length_);
|
| +
|
| + size_t processed_bytes = 0;
|
| + size_t processing = 0;
|
| + size_t bytes_remaining;
|
| + char* buffer;
|
| + size_t* buffer_len;
|
| +
|
| + while (len > 0) {
|
| + if (altsvc_scratch_.pid_len == 0) {
|
| + // The size of the frame up to the PID_LEN field.
|
| + size_t fixed_len_portion = GetAltSvcMinimumSize() - 1;
|
| + bytes_remaining = fixed_len_portion - current_frame_buffer_length_;
|
| + processing = std::min(len, bytes_remaining);
|
| + // Buffer the new ALTSVC bytes we got.
|
| + UpdateCurrentFrameBuffer(&data, &len, processing);
|
| +
|
| + // Do we have enough to parse the length of the protocol id?
|
| + if (current_frame_buffer_length_ == fixed_len_portion) {
|
| + // Parse out the max age, port, and pid_len.
|
| + SpdyFrameReader reader(current_frame_buffer_.get(),
|
| + current_frame_buffer_length_);
|
| + reader.Seek(GetControlFrameHeaderSize()); // Seek past frame header.
|
| + bool successful_read = reader.ReadUInt32(&altsvc_scratch_.max_age);
|
| + reader.ReadUInt16(&altsvc_scratch_.port);
|
| + reader.Seek(1); // Reserved byte.
|
| + successful_read = successful_read &&
|
| + reader.ReadUInt8(&altsvc_scratch_.pid_len);
|
| + DCHECK(successful_read);
|
| + // Sanity check length value.
|
| + if (GetAltSvcMinimumSize() + altsvc_scratch_.pid_len >=
|
| + current_frame_length_) {
|
| + set_error(SPDY_INVALID_CONTROL_FRAME);
|
| + return 0;
|
| + }
|
| + altsvc_scratch_.protocol_id.reset(
|
| + new char[size_t(altsvc_scratch_.pid_len)]);
|
| + }
|
| + processed_bytes += processing;
|
| + continue;
|
| + } else if (altsvc_scratch_.pid_buf_len < altsvc_scratch_.pid_len) {
|
| + // Buffer protocol id field as in comes in.
|
| + buffer = altsvc_scratch_.protocol_id.get();
|
| + buffer_len = &altsvc_scratch_.pid_buf_len;
|
| + bytes_remaining = altsvc_scratch_.pid_len - altsvc_scratch_.pid_buf_len;
|
| + } else if (altsvc_scratch_.host_len == 0) {
|
| + // Parse out the host length.
|
| + processing = 1;
|
| + altsvc_scratch_.host_len = *reinterpret_cast<const uint8*>(data);
|
| + // Sanity check length value.
|
| + if (GetAltSvcMinimumSize() + altsvc_scratch_.pid_len +
|
| + altsvc_scratch_.host_len > current_frame_length_) {
|
| + set_error(SPDY_INVALID_CONTROL_FRAME);
|
| + return 0;
|
| + }
|
| + altsvc_scratch_.host.reset(new char[altsvc_scratch_.host_len]);
|
| + // Once we have host length, we can also determine the origin length
|
| + // by process of elimination.
|
| + altsvc_scratch_.origin_len = current_frame_length_ -
|
| + GetAltSvcMinimumSize() -
|
| + altsvc_scratch_.pid_len -
|
| + altsvc_scratch_.host_len;
|
| + if (altsvc_scratch_.origin_len > 0) {
|
| + altsvc_scratch_.origin.reset(new char[altsvc_scratch_.origin_len]);
|
| + }
|
| + data += processing;
|
| + processed_bytes += processing;
|
| + len -= processing;
|
| + continue;
|
| + } else if (altsvc_scratch_.host_buf_len < altsvc_scratch_.host_len) {
|
| + // Buffer host field as it comes in.
|
| + // TODO(mlavan): check formatting for host and origin
|
| + buffer = altsvc_scratch_.host.get();
|
| + buffer_len = &altsvc_scratch_.host_buf_len;
|
| + bytes_remaining = altsvc_scratch_.host_len - altsvc_scratch_.host_buf_len;
|
| + } else {
|
| + // Buffer (optional) origin field as it comes in.
|
| + if (altsvc_scratch_.origin_len <= 0) {
|
| + set_error(SPDY_INVALID_CONTROL_FRAME);
|
| + return 0;
|
| + }
|
| + buffer = altsvc_scratch_.origin.get();
|
| + buffer_len = &altsvc_scratch_.origin_buf_len;
|
| + bytes_remaining = remaining_data_length_ -
|
| + processed_bytes -
|
| + altsvc_scratch_.origin_buf_len;
|
| + if (len > bytes_remaining) {
|
| + // This is our last field; there shouldn't be any more bytes.
|
| + set_error(SPDY_INVALID_CONTROL_FRAME);
|
| + return 0;
|
| + }
|
| + }
|
| +
|
| + // Copy data bytes into the appropriate field.
|
| + processing = std::min(len, bytes_remaining);
|
| + memcpy(buffer + *buffer_len,
|
| + data,
|
| + processing);
|
| + *buffer_len += processing;
|
| + data += processing;
|
| + processed_bytes += processing;
|
| + len -= processing;
|
| + }
|
| +
|
| + remaining_data_length_ -= processed_bytes;
|
| + if (remaining_data_length_ == 0) {
|
| + visitor_->OnAltSvc(current_frame_stream_id_,
|
| + altsvc_scratch_.max_age,
|
| + altsvc_scratch_.port,
|
| + StringPiece(altsvc_scratch_.protocol_id.get(),
|
| + altsvc_scratch_.pid_len),
|
| + StringPiece(altsvc_scratch_.host.get(),
|
| + altsvc_scratch_.host_len),
|
| + StringPiece(altsvc_scratch_.origin.get(),
|
| + altsvc_scratch_.origin_len));
|
| + CHANGE_STATE(SPDY_AUTO_RESET);
|
| + }
|
| +
|
| + return processed_bytes;
|
| +}
|
| +
|
| size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) {
|
| DCHECK_EQ(SPDY_READ_PADDING_LENGTH, state_);
|
|
|
| @@ -1902,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;
|
| }
|
| @@ -1914,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);
|
| }
|
| @@ -1928,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;
|
| @@ -2148,10 +2353,10 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream(
|
| string hpack_encoding;
|
| if (protocol_version() > SPDY3) {
|
| if (enable_compression_) {
|
| - hpack_encoder_->EncodeHeaderSet(
|
| + GetHpackEncoder()->EncodeHeaderSet(
|
| syn_stream.name_value_block(), &hpack_encoding);
|
| } else {
|
| - hpack_encoder_->EncodeHeaderSetWithoutCompression(
|
| + GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
|
| syn_stream.name_value_block(), &hpack_encoding);
|
| }
|
| size += hpack_encoding.size();
|
| @@ -2213,10 +2418,10 @@ SpdySerializedFrame* SpdyFramer::SerializeSynReply(
|
| string hpack_encoding;
|
| if (protocol_version() > SPDY3) {
|
| if (enable_compression_) {
|
| - hpack_encoder_->EncodeHeaderSet(
|
| + GetHpackEncoder()->EncodeHeaderSet(
|
| syn_reply.name_value_block(), &hpack_encoding);
|
| } else {
|
| - hpack_encoder_->EncodeHeaderSetWithoutCompression(
|
| + GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
|
| syn_reply.name_value_block(), &hpack_encoding);
|
| }
|
| size += hpack_encoding.size();
|
| @@ -2433,10 +2638,10 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders(
|
| string hpack_encoding;
|
| if (protocol_version() > SPDY3) {
|
| if (enable_compression_) {
|
| - hpack_encoder_->EncodeHeaderSet(
|
| + GetHpackEncoder()->EncodeHeaderSet(
|
| headers.name_value_block(), &hpack_encoding);
|
| } else {
|
| - hpack_encoder_->EncodeHeaderSetWithoutCompression(
|
| + GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
|
| headers.name_value_block(), &hpack_encoding);
|
| }
|
| size += hpack_encoding.size();
|
| @@ -2526,10 +2731,10 @@ SpdyFrame* SpdyFramer::SerializePushPromise(
|
| string hpack_encoding;
|
| if (protocol_version() > SPDY3) {
|
| if (enable_compression_) {
|
| - hpack_encoder_->EncodeHeaderSet(
|
| + GetHpackEncoder()->EncodeHeaderSet(
|
| push_promise.name_value_block(), &hpack_encoding);
|
| } else {
|
| - hpack_encoder_->EncodeHeaderSetWithoutCompression(
|
| + GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
|
| push_promise.name_value_block(), &hpack_encoding);
|
| }
|
| size += hpack_encoding.size();
|
| @@ -2586,10 +2791,10 @@ SpdyFrame* SpdyFramer::SerializeContinuation(
|
| size_t size = GetContinuationMinimumSize();
|
| string hpack_encoding;
|
| if (enable_compression_) {
|
| - hpack_encoder_->EncodeHeaderSet(
|
| + GetHpackEncoder()->EncodeHeaderSet(
|
| continuation.name_value_block(), &hpack_encoding);
|
| } else {
|
| - hpack_encoder_->EncodeHeaderSetWithoutCompression(
|
| + GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
|
| continuation.name_value_block(), &hpack_encoding);
|
| }
|
| size += hpack_encoding.size();
|
| @@ -2610,6 +2815,29 @@ SpdyFrame* SpdyFramer::SerializeContinuation(
|
| return builder.take();
|
| }
|
|
|
| +SpdyFrame* SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc) {
|
| + DCHECK_LT(SPDY3, protocol_version());
|
| + size_t size = GetAltSvcMinimumSize();
|
| + size += altsvc.protocol_id().length();
|
| + size += altsvc.host().length();
|
| + size += altsvc.origin().length();
|
| +
|
| + SpdyFrameBuilder builder(size, protocol_version());
|
| + builder.BeginNewFrame(*this, ALTSVC, kNoFlags, altsvc.stream_id());
|
| +
|
| + builder.WriteUInt32(altsvc.max_age());
|
| + builder.WriteUInt16(altsvc.port());
|
| + builder.WriteUInt8(0); // Reserved.
|
| + builder.WriteUInt8(altsvc.protocol_id().length());
|
| + builder.WriteBytes(altsvc.protocol_id().data(),
|
| + altsvc.protocol_id().length());
|
| + builder.WriteUInt8(altsvc.host().length());
|
| + builder.WriteBytes(altsvc.host().data(), altsvc.host().length());
|
| + builder.WriteBytes(altsvc.origin().data(), altsvc.origin().length());
|
| + DCHECK_LT(GetAltSvcMinimumSize(), builder.length());
|
| + return builder.take();
|
| +}
|
| +
|
| namespace {
|
|
|
| class FrameSerializationVisitor : public SpdyFrameVisitor {
|
| @@ -2658,6 +2886,9 @@ class FrameSerializationVisitor : public SpdyFrameVisitor {
|
| const SpdyContinuationIR& continuation) OVERRIDE {
|
| frame_.reset(framer_->SerializeContinuation(continuation));
|
| }
|
| + virtual void VisitAltSvc(const SpdyAltSvcIR& altsvc) OVERRIDE {
|
| + frame_.reset(framer_->SerializeAltSvc(altsvc));
|
| + }
|
|
|
| private:
|
| SpdyFramer* framer_;
|
| @@ -2808,6 +3039,22 @@ z_stream* SpdyFramer::GetHeaderDecompressor() {
|
| return header_decompressor_.get();
|
| }
|
|
|
| +HpackEncoder* SpdyFramer::GetHpackEncoder() {
|
| + DCHECK_LT(SPDY3, spdy_version_);
|
| + if (hpack_encoder_.get() == NULL) {
|
| + hpack_encoder_.reset(new HpackEncoder(ObtainHpackHuffmanTable()));
|
| + }
|
| + return hpack_encoder_.get();
|
| +}
|
| +
|
| +HpackDecoder* SpdyFramer::GetHpackDecoder() {
|
| + DCHECK_LT(SPDY3, spdy_version_);
|
| + if (hpack_decoder_.get() == NULL) {
|
| + hpack_decoder_.reset(new HpackDecoder(ObtainHpackHuffmanTable()));
|
| + }
|
| + return hpack_decoder_.get();
|
| +}
|
| +
|
| // Incrementally decompress the control frame's header block, feeding the
|
| // result to the visitor in chunks. Continue this until the visitor
|
| // indicates that it cannot process any more data, or (more commonly) we
|
|
|