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_; |