| Index: net/spdy/spdy_framer.cc
|
| diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
|
| index 33942b971add3922804b04c7f26e5be453679be8..ebc523b06156981bd371077d1d3b2dd9d0332af4 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;
|
| @@ -157,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,6 +311,16 @@ size_t SpdyFramer::GetContinuationMinimumSize() const {
|
| 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::GetFrameMinimumSize() const {
|
| return std::min(GetDataFrameMinimumSize(), GetControlFrameHeaderSize());
|
| }
|
| @@ -353,6 +367,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";
|
| }
|
| @@ -460,6 +476,8 @@ const char* SpdyFramer::FrameTypeToString(SpdyFrameType type) {
|
| return "PUSH_PROMISE";
|
| case CONTINUATION:
|
| return "CONTINUATION";
|
| + case ALTSVC:
|
| + return "ALTSVC";
|
| }
|
| return "UNKNOWN_CONTROL_TYPE";
|
| }
|
| @@ -539,6 +557,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;
|
| @@ -911,7 +936,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);
|
| @@ -935,6 +962,13 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
|
| 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;
|
| default:
|
| LOG(WARNING) << "Valid " << display_protocol_
|
| << " control frame with unhandled type: "
|
| @@ -969,6 +1003,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_) {
|
| @@ -1856,6 +1894,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_);
|
|
|
| @@ -2603,6 +2768,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 {
|
| @@ -2651,6 +2839,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_;
|
|
|