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