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 |