Index: net/spdy/spdy_framer.cc |
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc |
deleted file mode 100644 |
index e7603ff7bd445d6ee723db28f17a0ee7badba672..0000000000000000000000000000000000000000 |
--- a/net/spdy/spdy_framer.cc |
+++ /dev/null |
@@ -1,3286 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "net/spdy/spdy_framer.h" |
- |
-#include "base/lazy_instance.h" |
-#include "base/memory/scoped_ptr.h" |
-#include "base/third_party/valgrind/memcheck.h" |
-#include "net/spdy/spdy_frame_builder.h" |
-#include "net/spdy/spdy_frame_reader.h" |
-#include "net/spdy/spdy_bitmasks.h" |
-#include "third_party/zlib/zlib.h" |
- |
-using base::StringPiece; |
-using std::string; |
-using std::vector; |
- |
-namespace net { |
- |
-namespace { |
- |
-// Compute the id of our dictionary so that we know we're using the |
-// right one when asked for it. |
-uLong CalculateDictionaryId(const char* dictionary, |
- const size_t dictionary_size) { |
- uLong initial_value = adler32(0L, Z_NULL, 0); |
- return adler32(initial_value, |
- reinterpret_cast<const Bytef*>(dictionary), |
- dictionary_size); |
-} |
- |
-// Check to see if the name and value of a cookie are both empty. |
-bool IsCookieEmpty(const base::StringPiece& cookie) { |
- if (cookie.size() == 0) { |
- return true; |
- } |
- size_t pos = cookie.find('='); |
- if (pos == base::StringPiece::npos) { |
- return false; |
- } |
- // Ignore leading whitespaces of cookie value. |
- size_t value_start = pos + 1; |
- for (; value_start < cookie.size(); value_start++) { |
- if (!(cookie[value_start] == ' ' || cookie[value_start] == '\t')) { |
- break; |
- } |
- } |
- return (pos == 0) && ((cookie.size() - value_start) == 0); |
-} |
- |
-struct DictionaryIds { |
- DictionaryIds() |
- : v2_dictionary_id(CalculateDictionaryId(kV2Dictionary, kV2DictionarySize)), |
- v3_dictionary_id(CalculateDictionaryId(kV3Dictionary, kV3DictionarySize)) |
- {} |
- const uLong v2_dictionary_id; |
- const uLong v3_dictionary_id; |
-}; |
- |
-// Adler ID for the SPDY header compressor dictionaries. Note that they are |
-// initialized lazily to avoid static initializers. |
-base::LazyInstance<DictionaryIds>::Leaky g_dictionary_ids; |
- |
-// Used to indicate no flags in a SPDY flags field. |
-const uint8 kNoFlags = 0; |
- |
-// Wire sizes of priority payloads. |
-const size_t kPriorityDependencyPayloadSize = 4; |
-const size_t kPriorityWeightPayloadSize = 1; |
- |
-// Wire size of pad length field. |
-const size_t kPadLengthFieldSize = 1; |
- |
-} // namespace |
- |
-const SpdyStreamId SpdyFramer::kInvalidStream = static_cast<SpdyStreamId>(-1); |
-const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024; |
-// We fragment sent control frames at smaller payload boundaries. |
-const size_t SpdyFramer::kMaxControlFrameSize = 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 = 19; |
- |
-#ifdef DEBUG_SPDY_STATE_CHANGES |
-#define CHANGE_STATE(newstate) \ |
- do { \ |
- DVLOG(1) << "Changing state from: " \ |
- << StateToString(state_) \ |
- << " to " << StateToString(newstate) << "\n"; \ |
- DCHECK(state_ != SPDY_ERROR); \ |
- DCHECK_EQ(previous_state_, state_); \ |
- previous_state_ = state_; \ |
- state_ = newstate; \ |
- } while (false) |
-#else |
-#define CHANGE_STATE(newstate) \ |
- do { \ |
- DCHECK(state_ != SPDY_ERROR); \ |
- DCHECK_EQ(previous_state_, state_); \ |
- previous_state_ = state_; \ |
- state_ = newstate; \ |
- } while (false) |
-#endif |
- |
-SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat( |
- SpdyMajorVersion version, uint32 wire) { |
- if (version < SPDY3) { |
- ConvertFlagsAndIdForSpdy2(&wire); |
- } |
- return SettingsFlagsAndId(ntohl(wire) >> 24, ntohl(wire) & 0x00ffffff); |
-} |
- |
-SettingsFlagsAndId::SettingsFlagsAndId(uint8 flags, uint32 id) |
- : flags_(flags), id_(id & 0x00ffffff) { |
- LOG_IF(DFATAL, id > (1u << 24)) << "SPDY setting ID too large: " << id; |
-} |
- |
-uint32 SettingsFlagsAndId::GetWireFormat(SpdyMajorVersion version) |
- const { |
- uint32 wire = htonl(id_ & 0x00ffffff) | htonl(flags_ << 24); |
- if (version < SPDY3) { |
- ConvertFlagsAndIdForSpdy2(&wire); |
- } |
- return wire; |
-} |
- |
-// SPDY 2 had a bug in it with respect to byte ordering of id/flags field. |
-// This method is used to preserve buggy behavior and works on both |
-// little-endian and big-endian hosts. |
-// This method is also bidirectional (can be used to translate SPDY 2 to SPDY 3 |
-// as well as vice versa). |
-void SettingsFlagsAndId::ConvertFlagsAndIdForSpdy2(uint32* val) { |
- uint8* wire_array = reinterpret_cast<uint8*>(val); |
- std::swap(wire_array[0], wire_array[3]); |
- 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; |
-} |
- |
-bool SpdyFramerVisitorInterface::OnRstStreamFrameData( |
- const char* rst_stream_data, |
- size_t len) { |
- return true; |
-} |
- |
-SpdyFramer::SpdyFramer(SpdyMajorVersion version) |
- : current_frame_buffer_(new char[kControlFrameBufferSize]), |
- enable_compression_(true), |
- visitor_(NULL), |
- debug_visitor_(NULL), |
- display_protocol_("SPDY"), |
- protocol_version_(version), |
- syn_frame_processed_(false), |
- probable_http_response_(false), |
- expect_continuation_(0), |
- end_stream_when_done_(false) { |
- DCHECK_GE(protocol_version_, SPDY_MIN_VERSION); |
- DCHECK_LE(protocol_version_, SPDY_MAX_VERSION); |
- DCHECK_LE(kMaxControlFrameSize, |
- SpdyConstants::GetFrameMaximumSize(protocol_version_) + |
- SpdyConstants::GetControlFrameHeaderSize(protocol_version_)); |
- Reset(); |
-} |
- |
-SpdyFramer::~SpdyFramer() { |
- if (header_compressor_.get()) { |
- deflateEnd(header_compressor_.get()); |
- } |
- if (header_decompressor_.get()) { |
- inflateEnd(header_decompressor_.get()); |
- } |
-} |
- |
-void SpdyFramer::Reset() { |
- state_ = SPDY_RESET; |
- previous_state_ = SPDY_RESET; |
- error_code_ = SPDY_NO_ERROR; |
- remaining_data_length_ = 0; |
- remaining_control_header_ = 0; |
- current_frame_buffer_length_ = 0; |
- current_frame_type_ = DATA; |
- current_frame_flags_ = 0; |
- current_frame_length_ = 0; |
- current_frame_stream_id_ = kInvalidStream; |
- settings_scratch_.Reset(); |
- altsvc_scratch_.Reset(); |
- remaining_padding_payload_length_ = 0; |
-} |
- |
-size_t SpdyFramer::GetDataFrameMinimumSize() const { |
- return SpdyConstants::GetDataFrameMinimumSize(protocol_version()); |
-} |
- |
-// Size, in bytes, of the control frame header. |
-size_t SpdyFramer::GetControlFrameHeaderSize() const { |
- return SpdyConstants::GetControlFrameHeaderSize(protocol_version()); |
-} |
- |
-size_t SpdyFramer::GetSynStreamMinimumSize() const { |
- // Size, in bytes, of a SYN_STREAM frame not including the variable-length |
- // name-value block. |
- if (protocol_version() <= SPDY3) { |
- // Calculated as: |
- // control frame header + 2 * 4 (stream IDs) + 1 (priority) |
- // + 1 (unused, was credential slot) |
- return GetControlFrameHeaderSize() + 10; |
- } else { |
- return GetControlFrameHeaderSize() + |
- kPriorityDependencyPayloadSize + |
- kPriorityWeightPayloadSize; |
- } |
-} |
- |
-size_t SpdyFramer::GetSynReplyMinimumSize() const { |
- // Size, in bytes, of a SYN_REPLY frame not including the variable-length |
- // name-value block. |
- size_t size = GetControlFrameHeaderSize(); |
- if (protocol_version() <= SPDY3) { |
- // Calculated as: |
- // control frame header + 4 (stream IDs) |
- size += 4; |
- } |
- |
- // In SPDY 2, there were 2 unused bytes before payload. |
- if (protocol_version() < SPDY3) { |
- size += 2; |
- } |
- |
- return size; |
-} |
- |
-size_t SpdyFramer::GetRstStreamMinimumSize() const { |
- // Size, in bytes, of a RST_STREAM frame. |
- if (protocol_version() <= SPDY3) { |
- // Calculated as: |
- // control frame header + 4 (stream id) + 4 (status code) |
- return GetControlFrameHeaderSize() + 8; |
- } else { |
- // Calculated as: |
- // frame prefix + 4 (status code) |
- return GetControlFrameHeaderSize() + 4; |
- } |
-} |
- |
-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) |
- if (protocol_version() <= SPDY3) { |
- return GetControlFrameHeaderSize() + 4; |
- } else { |
- return GetControlFrameHeaderSize(); |
- } |
-} |
- |
-size_t SpdyFramer::GetPingSize() const { |
- // Size, in bytes, of this PING frame. |
- if (protocol_version() <= SPDY3) { |
- // Calculated as: |
- // control frame header + 4 (id) |
- return GetControlFrameHeaderSize() + 4; |
- } else { |
- // Calculated as: |
- // control frame header + 8 (id) |
- return GetControlFrameHeaderSize() + 8; |
- } |
-} |
- |
-size_t SpdyFramer::GetGoAwayMinimumSize() const { |
- // Size, in bytes, of this GOAWAY frame. Calculated as: |
- // 1. Control frame header size |
- size_t size = GetControlFrameHeaderSize(); |
- |
- // 2. Last good stream id (4 bytes) |
- size += 4; |
- |
- // 3. SPDY 3+ GOAWAY frames also contain a status (4 bytes) |
- if (protocol_version() >= SPDY3) { |
- size += 4; |
- } |
- |
- return size; |
-} |
- |
-size_t SpdyFramer::GetHeadersMinimumSize() const { |
- // Size, in bytes, of a HEADERS frame not including the variable-length |
- // name-value block. |
- size_t size = GetControlFrameHeaderSize(); |
- if (protocol_version() <= SPDY3) { |
- // Calculated as: |
- // control frame header + 4 (stream IDs) |
- size += 4; |
- } |
- |
- // In SPDY 2, there were 2 unused bytes before payload. |
- if (protocol_version() <= SPDY2) { |
- size += 2; |
- } |
- |
- return size; |
-} |
- |
-size_t SpdyFramer::GetWindowUpdateSize() const { |
- // Size, in bytes, of a WINDOW_UPDATE frame. |
- if (protocol_version() <= SPDY3) { |
- // Calculated as: |
- // control frame header + 4 (stream id) + 4 (delta) |
- return GetControlFrameHeaderSize() + 8; |
- } else { |
- // Calculated as: |
- // frame prefix + 4 (delta) |
- return GetControlFrameHeaderSize() + 4; |
- } |
-} |
- |
-size_t SpdyFramer::GetBlockedSize() const { |
- DCHECK_LT(SPDY3, protocol_version()); |
- // Size, in bytes, of a BLOCKED frame. |
- // The BLOCKED frame has no payload beyond the control frame header. |
- return GetControlFrameHeaderSize(); |
-} |
- |
-size_t SpdyFramer::GetPushPromiseMinimumSize() const { |
- DCHECK_LT(SPDY3, protocol_version()); |
- // Size, in bytes, of a PUSH_PROMISE frame, sans the embedded header block. |
- // Calculated as frame prefix + 4 (promised stream id). |
- return GetControlFrameHeaderSize() + 4; |
-} |
- |
-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. |
- return GetControlFrameHeaderSize() + |
- kPriorityDependencyPayloadSize + |
- kPriorityWeightPayloadSize; |
-} |
- |
-size_t SpdyFramer::GetFrameMinimumSize() const { |
- return std::min(GetDataFrameMinimumSize(), GetControlFrameHeaderSize()); |
-} |
- |
-size_t SpdyFramer::GetFrameMaximumSize() const { |
- return SpdyConstants::GetFrameMaximumSize(protocol_version()); |
-} |
- |
-size_t SpdyFramer::GetDataFrameMaximumPayload() const { |
- return GetFrameMaximumSize() - GetDataFrameMinimumSize(); |
-} |
- |
-size_t SpdyFramer::GetPrefixLength(SpdyFrameType type) const { |
- return SpdyConstants::GetPrefixLength(type, protocol_version()); |
-} |
- |
-const char* SpdyFramer::StateToString(int state) { |
- switch (state) { |
- case SPDY_ERROR: |
- return "ERROR"; |
- case SPDY_AUTO_RESET: |
- return "AUTO_RESET"; |
- case SPDY_RESET: |
- return "RESET"; |
- case SPDY_READING_COMMON_HEADER: |
- return "READING_COMMON_HEADER"; |
- case SPDY_CONTROL_FRAME_PAYLOAD: |
- return "CONTROL_FRAME_PAYLOAD"; |
- case SPDY_READ_DATA_FRAME_PADDING_LENGTH: |
- return "SPDY_READ_DATA_FRAME_PADDING_LENGTH"; |
- case SPDY_CONSUME_PADDING: |
- return "SPDY_CONSUME_PADDING"; |
- case SPDY_IGNORE_REMAINING_PAYLOAD: |
- return "IGNORE_REMAINING_PAYLOAD"; |
- case SPDY_FORWARD_STREAM_FRAME: |
- return "FORWARD_STREAM_FRAME"; |
- case SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK: |
- return "SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK"; |
- case SPDY_CONTROL_FRAME_HEADER_BLOCK: |
- return "SPDY_CONTROL_FRAME_HEADER_BLOCK"; |
- case SPDY_GOAWAY_FRAME_PAYLOAD: |
- return "SPDY_GOAWAY_FRAME_PAYLOAD"; |
- case SPDY_RST_STREAM_FRAME_PAYLOAD: |
- 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"; |
-} |
- |
-void SpdyFramer::set_error(SpdyError error) { |
- DCHECK(visitor_); |
- error_code_ = error; |
- // These values will usually get reset once we come to the end |
- // of a header block, but if we run into an error that |
- // might not happen, so reset them here. |
- expect_continuation_ = 0; |
- end_stream_when_done_ = false; |
- |
- CHANGE_STATE(SPDY_ERROR); |
- visitor_->OnError(this); |
-} |
- |
-const char* SpdyFramer::ErrorCodeToString(int error_code) { |
- switch (error_code) { |
- case SPDY_NO_ERROR: |
- return "NO_ERROR"; |
- case SPDY_INVALID_CONTROL_FRAME: |
- return "INVALID_CONTROL_FRAME"; |
- case SPDY_CONTROL_PAYLOAD_TOO_LARGE: |
- return "CONTROL_PAYLOAD_TOO_LARGE"; |
- case SPDY_ZLIB_INIT_FAILURE: |
- return "ZLIB_INIT_FAILURE"; |
- case SPDY_UNSUPPORTED_VERSION: |
- return "UNSUPPORTED_VERSION"; |
- case SPDY_DECOMPRESS_FAILURE: |
- return "DECOMPRESS_FAILURE"; |
- case SPDY_COMPRESS_FAILURE: |
- return "COMPRESS_FAILURE"; |
- case SPDY_INVALID_DATA_FRAME_FLAGS: |
- return "SPDY_INVALID_DATA_FRAME_FLAGS"; |
- case SPDY_INVALID_CONTROL_FRAME_FLAGS: |
- return "SPDY_INVALID_CONTROL_FRAME_FLAGS"; |
- case SPDY_UNEXPECTED_FRAME: |
- return "UNEXPECTED_FRAME"; |
- } |
- return "UNKNOWN_ERROR"; |
-} |
- |
-const char* SpdyFramer::StatusCodeToString(int status_code) { |
- switch (status_code) { |
- case RST_STREAM_INVALID: |
- return "INVALID"; |
- case RST_STREAM_PROTOCOL_ERROR: |
- return "PROTOCOL_ERROR"; |
- case RST_STREAM_INVALID_STREAM: |
- return "INVALID_STREAM"; |
- case RST_STREAM_REFUSED_STREAM: |
- return "REFUSED_STREAM"; |
- case RST_STREAM_UNSUPPORTED_VERSION: |
- return "UNSUPPORTED_VERSION"; |
- case RST_STREAM_CANCEL: |
- return "CANCEL"; |
- case RST_STREAM_INTERNAL_ERROR: |
- return "INTERNAL_ERROR"; |
- case RST_STREAM_FLOW_CONTROL_ERROR: |
- return "FLOW_CONTROL_ERROR"; |
- case RST_STREAM_STREAM_IN_USE: |
- return "STREAM_IN_USE"; |
- case RST_STREAM_STREAM_ALREADY_CLOSED: |
- return "STREAM_ALREADY_CLOSED"; |
- case RST_STREAM_INVALID_CREDENTIALS: |
- return "INVALID_CREDENTIALS"; |
- case RST_STREAM_FRAME_TOO_LARGE: |
- return "FRAME_TOO_LARGE"; |
- case RST_STREAM_CONNECT_ERROR: |
- return "CONNECT_ERROR"; |
- case RST_STREAM_ENHANCE_YOUR_CALM: |
- return "ENHANCE_YOUR_CALM"; |
- case RST_STREAM_INADEQUATE_SECURITY: |
- return "INADEQUATE_SECURITY"; |
- case RST_STREAM_HTTP_1_1_REQUIRED: |
- return "HTTP_1_1_REQUIRED"; |
- } |
- return "UNKNOWN_STATUS"; |
-} |
- |
-const char* SpdyFramer::FrameTypeToString(SpdyFrameType type) { |
- switch (type) { |
- case DATA: |
- return "DATA"; |
- case SYN_STREAM: |
- return "SYN_STREAM"; |
- case SYN_REPLY: |
- return "SYN_REPLY"; |
- case RST_STREAM: |
- return "RST_STREAM"; |
- case SETTINGS: |
- return "SETTINGS"; |
- case PING: |
- return "PING"; |
- case GOAWAY: |
- return "GOAWAY"; |
- case HEADERS: |
- return "HEADERS"; |
- case WINDOW_UPDATE: |
- return "WINDOW_UPDATE"; |
- case CREDENTIAL: |
- return "CREDENTIAL"; |
- case PUSH_PROMISE: |
- return "PUSH_PROMISE"; |
- case CONTINUATION: |
- return "CONTINUATION"; |
- case PRIORITY: |
- return "PRIORITY"; |
- case ALTSVC: |
- return "ALTSVC"; |
- case BLOCKED: |
- return "BLOCKED"; |
- } |
- return "UNKNOWN_CONTROL_TYPE"; |
-} |
- |
-size_t SpdyFramer::ProcessInput(const char* data, size_t len) { |
- DCHECK(visitor_); |
- DCHECK(data); |
- |
- size_t original_len = len; |
- do { |
- previous_state_ = state_; |
- switch (state_) { |
- case SPDY_ERROR: |
- goto bottom; |
- |
- case SPDY_AUTO_RESET: |
- case SPDY_RESET: |
- Reset(); |
- if (len > 0) { |
- CHANGE_STATE(SPDY_READING_COMMON_HEADER); |
- } |
- break; |
- |
- case SPDY_READING_COMMON_HEADER: { |
- size_t bytes_read = ProcessCommonHeader(data, len); |
- len -= bytes_read; |
- data += bytes_read; |
- break; |
- } |
- |
- case SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK: { |
- // Control frames that contain header blocks |
- // (SYN_STREAM, SYN_REPLY, HEADERS, PUSH_PROMISE, CONTINUATION) |
- // take a different path through the state machine - they |
- // will go: |
- // 1. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK |
- // 2. SPDY_CONTROL_FRAME_HEADER_BLOCK |
- // |
- // SETTINGS frames take a slightly modified route: |
- // 1. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK |
- // 2. SPDY_SETTINGS_FRAME_PAYLOAD |
- // |
- // All other control frames will use the alternate route directly to |
- // SPDY_CONTROL_FRAME_PAYLOAD |
- int bytes_read = ProcessControlFrameBeforeHeaderBlock(data, len); |
- len -= bytes_read; |
- data += bytes_read; |
- break; |
- } |
- |
- case SPDY_SETTINGS_FRAME_PAYLOAD: { |
- int bytes_read = ProcessSettingsFramePayload(data, len); |
- len -= bytes_read; |
- data += bytes_read; |
- break; |
- } |
- |
- case SPDY_CONTROL_FRAME_HEADER_BLOCK: { |
- int bytes_read = ProcessControlFrameHeaderBlock( |
- data, len, protocol_version() > SPDY3); |
- len -= bytes_read; |
- data += bytes_read; |
- break; |
- } |
- |
- case SPDY_RST_STREAM_FRAME_PAYLOAD: { |
- size_t bytes_read = ProcessRstStreamFramePayload(data, len); |
- len -= bytes_read; |
- data += bytes_read; |
- break; |
- } |
- |
- case SPDY_GOAWAY_FRAME_PAYLOAD: { |
- size_t bytes_read = ProcessGoAwayFramePayload(data, len); |
- len -= bytes_read; |
- data += bytes_read; |
- 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; |
- data += bytes_read; |
- break; |
- } |
- |
- case SPDY_READ_DATA_FRAME_PADDING_LENGTH: { |
- size_t bytes_read = ProcessDataFramePaddingLength(data, len); |
- len -= bytes_read; |
- data += bytes_read; |
- break; |
- } |
- |
- case SPDY_CONSUME_PADDING: { |
- size_t bytes_read = ProcessFramePadding(data, len); |
- len -= bytes_read; |
- data += bytes_read; |
- break; |
- } |
- |
- case SPDY_IGNORE_REMAINING_PAYLOAD: { |
- size_t bytes_read = ProcessIgnoredControlFramePayload(/*data,*/ len); |
- len -= bytes_read; |
- data += bytes_read; |
- break; |
- } |
- |
- case SPDY_FORWARD_STREAM_FRAME: { |
- size_t bytes_read = ProcessDataFramePayload(data, len); |
- len -= bytes_read; |
- data += bytes_read; |
- break; |
- } |
- |
- default: |
- LOG(DFATAL) << "Invalid value for " << display_protocol_ |
- << " framer state: " << state_; |
- // This ensures that we don't infinite-loop if state_ gets an |
- // invalid value somehow, such as due to a SpdyFramer getting deleted |
- // from a callback it calls. |
- goto bottom; |
- } |
- } while (state_ != previous_state_); |
- bottom: |
- DCHECK(len == 0 || state_ == SPDY_ERROR); |
- if (current_frame_buffer_length_ == 0 && |
- remaining_data_length_ == 0 && |
- remaining_control_header_ == 0) { |
- DCHECK(state_ == SPDY_RESET || state_ == SPDY_ERROR) |
- << "State: " << StateToString(state_); |
- } |
- |
- return original_len - len; |
-} |
- |
-size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { |
- // This should only be called when we're in the SPDY_READING_COMMON_HEADER |
- // state. |
- DCHECK_EQ(state_, SPDY_READING_COMMON_HEADER); |
- |
- size_t original_len = len; |
- |
- // Update current frame buffer as needed. |
- if (current_frame_buffer_length_ < GetControlFrameHeaderSize()) { |
- size_t bytes_desired = |
- GetControlFrameHeaderSize() - current_frame_buffer_length_; |
- UpdateCurrentFrameBuffer(&data, &len, bytes_desired); |
- } |
- |
- if (current_frame_buffer_length_ < GetControlFrameHeaderSize()) { |
- // Not enough information to do anything meaningful. |
- return original_len - len; |
- } |
- |
- // Using a scoped_ptr here since we may need to create a new SpdyFrameReader |
- // when processing DATA frames below. |
- scoped_ptr<SpdyFrameReader> reader( |
- new SpdyFrameReader(current_frame_buffer_.get(), |
- current_frame_buffer_length_)); |
- |
- bool is_control_frame = false; |
- |
- int control_frame_type_field = |
- SpdyConstants::DataFrameType(protocol_version()); |
- // ProcessControlFrameHeader() will set current_frame_type_ to the |
- // correct value if this is a valid control frame. |
- current_frame_type_ = DATA; |
- if (protocol_version() <= SPDY3) { |
- uint16 version = 0; |
- bool successful_read = reader->ReadUInt16(&version); |
- DCHECK(successful_read); |
- is_control_frame = (version & kControlFlagMask) != 0; |
- version &= ~kControlFlagMask; // Only valid for control frames. |
- if (is_control_frame) { |
- // We check version before we check validity: version can never be |
- // 'invalid', it can only be unsupported. |
- if (version < SpdyConstants::SerializeMajorVersion(SPDY_MIN_VERSION) || |
- version > SpdyConstants::SerializeMajorVersion(SPDY_MAX_VERSION) || |
- SpdyConstants::ParseMajorVersion(version) != protocol_version()) { |
- // Version does not match the version the framer was initialized with. |
- DVLOG(1) << "Unsupported SPDY version " |
- << version |
- << " (expected " << protocol_version() << ")"; |
- set_error(SPDY_UNSUPPORTED_VERSION); |
- return 0; |
- } |
- // We check control_frame_type_field's validity in |
- // ProcessControlFrameHeader(). |
- uint16 control_frame_type_field_uint16; |
- successful_read = reader->ReadUInt16(&control_frame_type_field_uint16); |
- control_frame_type_field = control_frame_type_field_uint16; |
- } else { |
- reader->Rewind(); |
- successful_read = reader->ReadUInt31(¤t_frame_stream_id_); |
- } |
- DCHECK(successful_read); |
- |
- successful_read = reader->ReadUInt8(¤t_frame_flags_); |
- DCHECK(successful_read); |
- |
- uint32 length_field = 0; |
- successful_read = reader->ReadUInt24(&length_field); |
- DCHECK(successful_read); |
- remaining_data_length_ = length_field; |
- current_frame_length_ = remaining_data_length_ + reader->GetBytesConsumed(); |
- } else { |
- uint32 length_field = 0; |
- bool successful_read = reader->ReadUInt24(&length_field); |
- DCHECK(successful_read); |
- |
- uint8 control_frame_type_field_uint8; |
- successful_read = reader->ReadUInt8(&control_frame_type_field_uint8); |
- DCHECK(successful_read); |
- // We check control_frame_type_field's validity in |
- // ProcessControlFrameHeader(). |
- control_frame_type_field = control_frame_type_field_uint8; |
- is_control_frame = control_frame_type_field != |
- SpdyConstants::SerializeFrameType(protocol_version(), DATA); |
- |
- if (is_control_frame) { |
- current_frame_length_ = length_field + GetControlFrameHeaderSize(); |
- } else { |
- current_frame_length_ = length_field + GetDataFrameMinimumSize(); |
- } |
- |
- successful_read = reader->ReadUInt8(¤t_frame_flags_); |
- DCHECK(successful_read); |
- |
- successful_read = reader->ReadUInt31(¤t_frame_stream_id_); |
- DCHECK(successful_read); |
- |
- remaining_data_length_ = current_frame_length_ - reader->GetBytesConsumed(); |
- |
- // Before we accept a DATA frame, we need to make sure we're not in the |
- // middle of processing a header block. |
- const bool is_continuation_frame = (control_frame_type_field == |
- SpdyConstants::SerializeFrameType(protocol_version(), CONTINUATION)); |
- if ((expect_continuation_ != 0) != is_continuation_frame) { |
- if (expect_continuation_ != 0) { |
- DLOG(ERROR) << "The framer was expecting to receive a CONTINUATION " |
- << "frame, but instead received frame type " |
- << control_frame_type_field; |
- } else { |
- DLOG(ERROR) << "The framer received an unexpected CONTINUATION frame."; |
- } |
- set_error(SPDY_UNEXPECTED_FRAME); |
- return original_len - len; |
- } |
- } |
- DCHECK_EQ(is_control_frame ? GetControlFrameHeaderSize() |
- : GetDataFrameMinimumSize(), |
- reader->GetBytesConsumed()); |
- DCHECK_EQ(current_frame_length_, |
- remaining_data_length_ + reader->GetBytesConsumed()); |
- |
- // This is just a sanity check for help debugging early frame errors. |
- if (remaining_data_length_ > 1000000u) { |
- // The strncmp for 5 is safe because we only hit this point if we |
- // have kMinCommonHeader (8) bytes |
- if (!syn_frame_processed_ && |
- strncmp(current_frame_buffer_.get(), "HTTP/", 5) == 0) { |
- LOG(WARNING) << "Unexpected HTTP response to " << display_protocol_ |
- << " request"; |
- probable_http_response_ = true; |
- } else { |
- LOG(WARNING) << "Unexpectedly large frame. " << display_protocol_ |
- << " session is likely corrupt."; |
- } |
- } |
- |
- // if we're here, then we have the common header all received. |
- if (!is_control_frame) { |
- if (protocol_version() > SPDY3) { |
- // Catch bogus tests sending oversized DATA frames. |
- DCHECK_GE(GetFrameMaximumSize(), current_frame_length_) |
- << "DATA frame too large for SPDY >= 4."; |
- } |
- |
- uint8 valid_data_flags = 0; |
- if (protocol_version() > SPDY3) { |
- valid_data_flags = |
- DATA_FLAG_FIN | DATA_FLAG_END_SEGMENT | DATA_FLAG_PADDED; |
- } else { |
- valid_data_flags = DATA_FLAG_FIN; |
- } |
- |
- if (current_frame_flags_ & ~valid_data_flags) { |
- set_error(SPDY_INVALID_DATA_FRAME_FLAGS); |
- } else { |
- visitor_->OnDataFrameHeader(current_frame_stream_id_, |
- remaining_data_length_, |
- current_frame_flags_ & DATA_FLAG_FIN); |
- if (remaining_data_length_ > 0) { |
- CHANGE_STATE(SPDY_READ_DATA_FRAME_PADDING_LENGTH); |
- } else { |
- // Empty data frame. |
- if (current_frame_flags_ & DATA_FLAG_FIN) { |
- visitor_->OnStreamFrameData( |
- current_frame_stream_id_, NULL, 0, true); |
- } |
- CHANGE_STATE(SPDY_AUTO_RESET); |
- } |
- } |
- } else { |
- ProcessControlFrameHeader(control_frame_type_field); |
- } |
- |
- return original_len - len; |
-} |
- |
-void SpdyFramer::ProcessControlFrameHeader(int control_frame_type_field) { |
- DCHECK_EQ(SPDY_NO_ERROR, error_code_); |
- DCHECK_LE(GetControlFrameHeaderSize(), current_frame_buffer_length_); |
- |
- // TODO(mlavan): Either remove credential frames from the code entirely, |
- // or add them to parsing + serialization methods for SPDY3. |
- // Early detection of deprecated frames that we ignore. |
- if (protocol_version() <= SPDY3) { |
- if (control_frame_type_field == CREDENTIAL) { |
- current_frame_type_ = CREDENTIAL; |
- DCHECK_EQ(SPDY3, protocol_version()); |
- DVLOG(1) << "CREDENTIAL control frame found. Ignoring."; |
- CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD); |
- return; |
- } |
- } |
- |
- if (!SpdyConstants::IsValidFrameType(protocol_version(), |
- control_frame_type_field)) { |
- if (protocol_version() <= SPDY3) { |
- DLOG(WARNING) << "Invalid control frame type " << control_frame_type_field |
- << " (protocol version: " << protocol_version() << ")"; |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- return; |
- } else { |
- // In HTTP2 we ignore unknown frame types for extensibility, as long as |
- // the rest of the control frame header is valid. |
- // We rely on the visitor to check validity of current_frame_stream_id_. |
- bool valid_stream = visitor_->OnUnknownFrame(current_frame_stream_id_, |
- control_frame_type_field); |
- if (valid_stream) { |
- DVLOG(1) << "Ignoring unknown frame type."; |
- CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD); |
- } else { |
- // Report an invalid frame error and close the stream if the |
- // stream_id is not valid. |
- DLOG(WARNING) << "Unknown control frame type " |
- << control_frame_type_field |
- << " received on invalid stream " |
- << current_frame_stream_id_; |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } |
- return; |
- } |
- } |
- |
- current_frame_type_ = SpdyConstants::ParseFrameType(protocol_version(), |
- control_frame_type_field); |
- |
- // Do some sanity checking on the control frame sizes and flags. |
- switch (current_frame_type_) { |
- case SYN_STREAM: |
- if (current_frame_length_ < GetSynStreamMinimumSize()) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ & |
- ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } |
- break; |
- case SYN_REPLY: |
- if (current_frame_length_ < GetSynReplyMinimumSize()) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ & ~CONTROL_FLAG_FIN) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } |
- break; |
- case RST_STREAM: |
- // For SPDY versions < 4, the header has a fixed length. |
- // For SPDY version 4 and up, the RST_STREAM frame may include optional |
- // opaque data, so we only have a lower limit on the frame size. |
- if ((current_frame_length_ != GetRstStreamMinimumSize() && |
- protocol_version() <= SPDY3) || |
- (current_frame_length_ < GetRstStreamMinimumSize() && |
- protocol_version() > SPDY3)) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ != 0) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } |
- break; |
- case SETTINGS: |
- { |
- // Make sure that we have an integral number of 8-byte key/value pairs, |
- // plus a 4-byte length field in SPDY3 and below. |
- size_t values_prefix_size = (protocol_version() <= SPDY3 ? 4 : 0); |
- // Size of each key/value pair in bytes. |
- size_t setting_size = SpdyConstants::GetSettingSize(protocol_version()); |
- if (current_frame_length_ < GetSettingsMinimumSize() || |
- (current_frame_length_ - GetControlFrameHeaderSize()) |
- % setting_size != values_prefix_size) { |
- DLOG(WARNING) << "Invalid length for SETTINGS frame: " |
- << current_frame_length_; |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (protocol_version() <= SPDY3 && |
- current_frame_flags_ & |
- ~SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } else if (protocol_version() > SPDY3 && |
- current_frame_flags_ & ~SETTINGS_FLAG_ACK) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } else if (protocol_version() > SPDY3 && |
- current_frame_flags_ & SETTINGS_FLAG_ACK && |
- current_frame_length_ > GetSettingsMinimumSize()) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } |
- break; |
- } |
- case PING: |
- if (current_frame_length_ != GetPingSize()) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if ((protocol_version() <= SPDY3 && current_frame_flags_ != 0) || |
- (current_frame_flags_ & ~PING_FLAG_ACK)) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } |
- break; |
- case GOAWAY: |
- { |
- // For SPDY version < 4, there are only mandatory fields and the header |
- // has a fixed length. For SPDY version >= 4, optional opaque data may |
- // be appended to the GOAWAY frame, thus there is only a minimal length |
- // restriction. |
- if ((current_frame_length_ != GetGoAwayMinimumSize() && |
- protocol_version() <= SPDY3) || |
- (current_frame_length_ < GetGoAwayMinimumSize() && |
- protocol_version() > SPDY3)) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ != 0) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } |
- break; |
- } |
- case HEADERS: |
- { |
- size_t min_size = GetHeadersMinimumSize(); |
- if (protocol_version() > SPDY3 && |
- (current_frame_flags_ & HEADERS_FLAG_PRIORITY)) { |
- min_size += 4; |
- } |
- if (current_frame_length_ < min_size) { |
- // TODO(mlavan): check here for HEADERS with no payload? |
- // (not allowed in SPDY4) |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } 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 | HEADERS_FLAG_END_SEGMENT | |
- HEADERS_FLAG_PADDED)) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } |
- } |
- break; |
- case WINDOW_UPDATE: |
- if (current_frame_length_ != GetWindowUpdateSize()) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ != 0) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } |
- break; |
- case BLOCKED: |
- if (current_frame_length_ != GetBlockedSize() || |
- protocol_version() <= SPDY3) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ != 0) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } |
- break; |
- case PUSH_PROMISE: |
- if (current_frame_length_ < GetPushPromiseMinimumSize()) { |
- 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 | |
- HEADERS_FLAG_PADDED)) { |
- set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); |
- } |
- break; |
- case CONTINUATION: |
- if (current_frame_length_ < GetContinuationMinimumSize() || |
- protocol_version() <= SPDY3) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- } else if (current_frame_flags_ & ~HEADERS_FLAG_END_HEADERS) { |
- 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; |
- default: |
- LOG(WARNING) << "Valid " << display_protocol_ |
- << " control frame with unhandled type: " |
- << current_frame_type_; |
- // This branch should be unreachable because of the frame type bounds |
- // check above. However, we DLOG(FATAL) here in an effort to painfully |
- // club the head of the developer who failed to keep this file in sync |
- // with spdy_protocol.h. |
- DLOG(FATAL); |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- break; |
- } |
- |
- if (state_ == SPDY_ERROR) { |
- return; |
- } |
- |
- if (current_frame_length_ > |
- SpdyConstants::GetFrameMaximumSize(protocol_version()) + |
- SpdyConstants::GetControlFrameHeaderSize(protocol_version())) { |
- DLOG(WARNING) << "Received control frame of type " << current_frame_type_ |
- << " with way too big of a payload: " |
- << current_frame_length_; |
- set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); |
- return; |
- } |
- |
- if (current_frame_type_ == GOAWAY) { |
- CHANGE_STATE(SPDY_GOAWAY_FRAME_PAYLOAD); |
- return; |
- } |
- |
- if (current_frame_type_ == RST_STREAM) { |
- CHANGE_STATE(SPDY_RST_STREAM_FRAME_PAYLOAD); |
- 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_) { |
- case SYN_STREAM: |
- syn_frame_processed_ = true; |
- frame_size_without_variable_data = GetSynStreamMinimumSize(); |
- break; |
- case SYN_REPLY: |
- syn_frame_processed_ = true; |
- frame_size_without_variable_data = GetSynReplyMinimumSize(); |
- break; |
- case SETTINGS: |
- frame_size_without_variable_data = GetSettingsMinimumSize(); |
- break; |
- case HEADERS: |
- frame_size_without_variable_data = GetHeadersMinimumSize(); |
- if (protocol_version() > SPDY3) { |
- if (current_frame_flags_ & HEADERS_FLAG_PADDED) { |
- frame_size_without_variable_data += kPadLengthFieldSize; |
- } |
- if (current_frame_flags_ & HEADERS_FLAG_PRIORITY) { |
- frame_size_without_variable_data += |
- kPriorityDependencyPayloadSize + |
- kPriorityWeightPayloadSize; |
- } |
- } |
- break; |
- case PUSH_PROMISE: |
- frame_size_without_variable_data = GetPushPromiseMinimumSize(); |
- if (protocol_version() > SPDY3 && |
- current_frame_flags_ & PUSH_PROMISE_FLAG_PADDED) { |
- frame_size_without_variable_data += kPadLengthFieldSize; |
- } |
- break; |
- case CONTINUATION: |
- frame_size_without_variable_data = GetContinuationMinimumSize(); |
- break; |
- default: |
- frame_size_without_variable_data = -1; |
- break; |
- } |
- |
- if ((frame_size_without_variable_data == -1) && |
- (current_frame_length_ > kControlFrameBufferSize)) { |
- // We should already be in an error state. Double-check. |
- DCHECK_EQ(SPDY_ERROR, state_); |
- if (state_ != SPDY_ERROR) { |
- LOG(DFATAL) << display_protocol_ |
- << " control frame buffer too small for fixed-length frame."; |
- set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); |
- } |
- return; |
- } |
- |
- if (frame_size_without_variable_data > 0) { |
- // We have a control frame with a header block. We need to parse the |
- // remainder of the control frame's header before we can parse the header |
- // block. The start of the header block varies with the control type. |
- DCHECK_GE(frame_size_without_variable_data, |
- static_cast<int32>(current_frame_buffer_length_)); |
- remaining_control_header_ = frame_size_without_variable_data - |
- current_frame_buffer_length_; |
- |
- CHANGE_STATE(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK); |
- return; |
- } |
- |
- CHANGE_STATE(SPDY_CONTROL_FRAME_PAYLOAD); |
-} |
- |
-size_t SpdyFramer::UpdateCurrentFrameBuffer(const char** data, size_t* len, |
- size_t max_bytes) { |
- size_t bytes_to_read = std::min(*len, max_bytes); |
- if (bytes_to_read > 0) { |
- DCHECK_GE(kControlFrameBufferSize, |
- current_frame_buffer_length_ + bytes_to_read); |
- memcpy(current_frame_buffer_.get() + current_frame_buffer_length_, |
- *data, |
- bytes_to_read); |
- current_frame_buffer_length_ += bytes_to_read; |
- *data += bytes_to_read; |
- *len -= bytes_to_read; |
- } |
- return bytes_to_read; |
-} |
- |
-size_t SpdyFramer::GetSerializedLength( |
- const SpdyMajorVersion spdy_version, |
- const SpdyHeaderBlock* headers) { |
- const size_t num_name_value_pairs_size |
- = (spdy_version < SPDY3) ? sizeof(uint16) : sizeof(uint32); |
- const size_t length_of_name_size = num_name_value_pairs_size; |
- const size_t length_of_value_size = num_name_value_pairs_size; |
- |
- size_t total_length = num_name_value_pairs_size; |
- for (SpdyHeaderBlock::const_iterator it = headers->begin(); |
- it != headers->end(); |
- ++it) { |
- // We add space for the length of the name and the length of the value as |
- // well as the length of the name and the length of the value. |
- total_length += length_of_name_size + it->first.size() + |
- length_of_value_size + it->second.size(); |
- } |
- return total_length; |
-} |
- |
-void SpdyFramer::WriteHeaderBlock(SpdyFrameBuilder* frame, |
- const SpdyMajorVersion spdy_version, |
- const SpdyHeaderBlock* headers) { |
- if (spdy_version < SPDY3) { |
- frame->WriteUInt16(static_cast<uint16>(headers->size())); |
- } else { |
- frame->WriteUInt32(headers->size()); |
- } |
- SpdyHeaderBlock::const_iterator it; |
- for (it = headers->begin(); it != headers->end(); ++it) { |
- if (spdy_version < SPDY3) { |
- frame->WriteString(it->first); |
- frame->WriteString(it->second); |
- } else { |
- frame->WriteStringPiece32(it->first); |
- frame->WriteStringPiece32(it->second); |
- } |
- } |
-} |
- |
-// TODO(phajdan.jr): Clean up after we no longer need |
-// to workaround http://crbug.com/139744. |
-#if !defined(USE_SYSTEM_ZLIB) |
- |
-// These constants are used by zlib to differentiate between normal data and |
-// cookie data. Cookie data is handled specially by zlib when compressing. |
-enum ZDataClass { |
- // kZStandardData is compressed normally, save that it will never match |
- // against any other class of data in the window. |
- kZStandardData = Z_CLASS_STANDARD, |
- // kZCookieData is compressed in its own Huffman blocks and only matches in |
- // its entirety and only against other kZCookieData blocks. Any matches must |
- // be preceeded by a kZStandardData byte, or a semicolon to prevent matching |
- // a suffix. It's assumed that kZCookieData ends in a semicolon to prevent |
- // prefix matches. |
- kZCookieData = Z_CLASS_COOKIE, |
- // kZHuffmanOnlyData is only Huffman compressed - no matches are performed |
- // against the window. |
- kZHuffmanOnlyData = Z_CLASS_HUFFMAN_ONLY, |
-}; |
- |
-// WriteZ writes |data| to the deflate context |out|. WriteZ will flush as |
-// needed when switching between classes of data. |
-static void WriteZ(const base::StringPiece& data, |
- ZDataClass clas, |
- z_stream* out) { |
- int rv; |
- |
- // If we are switching from standard to non-standard data then we need to end |
- // the current Huffman context to avoid it leaking between them. |
- if (out->clas == kZStandardData && |
- clas != kZStandardData) { |
- out->avail_in = 0; |
- rv = deflate(out, Z_PARTIAL_FLUSH); |
- DCHECK_EQ(Z_OK, rv); |
- DCHECK_EQ(0u, out->avail_in); |
- DCHECK_LT(0u, out->avail_out); |
- } |
- |
- out->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data.data())); |
- out->avail_in = data.size(); |
- out->clas = clas; |
- if (clas == kZStandardData) { |
- rv = deflate(out, Z_NO_FLUSH); |
- } else { |
- rv = deflate(out, Z_PARTIAL_FLUSH); |
- } |
- if (!data.empty()) { |
- // If we didn't provide any data then zlib will return Z_BUF_ERROR. |
- DCHECK_EQ(Z_OK, rv); |
- } |
- DCHECK_EQ(0u, out->avail_in); |
- DCHECK_LT(0u, out->avail_out); |
-} |
- |
-// WriteLengthZ writes |n| as a |length|-byte, big-endian number to |out|. |
-static void WriteLengthZ(size_t n, |
- unsigned length, |
- ZDataClass clas, |
- z_stream* out) { |
- char buf[4]; |
- DCHECK_LE(length, sizeof(buf)); |
- for (unsigned i = 1; i <= length; i++) { |
- buf[length - i] = static_cast<char>(n); |
- n >>= 8; |
- } |
- WriteZ(base::StringPiece(buf, length), clas, out); |
-} |
- |
-// WriteHeaderBlockToZ serialises |headers| to the deflate context |z| in a |
-// manner that resists the length of the compressed data from compromising |
-// cookie data. |
-void SpdyFramer::WriteHeaderBlockToZ(const SpdyHeaderBlock* headers, |
- z_stream* z) const { |
- unsigned length_length = 4; |
- if (protocol_version() < 3) |
- length_length = 2; |
- |
- WriteLengthZ(headers->size(), length_length, kZStandardData, z); |
- |
- std::map<std::string, std::string>::const_iterator it; |
- for (it = headers->begin(); it != headers->end(); ++it) { |
- WriteLengthZ(it->first.size(), length_length, kZStandardData, z); |
- WriteZ(it->first, kZStandardData, z); |
- |
- if (it->first == "cookie") { |
- // We require the cookie values (save for the last) to end with a |
- // semicolon and (save for the first) to start with a space. This is |
- // typically the format that we are given them in but we reserialize them |
- // to be sure. |
- |
- std::vector<base::StringPiece> cookie_values; |
- size_t cookie_length = 0; |
- base::StringPiece cookie_data(it->second); |
- |
- for (;;) { |
- while (!cookie_data.empty() && |
- (cookie_data[0] == ' ' || cookie_data[0] == '\t')) { |
- cookie_data.remove_prefix(1); |
- } |
- if (cookie_data.empty()) |
- break; |
- |
- size_t i; |
- for (i = 0; i < cookie_data.size(); i++) { |
- if (cookie_data[i] == ';') |
- break; |
- } |
- if (i < cookie_data.size()) { |
- if (!IsCookieEmpty(cookie_data.substr(0, i))) { |
- cookie_values.push_back(cookie_data.substr(0, i)); |
- cookie_length += i + 2 /* semicolon and space */; |
- } |
- cookie_data.remove_prefix(i + 1); |
- } else { |
- if (!IsCookieEmpty(cookie_data)) { |
- cookie_values.push_back(cookie_data); |
- cookie_length += cookie_data.size(); |
- } else if (cookie_length > 2) { |
- cookie_length -= 2 /* compensate for previously added length */; |
- } |
- cookie_data.remove_prefix(i); |
- } |
- } |
- |
- WriteLengthZ(cookie_length, length_length, kZStandardData, z); |
- for (size_t i = 0; i < cookie_values.size(); i++) { |
- std::string cookie; |
- // Since zlib will only back-reference complete cookies, a cookie that |
- // is currently last (and so doesn't have a trailing semicolon) won't |
- // match if it's later in a non-final position. The same is true of |
- // the first cookie. |
- if (i == 0 && cookie_values.size() == 1) { |
- cookie = cookie_values[i].as_string(); |
- } else if (i == 0) { |
- cookie = cookie_values[i].as_string() + ";"; |
- } else if (i < cookie_values.size() - 1) { |
- cookie = " " + cookie_values[i].as_string() + ";"; |
- } else { |
- cookie = " " + cookie_values[i].as_string(); |
- } |
- WriteZ(cookie, kZCookieData, z); |
- } |
- } else if (it->first == "accept" || |
- it->first == "accept-charset" || |
- it->first == "accept-encoding" || |
- it->first == "accept-language" || |
- it->first == "host" || |
- it->first == "version" || |
- it->first == "method" || |
- it->first == "scheme" || |
- it->first == ":host" || |
- it->first == ":version" || |
- it->first == ":method" || |
- it->first == ":scheme" || |
- it->first == "user-agent") { |
- WriteLengthZ(it->second.size(), length_length, kZStandardData, z); |
- WriteZ(it->second, kZStandardData, z); |
- } else { |
- // Non-whitelisted headers are Huffman compressed in their own block, but |
- // don't match against the window. |
- WriteLengthZ(it->second.size(), length_length, kZStandardData, z); |
- WriteZ(it->second, kZHuffmanOnlyData, z); |
- } |
- } |
- |
- z->avail_in = 0; |
- int rv = deflate(z, Z_SYNC_FLUSH); |
- DCHECK_EQ(Z_OK, rv); |
- z->clas = kZStandardData; |
-} |
- |
-#endif // !defined(USE_SYSTEM_ZLIB) |
- |
-size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, |
- size_t len) { |
- DCHECK_EQ(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, state_); |
- const size_t original_len = len; |
- |
- if (remaining_control_header_ > 0) { |
- size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, |
- remaining_control_header_); |
- remaining_control_header_ -= bytes_read; |
- remaining_data_length_ -= bytes_read; |
- } |
- |
- if (remaining_control_header_ == 0) { |
- SpdyFrameReader reader(current_frame_buffer_.get(), |
- current_frame_buffer_length_); |
- reader.Seek(GetControlFrameHeaderSize()); // Seek past frame header. |
- |
- switch (current_frame_type_) { |
- case SYN_STREAM: |
- { |
- DCHECK_GE(SPDY3, protocol_version()); |
- bool successful_read = true; |
- successful_read = reader.ReadUInt31(¤t_frame_stream_id_); |
- DCHECK(successful_read); |
- if (current_frame_stream_id_ == 0) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- break; |
- } |
- |
- SpdyStreamId associated_to_stream_id = kInvalidStream; |
- successful_read = reader.ReadUInt31(&associated_to_stream_id); |
- DCHECK(successful_read); |
- |
- SpdyPriority priority = 0; |
- successful_read = reader.ReadUInt8(&priority); |
- DCHECK(successful_read); |
- if (protocol_version() <= SPDY2) { |
- priority = priority >> 6; |
- } else { |
- priority = priority >> 5; |
- } |
- |
- // Seek past unused byte; used to be credential slot in SPDY 3. |
- reader.Seek(1); |
- |
- DCHECK(reader.IsDoneReading()); |
- if (debug_visitor_) { |
- debug_visitor_->OnReceiveCompressedFrame( |
- current_frame_stream_id_, |
- current_frame_type_, |
- current_frame_length_); |
- } |
- visitor_->OnSynStream( |
- current_frame_stream_id_, |
- associated_to_stream_id, |
- priority, |
- (current_frame_flags_ & CONTROL_FLAG_FIN) != 0, |
- (current_frame_flags_ & CONTROL_FLAG_UNIDIRECTIONAL) != 0); |
- } |
- CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); |
- break; |
- case SETTINGS: |
- if (protocol_version() > SPDY3 && |
- current_frame_flags_ & SETTINGS_FLAG_ACK) { |
- visitor_->OnSettingsAck(); |
- CHANGE_STATE(SPDY_AUTO_RESET); |
- } else { |
- visitor_->OnSettings(current_frame_flags_ & |
- SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS); |
- CHANGE_STATE(SPDY_SETTINGS_FRAME_PAYLOAD); |
- } |
- break; |
- case SYN_REPLY: |
- case HEADERS: |
- // SYN_REPLY and HEADERS are the same, save for the visitor call. |
- { |
- if (protocol_version() > SPDY3) { |
- DCHECK_EQ(HEADERS, current_frame_type_); |
- } |
- bool successful_read = true; |
- if (protocol_version() <= SPDY3) { |
- successful_read = reader.ReadUInt31(¤t_frame_stream_id_); |
- DCHECK(successful_read); |
- } |
- if (current_frame_stream_id_ == 0) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- break; |
- } |
- if (protocol_version() <= SPDY2) { |
- // SPDY 2 had two unused bytes here. Seek past them. |
- reader.Seek(2); |
- } |
- if (protocol_version() > SPDY3 && |
- !(current_frame_flags_ & HEADERS_FLAG_END_HEADERS) && |
- current_frame_type_ == HEADERS) { |
- expect_continuation_ = current_frame_stream_id_; |
- end_stream_when_done_ = current_frame_flags_ & CONTROL_FLAG_FIN; |
- } |
- if (protocol_version() > SPDY3 && |
- current_frame_flags_ & HEADERS_FLAG_PADDED) { |
- uint8 pad_payload_len = 0; |
- DCHECK_EQ(remaining_padding_payload_length_, 0u); |
- successful_read = reader.ReadUInt8(&pad_payload_len); |
- DCHECK(successful_read); |
- remaining_padding_payload_length_ = pad_payload_len; |
- } |
- const bool has_priority = |
- (current_frame_flags_ & HEADERS_FLAG_PRIORITY) != 0; |
- SpdyPriority priority = 0; |
- if (protocol_version() > SPDY3 && has_priority) { |
- // TODO(jgraettinger): Process dependency rather than ignoring it. |
- reader.Seek(kPriorityDependencyPayloadSize); |
- uint8 weight = 0; |
- successful_read = reader.ReadUInt8(&weight); |
- if (successful_read) { |
- priority = MapWeightToPriority(weight); |
- } |
- } |
- DCHECK(reader.IsDoneReading()); |
- if (debug_visitor_) { |
- debug_visitor_->OnReceiveCompressedFrame( |
- current_frame_stream_id_, |
- current_frame_type_, |
- current_frame_length_); |
- } |
- if (current_frame_type_ == SYN_REPLY) { |
- visitor_->OnSynReply( |
- current_frame_stream_id_, |
- (current_frame_flags_ & CONTROL_FLAG_FIN) != 0); |
- } else { |
- visitor_->OnHeaders( |
- current_frame_stream_id_, |
- (current_frame_flags_ & HEADERS_FLAG_PRIORITY) != 0, priority, |
- (current_frame_flags_ & CONTROL_FLAG_FIN) != 0, |
- expect_continuation_ == 0); |
- } |
- } |
- CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); |
- break; |
- case PUSH_PROMISE: |
- { |
- DCHECK_LT(SPDY3, protocol_version()); |
- if (current_frame_stream_id_ == 0) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- break; |
- } |
- bool successful_read = true; |
- if (protocol_version() > SPDY3 && |
- current_frame_flags_ & PUSH_PROMISE_FLAG_PADDED) { |
- DCHECK_EQ(remaining_padding_payload_length_, 0u); |
- uint8 pad_payload_len = 0; |
- successful_read = reader.ReadUInt8(&pad_payload_len); |
- DCHECK(successful_read); |
- remaining_padding_payload_length_ = pad_payload_len; |
- } |
- } |
- { |
- SpdyStreamId promised_stream_id = kInvalidStream; |
- bool successful_read = reader.ReadUInt31(&promised_stream_id); |
- DCHECK(successful_read); |
- DCHECK(reader.IsDoneReading()); |
- if (promised_stream_id == 0) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- break; |
- } |
- if (!(current_frame_flags_ & PUSH_PROMISE_FLAG_END_PUSH_PROMISE)) { |
- expect_continuation_ = current_frame_stream_id_; |
- } |
- if (debug_visitor_) { |
- debug_visitor_->OnReceiveCompressedFrame( |
- current_frame_stream_id_, |
- current_frame_type_, |
- current_frame_length_); |
- } |
- visitor_->OnPushPromise(current_frame_stream_id_, |
- promised_stream_id, |
- (current_frame_flags_ & |
- PUSH_PROMISE_FLAG_END_PUSH_PROMISE) != 0); |
- } |
- CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); |
- break; |
- case CONTINUATION: |
- { |
- // Check to make sure the stream id of the current frame is |
- // the same as that of the preceding frame. |
- // If we're at this point we should already know that |
- // expect_continuation_ != 0, so this doubles as a check |
- // that current_frame_stream_id != 0. |
- if (current_frame_stream_id_ != expect_continuation_) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- break; |
- } |
- if (current_frame_flags_ & HEADERS_FLAG_END_HEADERS) { |
- expect_continuation_ = 0; |
- } |
- if (debug_visitor_) { |
- debug_visitor_->OnReceiveCompressedFrame( |
- current_frame_stream_id_, |
- current_frame_type_, |
- current_frame_length_); |
- } |
- visitor_->OnContinuation(current_frame_stream_id_, |
- (current_frame_flags_ & |
- HEADERS_FLAG_END_HEADERS) != 0); |
- } |
- CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); |
- break; |
- default: |
- DCHECK(false); |
- } |
- } |
- return original_len - len; |
-} |
- |
-// Does not buffer the control payload. Instead, either passes directly to the |
-// visitor or decompresses and then passes directly to the visitor, via |
-// IncrementallyDeliverControlFrameHeaderData() or |
-// IncrementallyDecompressControlFrameHeaderData() respectively. |
-size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, |
- size_t data_len, |
- bool is_hpack_header_block) { |
- DCHECK_EQ(SPDY_CONTROL_FRAME_HEADER_BLOCK, state_); |
- |
- bool processed_successfully = true; |
- if (current_frame_type_ != SYN_STREAM && |
- current_frame_type_ != SYN_REPLY && |
- current_frame_type_ != HEADERS && |
- current_frame_type_ != PUSH_PROMISE && |
- current_frame_type_ != CONTINUATION) { |
- LOG(DFATAL) << "Unhandled frame type in ProcessControlFrameHeaderBlock."; |
- } |
- size_t process_bytes = std::min( |
- data_len, remaining_data_length_ - remaining_padding_payload_length_); |
- if (is_hpack_header_block) { |
- 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; |
- } |
- } else if (process_bytes > 0) { |
- if (enable_compression_ && protocol_version() <= SPDY3) { |
- processed_successfully = IncrementallyDecompressControlFrameHeaderData( |
- current_frame_stream_id_, data, process_bytes); |
- } else { |
- processed_successfully = IncrementallyDeliverControlFrameHeaderData( |
- current_frame_stream_id_, data, process_bytes); |
- } |
- } |
- remaining_data_length_ -= process_bytes; |
- |
- // Handle the case that there is no futher data in this frame. |
- if (remaining_data_length_ == remaining_padding_payload_length_ && |
- processed_successfully) { |
- if (expect_continuation_ == 0) { |
- if (is_hpack_header_block) { |
- if (!GetHpackDecoder()->HandleControlFrameHeadersComplete( |
- current_frame_stream_id_)) { |
- set_error(SPDY_DECOMPRESS_FAILURE); |
- processed_successfully = false; |
- } else { |
- // TODO(jgraettinger): To be removed with migration to |
- // SpdyHeadersHandlerInterface. Serializes the HPACK block as a SPDY3 |
- // block, delivered via reentrant call to |
- // ProcessControlFrameHeaderBlock(). |
- DeliverHpackBlockAsSpdy3Block(); |
- return process_bytes; |
- } |
- } else { |
- // The complete header block has been delivered. We send a zero-length |
- // OnControlFrameHeaderData() to indicate this. |
- visitor_->OnControlFrameHeaderData(current_frame_stream_id_, NULL, 0); |
- } |
- } |
- if (processed_successfully) { |
- CHANGE_STATE(SPDY_CONSUME_PADDING); |
- } |
- } |
- |
- // Handle error. |
- if (!processed_successfully) { |
- return data_len; |
- } |
- |
- // Return amount processed. |
- return process_bytes; |
-} |
- |
-size_t SpdyFramer::ProcessSettingsFramePayload(const char* data, |
- size_t data_len) { |
- DCHECK_EQ(SPDY_SETTINGS_FRAME_PAYLOAD, state_); |
- DCHECK_EQ(SETTINGS, current_frame_type_); |
- size_t unprocessed_bytes = std::min(data_len, remaining_data_length_); |
- size_t processed_bytes = 0; |
- |
- size_t setting_size = SpdyConstants::GetSettingSize(protocol_version()); |
- |
- // Loop over our incoming data. |
- while (unprocessed_bytes > 0) { |
- // Process up to one setting at a time. |
- size_t processing = std::min( |
- unprocessed_bytes, |
- static_cast<size_t>(setting_size - settings_scratch_.setting_buf_len)); |
- |
- // Check if we have a complete setting in our input. |
- if (processing == setting_size) { |
- // Parse the setting directly out of the input without buffering. |
- if (!ProcessSetting(data + processed_bytes)) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- return processed_bytes; |
- } |
- } else { |
- // Continue updating settings_scratch_.setting_buf. |
- memcpy(settings_scratch_.setting_buf + settings_scratch_.setting_buf_len, |
- data + processed_bytes, |
- processing); |
- settings_scratch_.setting_buf_len += processing; |
- |
- // Check if we have a complete setting buffered. |
- if (settings_scratch_.setting_buf_len == setting_size) { |
- if (!ProcessSetting(settings_scratch_.setting_buf)) { |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
- return processed_bytes; |
- } |
- // Reset settings_scratch_.setting_buf for our next setting. |
- settings_scratch_.setting_buf_len = 0; |
- } |
- } |
- |
- // Iterate. |
- unprocessed_bytes -= processing; |
- processed_bytes += processing; |
- } |
- |
- // Check if we're done handling this SETTINGS frame. |
- remaining_data_length_ -= processed_bytes; |
- if (remaining_data_length_ == 0) { |
- visitor_->OnSettingsEnd(); |
- CHANGE_STATE(SPDY_AUTO_RESET); |
- } |
- |
- return processed_bytes; |
-} |
- |
-void SpdyFramer::DeliverHpackBlockAsSpdy3Block() { |
- DCHECK_LT(SPDY3, protocol_version()); |
- DCHECK_EQ(remaining_padding_payload_length_, remaining_data_length_); |
- |
- const SpdyNameValueBlock& block = GetHpackDecoder()->decoded_block(); |
- if (block.empty()) { |
- // Special-case this to make tests happy. |
- ProcessControlFrameHeaderBlock(NULL, 0, false); |
- return; |
- } |
- SpdyFrameBuilder builder( |
- GetSerializedLength(protocol_version(), &block), |
- SPDY3); |
- |
- 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) { |
- int id_field; |
- SpdySettingsIds id; |
- uint8 flags = 0; |
- uint32 value; |
- |
- // Extract fields. |
- // Maintain behavior of old SPDY 2 bug with byte ordering of flags/id. |
- if (protocol_version() <= SPDY3) { |
- const uint32 id_and_flags_wire = *(reinterpret_cast<const uint32*>(data)); |
- SettingsFlagsAndId id_and_flags = |
- SettingsFlagsAndId::FromWireFormat(protocol_version(), id_and_flags_wire); |
- id_field = id_and_flags.id(); |
- flags = id_and_flags.flags(); |
- value = ntohl(*(reinterpret_cast<const uint32*>(data + 4))); |
- } else { |
- id_field = ntohs(*(reinterpret_cast<const uint16*>(data))); |
- value = ntohl(*(reinterpret_cast<const uint32*>(data + 2))); |
- } |
- |
- // Validate id. |
- if (!SpdyConstants::IsValidSettingId(protocol_version(), id_field)) { |
- DLOG(WARNING) << "Unknown SETTINGS ID: " << id_field; |
- if (protocol_version() <= SPDY3) { |
- return false; |
- } else { |
- // In HTTP2 we ignore unknown settings for extensibility. |
- return true; |
- } |
- } |
- id = SpdyConstants::ParseSettingId(protocol_version(), id_field); |
- |
- if (protocol_version() <= SPDY3) { |
- // Detect duplicates. |
- if (id <= settings_scratch_.last_setting_id) { |
- DLOG(WARNING) << "Duplicate entry or invalid ordering for id " << id |
- << " in " << display_protocol_ << " SETTINGS frame " |
- << "(last setting id was " |
- << settings_scratch_.last_setting_id << ")."; |
- return false; |
- } |
- settings_scratch_.last_setting_id = id; |
- |
- // Validate flags. |
- uint8 kFlagsMask = SETTINGS_FLAG_PLEASE_PERSIST | SETTINGS_FLAG_PERSISTED; |
- if ((flags & ~(kFlagsMask)) != 0) { |
- DLOG(WARNING) << "Unknown SETTINGS flags provided for id " << id << ": " |
- << flags; |
- return false; |
- } |
- } |
- |
- // Validation succeeded. Pass on to visitor. |
- visitor_->OnSetting(id, flags, value); |
- return true; |
-} |
- |
-size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { |
- size_t original_len = len; |
- size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, |
- remaining_data_length_); |
- remaining_data_length_ -= bytes_read; |
- if (remaining_data_length_ == 0) { |
- SpdyFrameReader reader(current_frame_buffer_.get(), |
- current_frame_buffer_length_); |
- reader.Seek(GetControlFrameHeaderSize()); // Skip frame header. |
- |
- // Use frame-specific handlers. |
- switch (current_frame_type_) { |
- case PING: { |
- SpdyPingId id = 0; |
- bool is_ack = protocol_version() > SPDY3 && |
- (current_frame_flags_ & PING_FLAG_ACK); |
- bool successful_read = true; |
- if (protocol_version() <= SPDY3) { |
- uint32 id32 = 0; |
- successful_read = reader.ReadUInt32(&id32); |
- id = id32; |
- } else { |
- successful_read = reader.ReadUInt64(&id); |
- } |
- DCHECK(successful_read); |
- DCHECK(reader.IsDoneReading()); |
- visitor_->OnPing(id, is_ack); |
- } |
- break; |
- case WINDOW_UPDATE: { |
- uint32 delta_window_size = 0; |
- bool successful_read = true; |
- if (protocol_version() <= SPDY3) { |
- successful_read = reader.ReadUInt31(¤t_frame_stream_id_); |
- DCHECK(successful_read); |
- } |
- successful_read = reader.ReadUInt32(&delta_window_size); |
- DCHECK(successful_read); |
- DCHECK(reader.IsDoneReading()); |
- visitor_->OnWindowUpdate(current_frame_stream_id_, |
- delta_window_size); |
- } |
- break; |
- case BLOCKED: { |
- DCHECK_LT(SPDY3, protocol_version()); |
- DCHECK(reader.IsDoneReading()); |
- visitor_->OnBlocked(current_frame_stream_id_); |
- } |
- break; |
- case PRIORITY: { |
- DCHECK_LT(SPDY3, protocol_version()); |
- uint32 parent_stream_id; |
- uint8 weight; |
- bool exclusive; |
- bool successful_read = true; |
- successful_read = reader.ReadUInt32(&parent_stream_id); |
- DCHECK(successful_read); |
- // Exclusivity is indicated by a single bit flag. |
- exclusive = (parent_stream_id >> 31) != 0; |
- // Zero out the highest-order bit to get the parent stream id. |
- parent_stream_id &= 0x7fffffff; |
- successful_read = reader.ReadUInt8(&weight); |
- DCHECK(successful_read); |
- DCHECK(reader.IsDoneReading()); |
- visitor_->OnPriority( |
- current_frame_stream_id_, parent_stream_id, weight, exclusive); |
- } |
- break; |
- default: |
- // Unreachable. |
- LOG(FATAL) << "Unhandled control frame " << current_frame_type_; |
- } |
- |
- CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD); |
- } |
- return original_len - len; |
-} |
- |
-size_t SpdyFramer::ProcessGoAwayFramePayload(const char* data, size_t len) { |
- if (len == 0) { |
- return 0; |
- } |
- // Clamp to the actual remaining payload. |
- if (len > remaining_data_length_) { |
- len = remaining_data_length_; |
- } |
- size_t original_len = len; |
- |
- // Check if we had already read enough bytes to parse the GOAWAY header. |
- const size_t header_size = GetGoAwayMinimumSize(); |
- size_t unread_header_bytes = header_size - current_frame_buffer_length_; |
- bool already_parsed_header = (unread_header_bytes == 0); |
- if (!already_parsed_header) { |
- // Buffer the new GOAWAY header bytes we got. |
- UpdateCurrentFrameBuffer(&data, &len, unread_header_bytes); |
- |
- // Do we have enough to parse the constant size GOAWAY header? |
- if (current_frame_buffer_length_ == header_size) { |
- // Parse out the last good stream id. |
- SpdyFrameReader reader(current_frame_buffer_.get(), |
- current_frame_buffer_length_); |
- reader.Seek(GetControlFrameHeaderSize()); // Seek past frame header. |
- bool successful_read = reader.ReadUInt31(¤t_frame_stream_id_); |
- DCHECK(successful_read); |
- |
- // In SPDYv3 and up, frames also specify a status code - parse it out. |
- SpdyGoAwayStatus status = GOAWAY_OK; |
- if (protocol_version() >= SPDY3) { |
- uint32 status_raw = GOAWAY_OK; |
- successful_read = reader.ReadUInt32(&status_raw); |
- DCHECK(successful_read); |
- if (SpdyConstants::IsValidGoAwayStatus(protocol_version(), |
- status_raw)) { |
- status = SpdyConstants::ParseGoAwayStatus(protocol_version(), |
- status_raw); |
- } else { |
- if (protocol_version() > SPDY3) { |
- // Treat unrecognized status codes as INTERNAL_ERROR as |
- // recommended by the HTTP/2 spec. |
- status = GOAWAY_INTERNAL_ERROR; |
- } |
- } |
- } |
- // Finished parsing the GOAWAY header, call frame handler. |
- visitor_->OnGoAway(current_frame_stream_id_, status); |
- } |
- } |
- |
- // Handle remaining data as opaque. |
- bool processed_successfully = true; |
- if (len > 0) { |
- processed_successfully = visitor_->OnGoAwayFrameData(data, len); |
- } |
- remaining_data_length_ -= original_len; |
- if (!processed_successfully) { |
- set_error(SPDY_GOAWAY_FRAME_CORRUPT); |
- } else if (remaining_data_length_ == 0) { |
- // Signal that there is not more opaque data. |
- visitor_->OnGoAwayFrameData(NULL, 0); |
- CHANGE_STATE(SPDY_AUTO_RESET); |
- } |
- return original_len; |
-} |
- |
-size_t SpdyFramer::ProcessRstStreamFramePayload(const char* data, size_t len) { |
- if (len == 0) { |
- return 0; |
- } |
- // Clamp to the actual remaining payload. |
- if (len > remaining_data_length_) { |
- len = remaining_data_length_; |
- } |
- size_t original_len = len; |
- |
- // Check if we had already read enough bytes to parse the fixed-length portion |
- // of the RST_STREAM frame. |
- const size_t header_size = GetRstStreamMinimumSize(); |
- size_t unread_header_bytes = header_size - current_frame_buffer_length_; |
- bool already_parsed_header = (unread_header_bytes == 0); |
- if (!already_parsed_header) { |
- // Buffer the new RST_STREAM header bytes we got. |
- UpdateCurrentFrameBuffer(&data, &len, unread_header_bytes); |
- |
- // Do we have enough to parse the constant size RST_STREAM header? |
- if (current_frame_buffer_length_ == header_size) { |
- // Parse out the last good stream id. |
- SpdyFrameReader reader(current_frame_buffer_.get(), |
- current_frame_buffer_length_); |
- reader.Seek(GetControlFrameHeaderSize()); // Seek past frame header. |
- if (protocol_version() <= SPDY3) { |
- bool successful_read = reader.ReadUInt31(¤t_frame_stream_id_); |
- DCHECK(successful_read); |
- } |
- |
- SpdyRstStreamStatus status = RST_STREAM_INVALID; |
- uint32 status_raw = status; |
- bool successful_read = reader.ReadUInt32(&status_raw); |
- DCHECK(successful_read); |
- if (SpdyConstants::IsValidRstStreamStatus(protocol_version(), |
- status_raw)) { |
- status = |
- SpdyConstants::ParseRstStreamStatus(protocol_version(), status_raw); |
- } else { |
- if (protocol_version() > SPDY3) { |
- // Treat unrecognized status codes as INTERNAL_ERROR as |
- // recommended by the HTTP/2 spec. |
- status = RST_STREAM_INTERNAL_ERROR; |
- } |
- } |
- // Finished parsing the RST_STREAM header, call frame handler. |
- visitor_->OnRstStream(current_frame_stream_id_, status); |
- } |
- } |
- |
- // Handle remaining data as opaque. |
- bool processed_successfully = true; |
- if (len > 0) { |
- processed_successfully = visitor_->OnRstStreamFrameData(data, len); |
- } |
- remaining_data_length_ -= original_len; |
- if (!processed_successfully) { |
- set_error(SPDY_RST_STREAM_FRAME_CORRUPT); |
- } else if (remaining_data_length_ == 0) { |
- // Signal that there is not more opaque data. |
- visitor_->OnRstStreamFrameData(NULL, 0); |
- CHANGE_STATE(SPDY_AUTO_RESET); |
- } |
- 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::ProcessDataFramePaddingLength(const char* data, size_t len) { |
- DCHECK_EQ(SPDY_READ_DATA_FRAME_PADDING_LENGTH, state_); |
- DCHECK_EQ(0u, remaining_padding_payload_length_); |
- DCHECK_EQ(DATA, current_frame_type_); |
- |
- size_t original_len = len; |
- if (current_frame_flags_ & DATA_FLAG_PADDED) { |
- if (len != 0) { |
- if (remaining_data_length_ < kPadLengthFieldSize) { |
- set_error(SPDY_INVALID_DATA_FRAME_FLAGS); |
- return 0; |
- } |
- |
- remaining_padding_payload_length_ = *reinterpret_cast<const uint8*>(data); |
- ++data; |
- --len; |
- --remaining_data_length_; |
- } else { |
- // We don't have the data available for parsing the pad length field. Keep |
- // waiting. |
- return 0; |
- } |
- } |
- |
- if (remaining_padding_payload_length_ > remaining_data_length_) { |
- set_error(SPDY_INVALID_DATA_FRAME_FLAGS); |
- return 0; |
- } |
- CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); |
- return original_len - len; |
-} |
- |
-size_t SpdyFramer::ProcessFramePadding(const char* data, size_t len) { |
- DCHECK_EQ(SPDY_CONSUME_PADDING, state_); |
- |
- size_t original_len = 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); |
- 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); |
- } |
- data += amount_to_discard; |
- len -= amount_to_discard; |
- remaining_padding_payload_length_ -= amount_to_discard; |
- remaining_data_length_ -= amount_to_discard; |
- } |
- |
- if (remaining_data_length_ == 0) { |
- // 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 (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; |
-} |
- |
-size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { |
- size_t original_len = len; |
- if (remaining_data_length_ - remaining_padding_payload_length_ > 0) { |
- size_t amount_to_forward = std::min( |
- remaining_data_length_ - remaining_padding_payload_length_, len); |
- if (amount_to_forward && state_ != SPDY_IGNORE_REMAINING_PAYLOAD) { |
- // Only inform the visitor if there is data. |
- if (amount_to_forward) { |
- visitor_->OnStreamFrameData( |
- current_frame_stream_id_, data, amount_to_forward, false); |
- } |
- } |
- data += amount_to_forward; |
- len -= amount_to_forward; |
- remaining_data_length_ -= amount_to_forward; |
- } |
- |
- if (remaining_data_length_ == remaining_padding_payload_length_) { |
- CHANGE_STATE(SPDY_CONSUME_PADDING); |
- } |
- return original_len - len; |
-} |
- |
-size_t SpdyFramer::ProcessIgnoredControlFramePayload(/*const char* data,*/ |
- size_t len) { |
- size_t original_len = len; |
- if (remaining_data_length_ > 0) { |
- size_t amount_to_ignore = std::min(remaining_data_length_, len); |
- len -= amount_to_ignore; |
- remaining_data_length_ -= amount_to_ignore; |
- } |
- |
- if (remaining_data_length_ == 0) { |
- CHANGE_STATE(SPDY_AUTO_RESET); |
- } |
- return original_len - len; |
-} |
- |
-size_t SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data, |
- size_t header_length, |
- SpdyHeaderBlock* block) const { |
- SpdyFrameReader reader(header_data, header_length); |
- |
- // Read number of headers. |
- uint32 num_headers; |
- if (protocol_version() <= SPDY2) { |
- uint16 temp; |
- if (!reader.ReadUInt16(&temp)) { |
- DVLOG(1) << "Unable to read number of headers."; |
- return 0; |
- } |
- num_headers = temp; |
- } else { |
- if (!reader.ReadUInt32(&num_headers)) { |
- DVLOG(1) << "Unable to read number of headers."; |
- return 0; |
- } |
- } |
- |
- // Read each header. |
- for (uint32 index = 0; index < num_headers; ++index) { |
- base::StringPiece temp; |
- |
- // Read header name. |
- if ((protocol_version() <= SPDY2) ? !reader.ReadStringPiece16(&temp) |
- : !reader.ReadStringPiece32(&temp)) { |
- DVLOG(1) << "Unable to read header name (" << index + 1 << " of " |
- << num_headers << ")."; |
- return 0; |
- } |
- std::string name = temp.as_string(); |
- |
- // Read header value. |
- if ((protocol_version() <= SPDY2) ? !reader.ReadStringPiece16(&temp) |
- : !reader.ReadStringPiece32(&temp)) { |
- DVLOG(1) << "Unable to read header value (" << index + 1 << " of " |
- << num_headers << ")."; |
- return 0; |
- } |
- std::string value = temp.as_string(); |
- |
- // Ensure no duplicates. |
- if (block->find(name) != block->end()) { |
- DVLOG(1) << "Duplicate header '" << name << "' (" << index + 1 << " of " |
- << num_headers << ")."; |
- return 0; |
- } |
- |
- // Store header. |
- (*block)[name] = value; |
- } |
- return reader.GetBytesConsumed(); |
-} |
- |
-SpdySerializedFrame* SpdyFramer::SerializeData( |
- const SpdyDataIR& data_ir) const { |
- uint8 flags = DATA_FLAG_NONE; |
- if (data_ir.fin()) { |
- flags = DATA_FLAG_FIN; |
- } |
- |
- if (protocol_version() > SPDY3) { |
- int num_padding_fields = 0; |
- if (data_ir.padded()) { |
- flags |= DATA_FLAG_PADDED; |
- ++num_padding_fields; |
- } |
- |
- const size_t size_with_padding = num_padding_fields + |
- data_ir.data().length() + data_ir.padding_payload_len() + |
- GetDataFrameMinimumSize(); |
- SpdyFrameBuilder builder(size_with_padding, protocol_version()); |
- builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags); |
- if (data_ir.padded()) { |
- builder.WriteUInt8(data_ir.padding_payload_len() & 0xff); |
- } |
- builder.WriteBytes(data_ir.data().data(), data_ir.data().length()); |
- if (data_ir.padding_payload_len() > 0) { |
- string padding(data_ir.padding_payload_len(), 0); |
- builder.WriteBytes(padding.data(), padding.length()); |
- } |
- DCHECK_EQ(size_with_padding, builder.length()); |
- return builder.take(); |
- } else { |
- const size_t size = GetDataFrameMinimumSize() + data_ir.data().length(); |
- SpdyFrameBuilder builder(size, protocol_version()); |
- builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags); |
- builder.WriteBytes(data_ir.data().data(), data_ir.data().length()); |
- DCHECK_EQ(size, builder.length()); |
- return builder.take(); |
- } |
-} |
- |
-SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField( |
- const SpdyDataIR& data_ir) const { |
- uint8 flags = DATA_FLAG_NONE; |
- if (data_ir.fin()) { |
- flags = DATA_FLAG_FIN; |
- } |
- |
- size_t frame_size = GetDataFrameMinimumSize(); |
- size_t num_padding_fields = 0; |
- if (protocol_version() > SPDY3) { |
- if (data_ir.padded()) { |
- flags |= DATA_FLAG_PADDED; |
- ++num_padding_fields; |
- } |
- frame_size += num_padding_fields; |
- } |
- |
- SpdyFrameBuilder builder(frame_size, protocol_version()); |
- builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags); |
- if (protocol_version() > SPDY3) { |
- if (data_ir.padded()) { |
- builder.WriteUInt8(data_ir.padding_payload_len() & 0xff); |
- } |
- builder.OverwriteLength(*this, num_padding_fields + |
- data_ir.data().length() + data_ir.padding_payload_len()); |
- } else { |
- builder.OverwriteLength(*this, data_ir.data().length()); |
- } |
- DCHECK_EQ(frame_size, builder.length()); |
- return builder.take(); |
-} |
- |
-SpdySerializedFrame* SpdyFramer::SerializeSynStream( |
- const SpdySynStreamIR& syn_stream) { |
- DCHECK_GE(SPDY3, protocol_version()); |
- uint8 flags = 0; |
- if (syn_stream.fin()) { |
- flags |= CONTROL_FLAG_FIN; |
- } |
- if (syn_stream.unidirectional()) { |
- // TODO(hkhalil): invalid for HTTP2. |
- flags |= CONTROL_FLAG_UNIDIRECTIONAL; |
- } |
- |
- // 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. |
- size_t size = GetSynStreamMinimumSize() + |
- GetSerializedLength(syn_stream.name_value_block()); |
- |
- SpdyFrameBuilder builder(size, protocol_version()); |
- builder.WriteControlFrameHeader(*this, SYN_STREAM, flags); |
- builder.WriteUInt32(syn_stream.stream_id()); |
- builder.WriteUInt32(syn_stream.associated_to_stream_id()); |
- builder.WriteUInt8(priority << ((protocol_version() <= SPDY2) ? 6 : 5)); |
- builder.WriteUInt8(0); // Unused byte where credential slot used to be. |
- DCHECK_EQ(GetSynStreamMinimumSize(), builder.length()); |
- SerializeNameValueBlock(&builder, syn_stream); |
- |
- if (debug_visitor_) { |
- const size_t payload_len = |
- GetSerializedLength(protocol_version(), |
- &(syn_stream.name_value_block())); |
- debug_visitor_->OnSendCompressedFrame(syn_stream.stream_id(), |
- SYN_STREAM, |
- payload_len, |
- builder.length()); |
- } |
- |
- return builder.take(); |
-} |
- |
-SpdySerializedFrame* SpdyFramer::SerializeSynReply( |
- const SpdySynReplyIR& syn_reply) { |
- DCHECK_GE(SPDY3, protocol_version()); |
- uint8 flags = 0; |
- if (syn_reply.fin()) { |
- flags |= CONTROL_FLAG_FIN; |
- } |
- |
- // The size of this frame, including variable-length name-value block. |
- const size_t size = GetSynReplyMinimumSize() + |
- GetSerializedLength(syn_reply.name_value_block()); |
- |
- SpdyFrameBuilder builder(size, protocol_version()); |
- if (protocol_version() <= SPDY3) { |
- builder.WriteControlFrameHeader(*this, SYN_REPLY, flags); |
- builder.WriteUInt32(syn_reply.stream_id()); |
- } else { |
- builder.BeginNewFrame(*this, |
- HEADERS, |
- flags, |
- syn_reply.stream_id()); |
- } |
- if (protocol_version() < SPDY3) { |
- builder.WriteUInt16(0); // Unused. |
- } |
- DCHECK_EQ(GetSynReplyMinimumSize(), builder.length()); |
- SerializeNameValueBlock(&builder, syn_reply); |
- |
- if (debug_visitor_) { |
- const size_t payload_len = GetSerializedLength( |
- protocol_version(), &(syn_reply.name_value_block())); |
- debug_visitor_->OnSendCompressedFrame(syn_reply.stream_id(), |
- SYN_REPLY, |
- payload_len, |
- builder.length()); |
- } |
- |
- return builder.take(); |
-} |
- |
-SpdySerializedFrame* SpdyFramer::SerializeRstStream( |
- const SpdyRstStreamIR& rst_stream) const { |
- // TODO(jgraettinger): For now, Chromium will support parsing RST_STREAM |
- // payloads, but will not emit them. SPDY4 is used for draft HTTP/2, |
- // which doesn't currently include RST_STREAM payloads. GFE flags have been |
- // commented but left in place to simplify future patching. |
- // Compute the output buffer size, taking opaque data into account. |
- size_t expected_length = GetRstStreamMinimumSize(); |
- if (protocol_version() > SPDY3) { |
- expected_length += rst_stream.description().size(); |
- } |
- SpdyFrameBuilder builder(expected_length, protocol_version()); |
- |
- // Serialize the RST_STREAM frame. |
- if (protocol_version() <= SPDY3) { |
- builder.WriteControlFrameHeader(*this, RST_STREAM, 0); |
- builder.WriteUInt32(rst_stream.stream_id()); |
- } else { |
- builder.BeginNewFrame(*this, RST_STREAM, 0, rst_stream.stream_id()); |
- } |
- |
- builder.WriteUInt32(SpdyConstants::SerializeRstStreamStatus( |
- protocol_version(), rst_stream.status())); |
- |
- // In SPDY4 and up, RST_STREAM frames may also specify opaque data. |
- if (protocol_version() > SPDY3 && rst_stream.description().size() > 0) { |
- builder.WriteBytes(rst_stream.description().data(), |
- rst_stream.description().size()); |
- } |
- |
- DCHECK_EQ(expected_length, builder.length()); |
- return builder.take(); |
-} |
- |
-SpdySerializedFrame* SpdyFramer::SerializeSettings( |
- const SpdySettingsIR& settings) const { |
- uint8 flags = 0; |
- |
- if (protocol_version() <= SPDY3) { |
- if (settings.clear_settings()) { |
- flags |= SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS; |
- } |
- } else { |
- if (settings.is_ack()) { |
- flags |= SETTINGS_FLAG_ACK; |
- } |
- } |
- const SpdySettingsIR::ValueMap* values = &(settings.values()); |
- |
- size_t setting_size = SpdyConstants::GetSettingSize(protocol_version()); |
- // Size, in bytes, of this SETTINGS frame. |
- const size_t size = GetSettingsMinimumSize() + |
- (values->size() * setting_size); |
- SpdyFrameBuilder builder(size, protocol_version()); |
- if (protocol_version() <= SPDY3) { |
- builder.WriteControlFrameHeader(*this, SETTINGS, flags); |
- } else { |
- builder.BeginNewFrame(*this, SETTINGS, flags, 0); |
- } |
- |
- // If this is an ACK, payload should be empty. |
- if (protocol_version() > SPDY3 && settings.is_ack()) { |
- return builder.take(); |
- } |
- |
- if (protocol_version() <= SPDY3) { |
- builder.WriteUInt32(values->size()); |
- } |
- DCHECK_EQ(GetSettingsMinimumSize(), builder.length()); |
- for (SpdySettingsIR::ValueMap::const_iterator it = values->begin(); |
- it != values->end(); |
- ++it) { |
- int setting_id = |
- SpdyConstants::SerializeSettingId(protocol_version(), it->first); |
- DCHECK_GE(setting_id, 0); |
- if (protocol_version() <= SPDY3) { |
- uint8 setting_flags = 0; |
- 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, setting_id); |
- uint32 id_and_flags_wire = flags_and_id.GetWireFormat(protocol_version()); |
- builder.WriteBytes(&id_and_flags_wire, 4); |
- } else { |
- builder.WriteUInt16(static_cast<uint16>(setting_id)); |
- } |
- builder.WriteUInt32(it->second.value); |
- } |
- DCHECK_EQ(size, builder.length()); |
- return builder.take(); |
-} |
- |
-SpdySerializedFrame* SpdyFramer::SerializePing(const SpdyPingIR& ping) const { |
- SpdyFrameBuilder builder(GetPingSize(), protocol_version()); |
- if (protocol_version() <= SPDY3) { |
- builder.WriteControlFrameHeader(*this, PING, kNoFlags); |
- builder.WriteUInt32(static_cast<uint32>(ping.id())); |
- } else { |
- uint8 flags = 0; |
- if (ping.is_ack()) { |
- flags |= PING_FLAG_ACK; |
- } |
- builder.BeginNewFrame(*this, PING, flags, 0); |
- builder.WriteUInt64(ping.id()); |
- } |
- DCHECK_EQ(GetPingSize(), builder.length()); |
- return builder.take(); |
-} |
- |
-SpdySerializedFrame* SpdyFramer::SerializeGoAway( |
- const SpdyGoAwayIR& goaway) const { |
- |
- // Compute the output buffer size, take opaque data into account. |
- size_t expected_length = GetGoAwayMinimumSize(); |
- if (protocol_version() > SPDY3) { |
- expected_length += goaway.description().size(); |
- } |
- SpdyFrameBuilder builder(expected_length, protocol_version()); |
- |
- // Serialize the GOAWAY frame. |
- if (protocol_version() <= SPDY3) { |
- builder.WriteControlFrameHeader(*this, GOAWAY, kNoFlags); |
- } else { |
- builder.BeginNewFrame(*this, GOAWAY, 0, 0); |
- } |
- |
- // GOAWAY frames specify the last good stream id for all SPDY versions. |
- builder.WriteUInt32(goaway.last_good_stream_id()); |
- |
- // In SPDY3 and up, GOAWAY frames also specify the error status code. |
- if (protocol_version() >= SPDY3) { |
- // TODO(jgraettinger): Merge back to server-side. |
- builder.WriteUInt32(SpdyConstants::SerializeGoAwayStatus(protocol_version(), |
- goaway.status())); |
- } |
- |
- // In SPDY4 and up, GOAWAY frames may also specify opaque data. |
- if ((protocol_version() > SPDY3) && (goaway.description().size() > 0)) { |
- builder.WriteBytes(goaway.description().data(), |
- goaway.description().size()); |
- } |
- |
- DCHECK_EQ(expected_length, builder.length()); |
- return builder.take(); |
-} |
- |
-SpdySerializedFrame* SpdyFramer::SerializeHeaders( |
- const SpdyHeadersIR& headers) { |
- uint8 flags = 0; |
- if (headers.fin()) { |
- flags |= CONTROL_FLAG_FIN; |
- } |
- if (protocol_version() > SPDY3) { |
- // This will get overwritten if we overflow into a CONTINUATION frame. |
- flags |= HEADERS_FLAG_END_HEADERS; |
- if (headers.has_priority()) { |
- flags |= HEADERS_FLAG_PRIORITY; |
- } |
- if (headers.padded()) { |
- flags |= HEADERS_FLAG_PADDED; |
- } |
- } |
- |
- // The size of this frame, including padding (if there is any) |
- // and variable-length name-value block. |
- size_t size = GetHeadersMinimumSize(); |
- |
- if (protocol_version() > SPDY3 && headers.padded()) { |
- size += kPadLengthFieldSize; |
- size += headers.padding_payload_len(); |
- } |
- |
- SpdyPriority priority = static_cast<SpdyPriority>(headers.priority()); |
- if (headers.has_priority()) { |
- if (headers.priority() > GetLowestPriority()) { |
- DLOG(DFATAL) << "Priority out-of-bounds."; |
- priority = GetLowestPriority(); |
- } |
- size += 5; |
- } |
- |
- string hpack_encoding; |
- if (protocol_version() > SPDY3) { |
- if (enable_compression_) { |
- GetHpackEncoder()->EncodeHeaderSet( |
- headers.name_value_block(), &hpack_encoding); |
- } else { |
- GetHpackEncoder()->EncodeHeaderSetWithoutCompression( |
- headers.name_value_block(), &hpack_encoding); |
- } |
- size += hpack_encoding.size(); |
- if (size > kMaxControlFrameSize) { |
- size += GetNumberRequiredContinuationFrames(size) * |
- GetContinuationMinimumSize(); |
- flags &= ~HEADERS_FLAG_END_HEADERS; |
- } |
- } else { |
- size += GetSerializedLength(headers.name_value_block()); |
- } |
- |
- SpdyFrameBuilder builder(size, protocol_version()); |
- if (protocol_version() <= SPDY3) { |
- builder.WriteControlFrameHeader(*this, HEADERS, flags); |
- builder.WriteUInt32(headers.stream_id()); |
- } else { |
- builder.BeginNewFrame(*this, |
- HEADERS, |
- flags, |
- headers.stream_id()); |
- } |
- if (protocol_version() <= SPDY2) { |
- builder.WriteUInt16(0); // Unused. |
- } |
- DCHECK_EQ(GetHeadersMinimumSize(), builder.length()); |
- |
- if (protocol_version() > SPDY3) { |
- int padding_payload_len = 0; |
- if (headers.padded()) { |
- builder.WriteUInt8(headers.padding_payload_len()); |
- padding_payload_len = headers.padding_payload_len(); |
- } |
- if (headers.has_priority()) { |
- // TODO(jgraettinger): Plumb priorities and stream dependencies. |
- builder.WriteUInt32(0); // Non-exclusive bit and root stream ID. |
- builder.WriteUInt8(MapPriorityToWeight(priority)); |
- } |
- WritePayloadWithContinuation(&builder, |
- hpack_encoding, |
- headers.stream_id(), |
- HEADERS, |
- padding_payload_len); |
- } else { |
- SerializeNameValueBlock(&builder, headers); |
- } |
- |
- if (debug_visitor_) { |
- // SPDY4 uses HPACK for header compression. However, continue to |
- // use GetSerializedLength() for an apples-to-apples comparision of |
- // compression performance between HPACK and SPDY w/ deflate. |
- const size_t payload_len = |
- GetSerializedLength(protocol_version(), |
- &(headers.name_value_block())); |
- debug_visitor_->OnSendCompressedFrame(headers.stream_id(), |
- HEADERS, |
- payload_len, |
- builder.length()); |
- } |
- |
- return builder.take(); |
-} |
- |
-SpdySerializedFrame* SpdyFramer::SerializeWindowUpdate( |
- const SpdyWindowUpdateIR& window_update) const { |
- SpdyFrameBuilder builder(GetWindowUpdateSize(), protocol_version()); |
- if (protocol_version() <= SPDY3) { |
- builder.WriteControlFrameHeader(*this, WINDOW_UPDATE, kNoFlags); |
- builder.WriteUInt32(window_update.stream_id()); |
- } else { |
- builder.BeginNewFrame(*this, |
- WINDOW_UPDATE, |
- kNoFlags, |
- window_update.stream_id()); |
- } |
- builder.WriteUInt32(window_update.delta()); |
- DCHECK_EQ(GetWindowUpdateSize(), builder.length()); |
- return builder.take(); |
-} |
- |
-SpdyFrame* SpdyFramer::SerializeBlocked(const SpdyBlockedIR& blocked) const { |
- DCHECK_LT(SPDY3, protocol_version()); |
- SpdyFrameBuilder builder(GetBlockedSize(), protocol_version()); |
- builder.BeginNewFrame(*this, BLOCKED, kNoFlags, blocked.stream_id()); |
- return builder.take(); |
-} |
- |
-SpdyFrame* SpdyFramer::SerializePushPromise( |
- const SpdyPushPromiseIR& push_promise) { |
- DCHECK_LT(SPDY3, protocol_version()); |
- uint8 flags = 0; |
- // This will get overwritten if we overflow into a CONTINUATION frame. |
- flags |= PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
- // The size of this frame, including variable-length name-value block. |
- size_t size = GetPushPromiseMinimumSize(); |
- |
- if (push_promise.padded()) { |
- flags |= PUSH_PROMISE_FLAG_PADDED; |
- size += kPadLengthFieldSize; |
- size += push_promise.padding_payload_len(); |
- } |
- |
- string hpack_encoding; |
- if (enable_compression_) { |
- GetHpackEncoder()->EncodeHeaderSet( |
- push_promise.name_value_block(), &hpack_encoding); |
- } else { |
- GetHpackEncoder()->EncodeHeaderSetWithoutCompression( |
- push_promise.name_value_block(), &hpack_encoding); |
- } |
- size += hpack_encoding.size(); |
- if (size > kMaxControlFrameSize) { |
- size += GetNumberRequiredContinuationFrames(size) * |
- GetContinuationMinimumSize(); |
- flags &= ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
- } |
- |
- SpdyFrameBuilder builder(size, protocol_version()); |
- builder.BeginNewFrame(*this, |
- PUSH_PROMISE, |
- flags, |
- push_promise.stream_id()); |
- int padding_payload_len = 0; |
- if (push_promise.padded()) { |
- builder.WriteUInt8(push_promise.padding_payload_len()); |
- builder.WriteUInt32(push_promise.promised_stream_id()); |
- DCHECK_EQ(GetPushPromiseMinimumSize() + kPadLengthFieldSize, |
- builder.length()); |
- |
- padding_payload_len = push_promise.padding_payload_len(); |
- } else { |
- builder.WriteUInt32(push_promise.promised_stream_id()); |
- DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length()); |
- } |
- |
- WritePayloadWithContinuation(&builder, |
- hpack_encoding, |
- push_promise.stream_id(), |
- PUSH_PROMISE, |
- padding_payload_len); |
- |
- if (debug_visitor_) { |
- // SPDY4 uses HPACK for header compression. However, continue to |
- // use GetSerializedLength() for an apples-to-apples comparision of |
- // compression performance between HPACK and SPDY w/ deflate. |
- const size_t payload_len = |
- GetSerializedLength(protocol_version(), |
- &(push_promise.name_value_block())); |
- debug_visitor_->OnSendCompressedFrame(push_promise.stream_id(), |
- PUSH_PROMISE, |
- payload_len, |
- builder.length()); |
- } |
- |
- return builder.take(); |
-} |
- |
-// TODO(jgraettinger): This implementation is incorrect. The continuation |
-// frame continues a previously-begun HPACK encoding; it doesn't begin a |
-// new one. Figure out whether it makes sense to keep SerializeContinuation(). |
-SpdyFrame* SpdyFramer::SerializeContinuation( |
- const SpdyContinuationIR& continuation) { |
- CHECK_LT(SPDY3, protocol_version()); |
- uint8 flags = 0; |
- if (continuation.end_headers()) { |
- flags |= HEADERS_FLAG_END_HEADERS; |
- } |
- |
- // The size of this frame, including variable-length name-value block. |
- size_t size = GetContinuationMinimumSize(); |
- string hpack_encoding; |
- if (enable_compression_) { |
- GetHpackEncoder()->EncodeHeaderSet( |
- continuation.name_value_block(), &hpack_encoding); |
- } else { |
- GetHpackEncoder()->EncodeHeaderSetWithoutCompression( |
- continuation.name_value_block(), &hpack_encoding); |
- } |
- size += hpack_encoding.size(); |
- |
- SpdyFrameBuilder builder(size, protocol_version()); |
- builder.BeginNewFrame(*this, CONTINUATION, flags, |
- continuation.stream_id()); |
- DCHECK_EQ(GetContinuationMinimumSize(), builder.length()); |
- |
- builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size()); |
- 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()); |
- |
- // TODO(bnc): http://crbug.com/438263 |
- // Update the binary format here to the new text-based payload format. |
- builder.WriteUInt32(altsvc.max_age()); |
- builder.WriteUInt16(altsvc.port()); |
- builder.WriteUInt8(0); // Reserved. |
- builder.WriteUInt8(static_cast<uint8>(altsvc.protocol_id().length())); |
- builder.WriteBytes(altsvc.protocol_id().data(), |
- altsvc.protocol_id().length()); |
- builder.WriteUInt8(static_cast<uint8>(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(); |
-} |
- |
-SpdyFrame* SpdyFramer::SerializePriority(const SpdyPriorityIR& priority) const { |
- DCHECK_LT(SPDY3, protocol_version()); |
- size_t size = GetPrioritySize(); |
- |
- SpdyFrameBuilder builder(size, protocol_version()); |
- builder.BeginNewFrame(*this, PRIORITY, kNoFlags, priority.stream_id()); |
- |
- // Make sure the highest-order bit in the parent stream id is zeroed out. |
- uint32 parent_stream_id = priority.parent_stream_id() & 0x7fffffff; |
- uint32 exclusive = priority.exclusive() ? 0x80000000 : 0; |
- // Set the one-bit exclusivity flag. |
- uint32 flag_and_parent_id = parent_stream_id | exclusive; |
- builder.WriteUInt32(flag_and_parent_id); |
- builder.WriteUInt8(priority.weight()); |
- DCHECK_EQ(GetPrioritySize(), builder.length()); |
- return builder.take(); |
-} |
- |
-namespace { |
- |
-class FrameSerializationVisitor : public SpdyFrameVisitor { |
- public: |
- explicit FrameSerializationVisitor(SpdyFramer* framer) : framer_(framer) {} |
- ~FrameSerializationVisitor() override {} |
- |
- SpdySerializedFrame* ReleaseSerializedFrame() { return frame_.release(); } |
- |
- void VisitData(const SpdyDataIR& data) override { |
- frame_.reset(framer_->SerializeData(data)); |
- } |
- void VisitSynStream(const SpdySynStreamIR& syn_stream) override { |
- frame_.reset(framer_->SerializeSynStream(syn_stream)); |
- } |
- void VisitSynReply(const SpdySynReplyIR& syn_reply) override { |
- frame_.reset(framer_->SerializeSynReply(syn_reply)); |
- } |
- void VisitRstStream(const SpdyRstStreamIR& rst_stream) override { |
- frame_.reset(framer_->SerializeRstStream(rst_stream)); |
- } |
- void VisitSettings(const SpdySettingsIR& settings) override { |
- frame_.reset(framer_->SerializeSettings(settings)); |
- } |
- void VisitPing(const SpdyPingIR& ping) override { |
- frame_.reset(framer_->SerializePing(ping)); |
- } |
- void VisitGoAway(const SpdyGoAwayIR& goaway) override { |
- frame_.reset(framer_->SerializeGoAway(goaway)); |
- } |
- void VisitHeaders(const SpdyHeadersIR& headers) override { |
- frame_.reset(framer_->SerializeHeaders(headers)); |
- } |
- void VisitWindowUpdate(const SpdyWindowUpdateIR& window_update) override { |
- frame_.reset(framer_->SerializeWindowUpdate(window_update)); |
- } |
- void VisitBlocked(const SpdyBlockedIR& blocked) override { |
- frame_.reset(framer_->SerializeBlocked(blocked)); |
- } |
- void VisitPushPromise(const SpdyPushPromiseIR& push_promise) override { |
- frame_.reset(framer_->SerializePushPromise(push_promise)); |
- } |
- void VisitContinuation(const SpdyContinuationIR& continuation) override { |
- frame_.reset(framer_->SerializeContinuation(continuation)); |
- } |
- void VisitAltSvc(const SpdyAltSvcIR& altsvc) override { |
- frame_.reset(framer_->SerializeAltSvc(altsvc)); |
- } |
- void VisitPriority(const SpdyPriorityIR& priority) override { |
- frame_.reset(framer_->SerializePriority(priority)); |
- } |
- |
- private: |
- SpdyFramer* framer_; |
- scoped_ptr<SpdySerializedFrame> frame_; |
-}; |
- |
-} // namespace |
- |
-SpdySerializedFrame* SpdyFramer::SerializeFrame(const SpdyFrameIR& frame) { |
- FrameSerializationVisitor visitor(this); |
- frame.Visit(&visitor); |
- return visitor.ReleaseSerializedFrame(); |
-} |
- |
-size_t SpdyFramer::GetSerializedLength(const SpdyHeaderBlock& headers) { |
- CHECK_GE(SPDY3, protocol_version()); |
- const size_t uncompressed_length = |
- GetSerializedLength(protocol_version(), &headers); |
- if (!enable_compression_) { |
- return uncompressed_length; |
- } |
- z_stream* compressor = GetHeaderCompressor(); |
- // Since we'll be performing lots of flushes when compressing the data, |
- // zlib's lower bounds may be insufficient. |
- return 2 * deflateBound(compressor, uncompressed_length); |
-} |
- |
-size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) { |
- DCHECK_GT(protocol_version(), SPDY3); |
- DCHECK_GT(size, kMaxControlFrameSize); |
- size_t overflow = size - kMaxControlFrameSize; |
- return overflow / (kMaxControlFrameSize - GetContinuationMinimumSize()) + 1; |
-} |
- |
-void SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder, |
- const string& hpack_encoding, |
- SpdyStreamId stream_id, |
- SpdyFrameType type, |
- int padding_payload_len) { |
- uint8 end_flag = 0; |
- uint8 flags = 0; |
- if (type == HEADERS) { |
- end_flag = HEADERS_FLAG_END_HEADERS; |
- } else if (type == PUSH_PROMISE) { |
- end_flag = PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
- } else { |
- DLOG(FATAL) << "CONTINUATION frames cannot be used with frame type " |
- << FrameTypeToString(type); |
- } |
- |
- // Write all the padding payload and as much of the data payload as possible |
- // into the initial frame. |
- size_t bytes_remaining = 0; |
- bytes_remaining = |
- hpack_encoding.size() - |
- std::min(hpack_encoding.size(), |
- kMaxControlFrameSize - builder->length() - padding_payload_len); |
- builder->WriteBytes(&hpack_encoding[0], |
- hpack_encoding.size() - bytes_remaining); |
- if (padding_payload_len > 0) { |
- string padding = string(padding_payload_len, 0); |
- builder->WriteBytes(padding.data(), padding.length()); |
- } |
- if (bytes_remaining > 0) { |
- builder->OverwriteLength( |
- *this, kMaxControlFrameSize - GetControlFrameHeaderSize()); |
- } |
- |
- // Tack on CONTINUATION frames for the overflow. |
- while (bytes_remaining > 0) { |
- size_t bytes_to_write = std::min( |
- bytes_remaining, kMaxControlFrameSize - GetContinuationMinimumSize()); |
- // Write CONTINUATION frame prefix. |
- if (bytes_remaining == bytes_to_write) { |
- flags |= end_flag; |
- } |
- builder->BeginNewFrame(*this, CONTINUATION, flags, stream_id); |
- // Write payload fragment. |
- builder->WriteBytes( |
- &hpack_encoding[hpack_encoding.size() - bytes_remaining], |
- bytes_to_write); |
- bytes_remaining -= bytes_to_write; |
- } |
-} |
- |
-// The following compression setting are based on Brian Olson's analysis. See |
-// https://groups.google.com/group/spdy-dev/browse_thread/thread/dfaf498542fac792 |
-// for more details. |
-#if defined(USE_SYSTEM_ZLIB) |
-// System zlib is not expected to have workaround for http://crbug.com/139744, |
-// so disable compression in that case. |
-// TODO(phajdan.jr): Remove the special case when it's no longer necessary. |
-static const int kCompressorLevel = 0; |
-#else // !defined(USE_SYSTEM_ZLIB) |
-static const int kCompressorLevel = 9; |
-#endif // !defined(USE_SYSTEM_ZLIB) |
-static const int kCompressorWindowSizeInBits = 11; |
-static const int kCompressorMemLevel = 1; |
- |
-z_stream* SpdyFramer::GetHeaderCompressor() { |
- if (header_compressor_.get()) |
- return header_compressor_.get(); // Already initialized. |
- |
- header_compressor_.reset(new z_stream); |
- memset(header_compressor_.get(), 0, sizeof(z_stream)); |
- |
- int success = deflateInit2(header_compressor_.get(), |
- kCompressorLevel, |
- Z_DEFLATED, |
- kCompressorWindowSizeInBits, |
- kCompressorMemLevel, |
- Z_DEFAULT_STRATEGY); |
- if (success == Z_OK) { |
- const char* dictionary = (protocol_version() <= SPDY2) ? |
- kV2Dictionary : kV3Dictionary; |
- const int dictionary_size = (protocol_version() <= SPDY2) ? |
- kV2DictionarySize : kV3DictionarySize; |
- success = deflateSetDictionary(header_compressor_.get(), |
- reinterpret_cast<const Bytef*>(dictionary), |
- dictionary_size); |
- } |
- if (success != Z_OK) { |
- LOG(WARNING) << "deflateSetDictionary failure: " << success; |
- header_compressor_.reset(NULL); |
- return NULL; |
- } |
- return header_compressor_.get(); |
-} |
- |
-z_stream* SpdyFramer::GetHeaderDecompressor() { |
- if (header_decompressor_.get()) |
- return header_decompressor_.get(); // Already initialized. |
- |
- header_decompressor_.reset(new z_stream); |
- memset(header_decompressor_.get(), 0, sizeof(z_stream)); |
- |
- int success = inflateInit(header_decompressor_.get()); |
- if (success != Z_OK) { |
- LOG(WARNING) << "inflateInit failure: " << success; |
- header_decompressor_.reset(NULL); |
- return NULL; |
- } |
- return header_decompressor_.get(); |
-} |
- |
-HpackEncoder* SpdyFramer::GetHpackEncoder() { |
- DCHECK_LT(SPDY3, protocol_version()); |
- if (hpack_encoder_.get() == nullptr) { |
- hpack_encoder_.reset(new HpackEncoder(ObtainHpackHuffmanTable())); |
- } |
- return hpack_encoder_.get(); |
-} |
- |
-HpackDecoder* SpdyFramer::GetHpackDecoder() { |
- DCHECK_LT(SPDY3, protocol_version()); |
- if (hpack_decoder_.get() == nullptr) { |
- hpack_decoder_.reset(new HpackDecoder(ObtainHpackHuffmanTable())); |
- } |
- return hpack_decoder_.get(); |
-} |
- |
-uint8 SpdyFramer::MapPriorityToWeight(SpdyPriority priority) { |
- const float kSteps = 255.9f / 7.f; |
- return static_cast<uint8>(kSteps * (7.f - priority)); |
-} |
- |
-SpdyPriority SpdyFramer::MapWeightToPriority(uint8 weight) { |
- const float kSteps = 255.9f / 7.f; |
- return static_cast<SpdyPriority>(7.f - weight / kSteps); |
-} |
- |
-// 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 |
-// run out of data to deliver. |
-bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData( |
- SpdyStreamId stream_id, |
- const char* data, |
- size_t len) { |
- // Get a decompressor or set error. |
- z_stream* decomp = GetHeaderDecompressor(); |
- if (decomp == NULL) { |
- LOG(DFATAL) << "Couldn't get decompressor for handling compressed headers."; |
- set_error(SPDY_DECOMPRESS_FAILURE); |
- return false; |
- } |
- |
- bool processed_successfully = true; |
- char buffer[kHeaderDataChunkMaxSize]; |
- |
- decomp->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data)); |
- decomp->avail_in = len; |
- // If we get a SYN_STREAM/SYN_REPLY/HEADERS frame with stream ID zero, we |
- // signal an error back in ProcessControlFrameBeforeHeaderBlock. So if we've |
- // reached this method successfully, stream_id should be nonzero. |
- DCHECK_LT(0u, stream_id); |
- while (decomp->avail_in > 0 && processed_successfully) { |
- decomp->next_out = reinterpret_cast<Bytef*>(buffer); |
- decomp->avail_out = arraysize(buffer); |
- |
- int rv = inflate(decomp, Z_SYNC_FLUSH); |
- if (rv == Z_NEED_DICT) { |
- const char* dictionary = (protocol_version() <= SPDY2) ? kV2Dictionary |
- : kV3Dictionary; |
- const int dictionary_size = (protocol_version() <= SPDY2) ? |
- kV2DictionarySize : kV3DictionarySize; |
- const DictionaryIds& ids = g_dictionary_ids.Get(); |
- const uLong dictionary_id = (protocol_version() <= SPDY2) ? |
- ids.v2_dictionary_id : ids.v3_dictionary_id; |
- // Need to try again with the right dictionary. |
- if (decomp->adler == dictionary_id) { |
- rv = inflateSetDictionary(decomp, |
- reinterpret_cast<const Bytef*>(dictionary), |
- dictionary_size); |
- if (rv == Z_OK) |
- rv = inflate(decomp, Z_SYNC_FLUSH); |
- } |
- } |
- |
- // Inflate will generate a Z_BUF_ERROR if it runs out of input |
- // without producing any output. The input is consumed and |
- // buffered internally by zlib so we can detect this condition by |
- // checking if avail_in is 0 after the call to inflate. |
- bool input_exhausted = ((rv == Z_BUF_ERROR) && (decomp->avail_in == 0)); |
- if ((rv == Z_OK) || input_exhausted) { |
- size_t decompressed_len = arraysize(buffer) - decomp->avail_out; |
- if (decompressed_len > 0) { |
- processed_successfully = visitor_->OnControlFrameHeaderData( |
- stream_id, buffer, decompressed_len); |
- } |
- if (!processed_successfully) { |
- // Assume that the problem was the header block was too large for the |
- // visitor. |
- set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); |
- } |
- } else { |
- DLOG(WARNING) << "inflate failure: " << rv << " " << len; |
- set_error(SPDY_DECOMPRESS_FAILURE); |
- processed_successfully = false; |
- } |
- } |
- return processed_successfully; |
-} |
- |
-bool SpdyFramer::IncrementallyDeliverControlFrameHeaderData( |
- SpdyStreamId stream_id, const char* data, size_t len) { |
- bool read_successfully = true; |
- while (read_successfully && len > 0) { |
- size_t bytes_to_deliver = std::min(len, kHeaderDataChunkMaxSize); |
- read_successfully = visitor_->OnControlFrameHeaderData(stream_id, data, |
- bytes_to_deliver); |
- data += bytes_to_deliver; |
- len -= bytes_to_deliver; |
- if (!read_successfully) { |
- // Assume that the problem was the header block was too large for the |
- // visitor. |
- set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); |
- } |
- } |
- return read_successfully; |
-} |
- |
-void SpdyFramer::SerializeNameValueBlockWithoutCompression( |
- SpdyFrameBuilder* builder, |
- const SpdyNameValueBlock& name_value_block) const { |
- // Serialize number of headers. |
- if (protocol_version() <= SPDY2) { |
- builder->WriteUInt16(static_cast<uint16>(name_value_block.size())); |
- } else { |
- builder->WriteUInt32(name_value_block.size()); |
- } |
- |
- // Serialize each header. |
- for (SpdyHeaderBlock::const_iterator it = name_value_block.begin(); |
- it != name_value_block.end(); |
- ++it) { |
- if (protocol_version() <= SPDY2) { |
- builder->WriteString(it->first); |
- builder->WriteString(it->second); |
- } else { |
- builder->WriteStringPiece32(it->first); |
- builder->WriteStringPiece32(it->second); |
- } |
- } |
-} |
- |
-void SpdyFramer::SerializeNameValueBlock( |
- SpdyFrameBuilder* builder, |
- const SpdyFrameWithNameValueBlockIR& frame) { |
- CHECK_GE(SPDY3, protocol_version()); |
- if (!enable_compression_) { |
- return SerializeNameValueBlockWithoutCompression(builder, |
- frame.name_value_block()); |
- } |
- |
- // First build an uncompressed version to be fed into the compressor. |
- const size_t uncompressed_len = GetSerializedLength( |
- protocol_version(), &(frame.name_value_block())); |
- SpdyFrameBuilder uncompressed_builder(uncompressed_len, protocol_version()); |
- SerializeNameValueBlockWithoutCompression(&uncompressed_builder, |
- frame.name_value_block()); |
- scoped_ptr<SpdyFrame> uncompressed_payload(uncompressed_builder.take()); |
- |
- z_stream* compressor = GetHeaderCompressor(); |
- if (!compressor) { |
- LOG(DFATAL) << "Could not obtain compressor."; |
- return; |
- } |
- // Create an output frame. |
- // Since we'll be performing lots of flushes when compressing the data, |
- // zlib's lower bounds may be insufficient. |
- // |
- // TODO(akalin): Avoid the duplicate calculation with |
- // GetSerializedLength(const SpdyHeaderBlock&). |
- const int compressed_max_size = |
- 2 * deflateBound(compressor, uncompressed_len); |
- |
- // TODO(phajdan.jr): Clean up after we no longer need |
- // to workaround http://crbug.com/139744. |
-#if defined(USE_SYSTEM_ZLIB) |
- compressor->next_in = reinterpret_cast<Bytef*>(uncompressed_payload->data()); |
- compressor->avail_in = uncompressed_len; |
-#endif // defined(USE_SYSTEM_ZLIB) |
- compressor->next_out = reinterpret_cast<Bytef*>( |
- builder->GetWritableBuffer(compressed_max_size)); |
- compressor->avail_out = compressed_max_size; |
- |
- // TODO(phajdan.jr): Clean up after we no longer need |
- // to workaround http://crbug.com/139744. |
-#if defined(USE_SYSTEM_ZLIB) |
- int rv = deflate(compressor, Z_SYNC_FLUSH); |
- if (rv != Z_OK) { // How can we know that it compressed everything? |
- // This shouldn't happen, right? |
- LOG(WARNING) << "deflate failure: " << rv; |
- // TODO(akalin): Upstream this return. |
- return; |
- } |
-#else |
- WriteHeaderBlockToZ(&frame.name_value_block(), compressor); |
-#endif // defined(USE_SYSTEM_ZLIB) |
- |
- int compressed_size = compressed_max_size - compressor->avail_out; |
- builder->Seek(compressed_size); |
- builder->RewriteLength(*this); |
-} |
- |
-} // namespace net |