blob: 9ea4e73fe837fa4ad66d9a3cacb298b76dff85e1 [file] [log] [blame]
// 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 <string.h>
#include <algorithm>
#include <cctype>
#include <ios>
#include <iterator>
#include <list>
#include <memory>
#include <new>
#include <string>
#include <vector>
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "net/quic/core/quic_flags.h"
#include "net/spdy/hpack/hpack_constants.h"
#include "net/spdy/hpack/hpack_decoder.h"
#include "net/spdy/hpack/hpack_decoder3.h"
#include "net/spdy/http2_frame_decoder_adapter.h"
#include "net/spdy/platform/api/spdy_estimate_memory_usage.h"
#include "net/spdy/spdy_bitmasks.h"
#include "net/spdy/spdy_bug_tracker.h"
#include "net/spdy/spdy_flags.h"
#include "net/spdy/spdy_frame_builder.h"
#include "net/spdy/spdy_frame_reader.h"
#include "net/spdy/spdy_framer_decoder_adapter.h"
using std::hex;
using std::string;
using std::vector;
namespace net {
namespace {
// Pack parent stream ID and exclusive flag into the format used by HTTP/2
// headers and priority frames.
uint32_t PackStreamDependencyValues(bool exclusive,
SpdyStreamId parent_stream_id) {
// Make sure the highest-order bit in the parent stream id is zeroed out.
uint32_t parent = parent_stream_id & 0x7fffffff;
// Set the one-bit exclusivity flag.
uint32_t e_bit = exclusive ? 0x80000000 : 0;
return parent | e_bit;
}
// Unpack parent stream ID and exclusive flag from the format used by HTTP/2
// headers and priority frames.
void UnpackStreamDependencyValues(uint32_t packed,
bool* exclusive,
SpdyStreamId* parent_stream_id) {
*exclusive = (packed >> 31) != 0;
// Zero out the highest-order bit to get the parent stream id.
*parent_stream_id = packed & 0x7fffffff;
}
// Creates a SpdyFramerDecoderAdapter if flags indicate that one should be
// used. This code is isolated to hopefully make merging into Chromium easier.
std::unique_ptr<SpdyFramerDecoderAdapter> DecoderAdapterFactory(
SpdyFramer* outer) {
if (FLAGS_use_nested_spdy_framer_decoder) {
// Since chromium_reloadable_flag_spdy_use_http2_frame_decoder_adapter can
// be flipped on in any test when all the feature flags are on,
// it can unintentionally override use_nested_spdy_framer_decoder which is
// used to validate that the adapter technique is working. Therefore, we
// give precedence to use_nested_spdy_framer_decoder.
if (FLAGS_chromium_http2_flag_spdy_use_http2_frame_decoder_adapter) {
VLOG(1) << "Both NestedSpdyFramerDecoder and Http2FrameDecoderAdapter "
<< "are enabled. NestedSpdyFramerDecoder selected.";
}
DVLOG(1) << "Creating NestedSpdyFramerDecoder.";
return CreateNestedSpdyFramerDecoder(outer);
}
if (FLAGS_chromium_http2_flag_spdy_use_http2_frame_decoder_adapter) {
DVLOG(1) << "Creating Http2FrameDecoderAdapter.";
return CreateHttp2FrameDecoderAdapter(outer);
}
return nullptr;
}
// Used to indicate no flags in a HTTP2 flags field.
const uint8_t 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;
// Even though the length field is 24 bits, we keep this 16 kB
// limit on control frame size for legacy reasons and to
// mitigate DOS attacks.
const size_t SpdyFramer::kMaxControlFrameSize = (1 << 14) - 1;
const size_t SpdyFramer::kMaxDataPayloadSendSize = 1 << 14;
// The size of the control frame buffer. Must be >= the minimum size of the
// largest control frame.
const size_t SpdyFramer::kControlFrameBufferSize = 19;
const size_t SpdyFramer::kOneSettingParameterSize = 6;
#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
bool SpdyFramerVisitorInterface::OnGoAwayFrameData(const char* goaway_data,
size_t len) {
return true;
}
SpdyFramer::SpdyFramer(SpdyFramer::DecoderAdapterFactoryFn adapter_factory,
CompressionOption option)
: current_frame_buffer_(kControlFrameBufferSize),
expect_continuation_(0),
visitor_(nullptr),
extension_(nullptr),
debug_visitor_(nullptr),
header_handler_(nullptr),
compression_option_(option),
probable_http_response_(false),
end_stream_when_done_(false) {
static_assert(
kMaxControlFrameSize <= kSpdyInitialFrameSizeLimit + kFrameHeaderSize,
"Our send limit should be at most our receive limit");
Reset();
if (adapter_factory != nullptr) {
decoder_adapter_ = adapter_factory(this);
}
skip_rewritelength_ = FLAGS_chromium_http2_flag_remove_rewritelength;
}
SpdyFramer::SpdyFramer(CompressionOption option)
: SpdyFramer(&DecoderAdapterFactory, option) {}
SpdyFramer::~SpdyFramer() {}
void SpdyFramer::Reset() {
if (decoder_adapter_ != nullptr) {
decoder_adapter_->Reset();
}
state_ = SPDY_READY_FOR_FRAME;
previous_state_ = SPDY_READY_FOR_FRAME;
spdy_framer_error_ = SPDY_NO_ERROR;
remaining_data_length_ = 0;
remaining_control_header_ = 0;
current_frame_buffer_.Rewind();
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;
}
void SpdyFramer::set_visitor(SpdyFramerVisitorInterface* visitor) {
if (decoder_adapter_ != nullptr) {
decoder_adapter_->set_visitor(visitor);
}
visitor_ = visitor;
}
void SpdyFramer::set_extension_visitor(ExtensionVisitorInterface* extension) {
extension_ = extension;
}
void SpdyFramer::set_debug_visitor(
SpdyFramerDebugVisitorInterface* debug_visitor) {
if (decoder_adapter_ != nullptr) {
decoder_adapter_->set_debug_visitor(debug_visitor);
}
debug_visitor_ = debug_visitor;
}
void SpdyFramer::set_process_single_input_frame(bool v) {
if (decoder_adapter_ != nullptr) {
decoder_adapter_->set_process_single_input_frame(v);
}
process_single_input_frame_ = v;
}
bool SpdyFramer::probable_http_response() const {
if (decoder_adapter_) {
return decoder_adapter_->probable_http_response();
}
return probable_http_response_;
}
SpdyFramer::SpdyFramerError SpdyFramer::spdy_framer_error() const {
if (decoder_adapter_ != nullptr) {
return decoder_adapter_->spdy_framer_error();
}
return spdy_framer_error_;
}
SpdyFramer::SpdyState SpdyFramer::state() const {
if (decoder_adapter_ != nullptr) {
return decoder_adapter_->state();
}
return state_;
}
size_t SpdyFramer::GetDataFrameMinimumSize() const {
return kDataFrameMinimumSize;
}
// Size, in bytes, of the control frame header.
size_t SpdyFramer::GetFrameHeaderSize() const {
return kFrameHeaderSize;
}
size_t SpdyFramer::GetRstStreamSize() const {
// Size, in bytes, of a RST_STREAM frame.
// Calculated as:
// frame prefix + 4 (status code)
return GetFrameHeaderSize() + 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.
return GetFrameHeaderSize();
}
size_t SpdyFramer::GetPingSize() const {
// Size, in bytes, of this PING frame.
// Calculated as:
// control frame header + 8 (id)
return GetFrameHeaderSize() + 8;
}
size_t SpdyFramer::GetGoAwayMinimumSize() const {
// Size, in bytes, of this GOAWAY frame. Calculated as:
// Control frame header + last stream id (4 bytes) + error code (4 bytes).
return GetFrameHeaderSize() + 8;
}
size_t SpdyFramer::GetHeadersMinimumSize() const {
// Size, in bytes, of a HEADERS frame not including the variable-length
// header block.
return GetFrameHeaderSize();
}
size_t SpdyFramer::GetWindowUpdateSize() const {
// Size, in bytes, of a WINDOW_UPDATE frame.
// Calculated as:
// frame prefix + 4 (delta)
return GetFrameHeaderSize() + 4;
}
size_t SpdyFramer::GetBlockedSize() const {
// Size, in bytes, of a BLOCKED frame.
// The BLOCKED frame has no payload beyond the control frame header.
return GetFrameHeaderSize();
}
size_t SpdyFramer::GetPushPromiseMinimumSize() const {
// Size, in bytes, of a PUSH_PROMISE frame, sans the embedded header block.
// Calculated as frame prefix + 4 (promised stream id)
return GetFrameHeaderSize() + 4;
}
size_t SpdyFramer::GetContinuationMinimumSize() const {
// Size, in bytes, of a CONTINUATION frame not including the variable-length
// headers fragments.
return GetFrameHeaderSize();
}
size_t SpdyFramer::GetAltSvcMinimumSize() const {
// Size, in bytes, of an ALTSVC frame not including the Field-Value and
// (optional) Origin fields, both 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 + 2 (origin_len).
return GetFrameHeaderSize() + 2;
}
size_t SpdyFramer::GetPrioritySize() const {
// Size, in bytes, of a PRIORITY frame.
return GetFrameHeaderSize() + kPriorityDependencyPayloadSize +
kPriorityWeightPayloadSize;
}
size_t SpdyFramer::GetFrameMinimumSize() const {
return GetFrameHeaderSize();
}
size_t SpdyFramer::GetFrameMaximumSize() const {
return send_frame_size_limit_ + kFrameHeaderSize;
}
size_t SpdyFramer::GetDataFrameMaximumPayload() const {
return std::min(kMaxDataPayloadSendSize,
GetFrameMaximumSize() - GetDataFrameMinimumSize());
}
const char* SpdyFramer::StateToString(int state) {
switch (state) {
case SPDY_ERROR:
return "ERROR";
case SPDY_FRAME_COMPLETE:
return "FRAME_COMPLETE";
case SPDY_READY_FOR_FRAME:
return "READY_FOR_FRAME";
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_SETTINGS_FRAME_HEADER:
return "SPDY_SETTINGS_FRAME_HEADER";
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(SpdyFramerError error) {
DCHECK(visitor_);
spdy_framer_error_ = 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::SpdyFramerErrorToString(
SpdyFramerError spdy_framer_error) {
switch (spdy_framer_error) {
case SPDY_NO_ERROR:
return "NO_ERROR";
case SPDY_INVALID_STREAM_ID:
return "INVALID_STREAM_ID";
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_GOAWAY_FRAME_CORRUPT:
return "GOAWAY_FRAME_CORRUPT";
case SPDY_RST_STREAM_FRAME_CORRUPT:
return "RST_STREAM_FRAME_CORRUPT";
case SPDY_INVALID_PADDING:
return "INVALID_PADDING";
case SPDY_INVALID_DATA_FRAME_FLAGS:
return "INVALID_DATA_FRAME_FLAGS";
case SPDY_INVALID_CONTROL_FRAME_FLAGS:
return "INVALID_CONTROL_FRAME_FLAGS";
case SPDY_UNEXPECTED_FRAME:
return "UNEXPECTED_FRAME";
case SPDY_INTERNAL_FRAMER_ERROR:
return "INTERNAL_FRAMER_ERROR";
case SPDY_INVALID_CONTROL_FRAME_SIZE:
return "INVALID_CONTROL_FRAME_SIZE";
case SPDY_OVERSIZED_PAYLOAD:
return "OVERSIZED_PAYLOAD";
case LAST_ERROR:
return "UNKNOWN_ERROR";
}
return "UNKNOWN_ERROR";
}
size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
DCHECK(visitor_);
DCHECK(data);
if (decoder_adapter_ != nullptr) {
return decoder_adapter_->ProcessInput(data, len);
}
const size_t original_len = len;
do {
previous_state_ = state_;
switch (state_) {
case SPDY_ERROR:
goto bottom;
case SPDY_FRAME_COMPLETE:
// Should not enter in this state.
DCHECK_LT(len, original_len);
Reset();
if (len > 0 && !process_single_input_frame_) {
CHANGE_STATE(SPDY_READING_COMMON_HEADER);
}
break;
case SPDY_READY_FOR_FRAME:
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
// (HEADERS, PUSH_PROMISE, CONTINUATION)
// take a special path through the state machine - they
// will go:
// 1. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK
// 2. SPDY_CONTROL_FRAME_HEADER_BLOCK
int bytes_read = ProcessControlFrameBeforeHeaderBlock(data, len);
len -= bytes_read;
data += bytes_read;
break;
}
case SPDY_SETTINGS_FRAME_HEADER: {
int bytes_read = ProcessSettingsFrameHeader(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);
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;
}
case SPDY_EXTENSION_FRAME_PAYLOAD: {
size_t bytes_read = ProcessExtensionFramePayload(data, len);
len -= bytes_read;
data += bytes_read;
break;
}
default:
SPDY_BUG << "Invalid value for 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 || process_single_input_frame_)
<< "len: " << len << " state: " << state_
<< " process single input frame: " << process_single_input_frame_;
if (current_frame_buffer_.len() == 0 && remaining_data_length_ == 0 &&
remaining_control_header_ == 0) {
DCHECK(state_ == SPDY_READY_FOR_FRAME || state_ == SPDY_ERROR)
<< "State: " << StateToString(state_);
}
return original_len - len;
}
SpdyFramer::CharBuffer::CharBuffer(size_t capacity)
: buffer_(new char[capacity]), capacity_(capacity), len_(0) {}
SpdyFramer::CharBuffer::~CharBuffer() {}
void SpdyFramer::CharBuffer::CopyFrom(const char* data, size_t size) {
DCHECK_GE(capacity_, len_ + size);
memcpy(buffer_.get() + len_, data, size);
len_ += size;
}
void SpdyFramer::CharBuffer::Rewind() {
len_ = 0;
}
size_t SpdyFramer::CharBuffer::EstimateMemoryUsage() const {
return capacity_;
}
SpdyFramer::SpdySettingsScratch::SpdySettingsScratch()
: buffer(8), last_setting_id(-1) {}
void SpdyFramer::SpdySettingsScratch::Reset() {
buffer.Rewind();
last_setting_id = -1;
}
size_t SpdyFramer::SpdySettingsScratch::EstimateMemoryUsage() const {
return SpdyEstimateMemoryUsage(buffer);
}
SpdyFrameType SpdyFramer::ValidateFrameHeader(bool is_control_frame,
uint8_t frame_type_field,
size_t payload_length_field) {
if (!IsDefinedFrameType(frame_type_field)) {
if (expect_continuation_) {
// Report an unexpected frame error and close the connection
// if we expect a continuation and receive an unknown frame.
DLOG(ERROR) << "The framer was expecting to receive a CONTINUATION "
<< "frame, but instead received an unknown frame of type "
<< base::StringPrintf("%x", frame_type_field);
set_error(SPDY_UNEXPECTED_FRAME);
return DATA;
}
if (extension_ != nullptr) {
if (extension_->OnFrameHeader(current_frame_stream_id_,
payload_length_field, frame_type_field,
current_frame_flags_)) {
return EXTENSION;
}
}
// 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_, frame_type_field);
if (!valid_stream) {
// Report an invalid frame error and close the stream if the
// stream_id is not valid.
DLOG(WARNING) << "Unknown control frame type "
<< base::StringPrintf("%x", frame_type_field)
<< " received on invalid stream "
<< current_frame_stream_id_;
set_error(SPDY_INVALID_CONTROL_FRAME);
} else {
DVLOG(1) << "Ignoring unknown frame type.";
CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD);
}
return DATA;
}
SpdyFrameType frame_type = ParseFrameType(frame_type_field);
if (!IsValidHTTP2FrameStreamId(current_frame_stream_id_, frame_type)) {
DLOG(ERROR) << "The framer received an invalid streamID of "
<< current_frame_stream_id_ << " for a frame of type "
<< FrameTypeToString(frame_type);
set_error(SPDY_INVALID_STREAM_ID);
return frame_type;
}
// Ensure that we see a CONTINUATION frame iff we expect to.
if ((frame_type == CONTINUATION) != (expect_continuation_ != 0)) {
if (expect_continuation_ != 0) {
DLOG(ERROR) << "The framer was expecting to receive a CONTINUATION "
<< "frame, but instead received a frame of type "
<< FrameTypeToString(frame_type);
} else {
DLOG(ERROR) << "The framer received an unexpected CONTINUATION frame.";
}
set_error(SPDY_UNEXPECTED_FRAME);
return frame_type;
}
if (payload_length_field > recv_frame_size_limit_) {
set_error(SPDY_OVERSIZED_PAYLOAD);
}
return frame_type;
}
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_.len() < GetFrameHeaderSize()) {
size_t bytes_desired = GetFrameHeaderSize() - current_frame_buffer_.len();
UpdateCurrentFrameBuffer(&data, &len, bytes_desired);
}
if (current_frame_buffer_.len() < GetFrameHeaderSize()) {
// Not enough information to do anything meaningful.
return original_len - len;
}
SpdyFrameReader reader(current_frame_buffer_.data(),
current_frame_buffer_.len());
bool is_control_frame = false;
uint32_t length_field = 0;
bool successful_read = reader.ReadUInt24(&length_field);
DCHECK(successful_read);
uint8_t control_frame_type_field = 0;
successful_read = reader.ReadUInt8(&control_frame_type_field);
DCHECK(successful_read);
// We check control_frame_type_field's validity in
// ValidateFrameHeader().
is_control_frame = control_frame_type_field != DATA;
current_frame_length_ = length_field + GetFrameHeaderSize();
successful_read = reader.ReadUInt8(&current_frame_flags_);
DCHECK(successful_read);
successful_read = reader.ReadUInt31(&current_frame_stream_id_);
DCHECK(successful_read);
remaining_data_length_ = current_frame_length_ - reader.GetBytesConsumed();
DCHECK_EQ(GetFrameHeaderSize(), reader.GetBytesConsumed());
DCHECK_EQ(current_frame_length_,
remaining_data_length_ + reader.GetBytesConsumed());
// This is just a sanity check for help debugging early frame errors.
// The strncmp for 5 is safe because we only hit this point if we
// have kMinCommonHeader (8) bytes
if (remaining_data_length_ > 1000000u &&
strncmp(current_frame_buffer_.data(), "HTTP/", 5) == 0) {
LOG(WARNING) << "Unexpected HTTP response to HTTP2 request";
probable_http_response_ = true;
}
// If we're here, then we have the common header all received.
visitor_->OnCommonHeader(current_frame_stream_id_, remaining_data_length_,
control_frame_type_field, current_frame_flags_);
current_frame_type_ = ValidateFrameHeader(
is_control_frame, control_frame_type_field, remaining_data_length_);
if (state_ == SPDY_ERROR || state_ == SPDY_IGNORE_REMAINING_PAYLOAD) {
return original_len - len;
}
if (!is_control_frame) {
uint8_t valid_data_flags = DATA_FLAG_FIN | DATA_FLAG_PADDED;
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_->OnStreamEnd(current_frame_stream_id_);
}
CHANGE_STATE(SPDY_FRAME_COMPLETE);
}
}
} else {
ProcessControlFrameHeader();
}
return original_len - len;
}
void SpdyFramer::ProcessControlFrameHeader() {
DCHECK_EQ(SPDY_NO_ERROR, spdy_framer_error_);
DCHECK_LE(GetFrameHeaderSize(), current_frame_buffer_.len());
// Do some sanity checking on the control frame sizes and flags.
switch (current_frame_type_) {
case RST_STREAM:
if (current_frame_length_ != GetRstStreamSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME_SIZE);
} else if (current_frame_flags_ != 0) {
VLOG(1) << "Undefined frame flags for RST_STREAM frame: " << hex
<< static_cast<int>(current_frame_flags_);
current_frame_flags_ = 0;
}
break;
case SETTINGS:
{
// Make sure that we have an integral number of 8-byte key/value pairs,
// Size of each key/value pair in bytes.
if (current_frame_length_ < GetSettingsMinimumSize() ||
(current_frame_length_ - GetFrameHeaderSize()) %
kOneSettingParameterSize !=
0) {
DLOG(WARNING) << "Invalid length for SETTINGS frame: "
<< current_frame_length_;
set_error(SPDY_INVALID_CONTROL_FRAME_SIZE);
} else if (current_frame_flags_ & SETTINGS_FLAG_ACK &&
current_frame_length_ > GetSettingsMinimumSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME_SIZE);
} else if (current_frame_flags_ & ~SETTINGS_FLAG_ACK) {
VLOG(1) << "Undefined frame flags for SETTINGS frame: " << hex
<< static_cast<int>(current_frame_flags_);
current_frame_flags_ &= SETTINGS_FLAG_ACK;
}
break;
}
case PING:
if (current_frame_length_ != GetPingSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME_SIZE);
} else {
if (current_frame_flags_ & ~PING_FLAG_ACK) {
VLOG(1) << "Undefined frame flags for PING frame: " << hex
<< static_cast<int>(current_frame_flags_);
current_frame_flags_ &= PING_FLAG_ACK;
}
}
break;
case GOAWAY:
{
// For HTTP/2, optional opaque data may be appended to the
// GOAWAY frame, thus there is only a minimal length restriction.
if (current_frame_length_ < GetGoAwayMinimumSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ != 0) {
VLOG(1) << "Undefined frame flags for GOAWAY frame: " << hex
<< static_cast<int>(current_frame_flags_);
current_frame_flags_ = 0;
}
break;
}
case HEADERS:
{
size_t min_size = GetHeadersMinimumSize();
if (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 HTTP2)
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ &
~(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY |
HEADERS_FLAG_END_HEADERS | HEADERS_FLAG_PADDED)) {
VLOG(1) << "Undefined frame flags for HEADERS frame: " << hex
<< static_cast<int>(current_frame_flags_);
current_frame_flags_ &=
(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY |
HEADERS_FLAG_END_HEADERS | HEADERS_FLAG_PADDED);
}
}
break;
case WINDOW_UPDATE:
if (current_frame_length_ != GetWindowUpdateSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME_SIZE);
} else if (current_frame_flags_ != 0) {
VLOG(1) << "Undefined frame flags for WINDOW_UPDATE frame: " << hex
<< static_cast<int>(current_frame_flags_);
current_frame_flags_ = 0;
}
break;
case BLOCKED:
if (current_frame_length_ != GetBlockedSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ != 0) {
VLOG(1) << "Undefined frame flags for BLOCKED frame: " << hex
<< static_cast<int>(current_frame_flags_);
current_frame_flags_ = 0;
}
break;
case PUSH_PROMISE:
if (current_frame_length_ < GetPushPromiseMinimumSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ &
~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE | HEADERS_FLAG_PADDED)) {
VLOG(1) << "Undefined frame flags for PUSH_PROMISE frame: " << hex
<< static_cast<int>(current_frame_flags_);
current_frame_flags_ &=
(PUSH_PROMISE_FLAG_END_PUSH_PROMISE | HEADERS_FLAG_PADDED);
}
break;
case CONTINUATION:
if (current_frame_length_ < GetContinuationMinimumSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ & ~HEADERS_FLAG_END_HEADERS) {
VLOG(1) << "Undefined frame flags for CONTINUATION frame: " << hex
<< static_cast<int>(current_frame_flags_);
current_frame_flags_ &= HEADERS_FLAG_END_HEADERS;
}
break;
case ALTSVC:
if (current_frame_length_ <= GetAltSvcMinimumSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ != 0) {
VLOG(1) << "Undefined frame flags for ALTSVC frame: " << hex
<< static_cast<int>(current_frame_flags_);
current_frame_flags_ = 0;
}
break;
case PRIORITY:
if (current_frame_length_ != GetPrioritySize()) {
set_error(SPDY_INVALID_CONTROL_FRAME_SIZE);
} else if (current_frame_flags_ != 0) {
VLOG(1) << "Undefined frame flags for PRIORITY frame: " << hex
<< static_cast<int>(current_frame_flags_);
current_frame_flags_ = 0;
}
break;
case EXTENSION:
// No particular requirements on frames handled by the registered
// extension.
break;
default:
LOG(WARNING) << "Valid 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.
set_error(SPDY_INVALID_CONTROL_FRAME);
DLOG(FATAL);
break;
}
if (state_ == SPDY_ERROR) {
return;
}
if (current_frame_type_ == GOAWAY) {
CHANGE_STATE(SPDY_GOAWAY_FRAME_PAYLOAD);
return;
}
if (current_frame_type_ == ALTSVC) {
CHANGE_STATE(SPDY_ALTSVC_FRAME_PAYLOAD);
return;
}
// Determine the frame size without variable-length data.
int32_t frame_size_without_variable_data;
switch (current_frame_type_) {
case SETTINGS:
frame_size_without_variable_data = GetSettingsMinimumSize();
break;
case HEADERS:
frame_size_without_variable_data = GetHeadersMinimumSize();
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 (current_frame_flags_ & PUSH_PROMISE_FLAG_PADDED) {
frame_size_without_variable_data += kPadLengthFieldSize;
}
break;
case CONTINUATION:
frame_size_without_variable_data = GetContinuationMinimumSize();
break;
case EXTENSION:
frame_size_without_variable_data = GetFrameHeaderSize();
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) {
SPDY_BUG << "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 variable-size data. We need to parse the
// remainder of the control frame's header before we can parse the payload.
// The start of the payload varies with the control frame type.
DCHECK_GE(frame_size_without_variable_data,
static_cast<int32_t>(current_frame_buffer_.len()));
remaining_control_header_ =
frame_size_without_variable_data - current_frame_buffer_.len();
if (current_frame_type_ == SETTINGS) {
CHANGE_STATE(SPDY_SETTINGS_FRAME_HEADER);
} else if (current_frame_type_ == EXTENSION) {
CHANGE_STATE(SPDY_EXTENSION_FRAME_PAYLOAD);
} else {
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) {
current_frame_buffer_.CopyFrom(*data, bytes_to_read);
*data += bytes_to_read;
*len -= bytes_to_read;
}
return bytes_to_read;
}
size_t SpdyFramer::GetSerializedLength(
const SpdyHeaderBlock* headers) {
const size_t num_name_value_pairs_size = sizeof(uint32_t);
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 (const auto& header : *headers) {
// 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 + header.first.size() +
length_of_value_size + header.second.size();
}
return total_length;
}
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_.data(),
current_frame_buffer_.len());
reader.Seek(GetFrameHeaderSize()); // Seek past frame header.
switch (current_frame_type_) {
case HEADERS:
{
bool successful_read = true;
if (current_frame_stream_id_ == 0) {
set_error(SPDY_INVALID_CONTROL_FRAME);
return original_len - len;
}
if (!(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 (current_frame_flags_ & HEADERS_FLAG_PADDED) {
uint8_t 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;
int weight = 0;
uint32_t parent_stream_id = 0;
bool exclusive = false;
if (has_priority) {
uint32_t stream_dependency;
successful_read = reader.ReadUInt32(&stream_dependency);
DCHECK(successful_read);
UnpackStreamDependencyValues(stream_dependency, &exclusive,
&parent_stream_id);
uint8_t serialized_weight = 0;
successful_read = reader.ReadUInt8(&serialized_weight);
if (successful_read) {
// Per RFC 7540 section 6.3, serialized weight value is actual
// value - 1.
weight = serialized_weight + 1;
}
}
DCHECK(reader.IsDoneReading());
if (debug_visitor_) {
debug_visitor_->OnReceiveCompressedFrame(current_frame_stream_id_,
current_frame_type_,
current_frame_length_);
}
visitor_->OnHeaders(
current_frame_stream_id_,
(current_frame_flags_ & HEADERS_FLAG_PRIORITY) != 0, weight,
parent_stream_id, exclusive,
(current_frame_flags_ & CONTROL_FLAG_FIN) != 0,
expect_continuation_ == 0);
}
break;
case PUSH_PROMISE:
{
if (current_frame_stream_id_ == 0) {
set_error(SPDY_INVALID_CONTROL_FRAME);
return original_len - len;
}
bool successful_read = true;
if (current_frame_flags_ & PUSH_PROMISE_FLAG_PADDED) {
DCHECK_EQ(remaining_padding_payload_length_, 0u);
uint8_t 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);
return original_len - len;
}
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);
}
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_UNEXPECTED_FRAME);
return original_len - len;
}
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);
}
break;
default:
#ifndef NDEBUG
LOG(FATAL) << "Invalid control frame type: " << current_frame_type_;
#else
set_error(SPDY_INVALID_CONTROL_FRAME);
return original_len - len;
#endif
}
if (current_frame_type_ != CONTINUATION) {
header_handler_ = visitor_->OnHeaderFrameStart(current_frame_stream_id_);
if (header_handler_ == nullptr) {
SPDY_BUG << "visitor_->OnHeaderFrameStart returned nullptr";
set_error(SPDY_INTERNAL_FRAMER_ERROR);
return original_len - len;
}
GetHpackDecoder()->HandleControlFrameHeadersStart(header_handler_);
}
CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
}
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.
size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data,
size_t data_len) {
DCHECK_EQ(SPDY_CONTROL_FRAME_HEADER_BLOCK, state_);
bool processed_successfully = true;
if (current_frame_type_ != HEADERS && current_frame_type_ != PUSH_PROMISE &&
current_frame_type_ != CONTINUATION) {
SPDY_BUG << "Unhandled frame type in ProcessControlFrameHeaderBlock.";
}
if (remaining_padding_payload_length_ > remaining_data_length_) {
set_error(SPDY_INVALID_PADDING);
return data_len;
}
size_t process_bytes = std::min(
data_len, remaining_data_length_ - remaining_padding_payload_length_);
if (!GetHpackDecoder()->HandleControlFrameHeadersData(data, process_bytes)) {
// TODO(jgraettinger): Finer-grained HPACK error codes.
set_error(SPDY_DECOMPRESS_FAILURE);
processed_successfully = false;
}
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) {
size_t compressed_len = 0;
if (GetHpackDecoder()->HandleControlFrameHeadersComplete(
&compressed_len)) {
visitor_->OnHeaderFrameEnd(current_frame_stream_id_, true);
if (state_ == SPDY_ERROR) {
return data_len;
}
} else {
set_error(SPDY_DECOMPRESS_FAILURE);
processed_successfully = false;
}
}
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::ProcessSettingsFrameHeader(const char* data, size_t len) {
// TODO(birenroy): Remove this state when removing SPDY3. I think it only
// exists to read the number of settings in the frame for SPDY3. This value
// is never parsed or used.
size_t bytes_read = 0;
if (remaining_control_header_ > 0) {
bytes_read =
UpdateCurrentFrameBuffer(&data, &len, remaining_control_header_);
remaining_control_header_ -= bytes_read;
remaining_data_length_ -= bytes_read;
}
if (remaining_control_header_ == 0) {
if (current_frame_flags_ & SETTINGS_FLAG_ACK) {
visitor_->OnSettingsAck();
CHANGE_STATE(SPDY_FRAME_COMPLETE);
} else {
visitor_->OnSettings(current_frame_flags_ &
SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS);
CHANGE_STATE(SPDY_SETTINGS_FRAME_PAYLOAD);
}
}
return bytes_read;
}
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;
// Loop over our incoming data.
while (unprocessed_bytes > 0) {
// Process up to one setting at a time.
size_t processing =
std::min(unprocessed_bytes,
kOneSettingParameterSize - settings_scratch_.buffer.len());
// Check if we have a complete setting in our input.
if (processing == kOneSettingParameterSize) {
// 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.
settings_scratch_.buffer.CopyFrom(data + processed_bytes, processing);
// Check if we have a complete setting buffered.
if (settings_scratch_.buffer.len() == kOneSettingParameterSize) {
if (!ProcessSetting(settings_scratch_.buffer.data())) {
set_error(SPDY_INVALID_CONTROL_FRAME);
return processed_bytes;
}
// Rewind settings buffer for our next setting.
settings_scratch_.buffer.Rewind();
}
}
// 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_FRAME_COMPLETE);
}
return processed_bytes;
}
bool SpdyFramer::ProcessSetting(const char* data) {
// Extract fields.
// Maintain behavior of old SPDY 2 bug with byte ordering of flags/id.
uint16_t id_field =
base::NetToHost16(*(reinterpret_cast<const uint16_t*>(data)));
uint32_t value =
base::NetToHost32(*(reinterpret_cast<const uint32_t*>(data + 2)));
// Validate id.
SpdySettingsIds setting_id;
if (!ParseSettingsId(id_field, &setting_id)) {
if (extension_ == nullptr) {
DLOG(WARNING) << "Unknown SETTINGS ID: " << id_field;
} else {
extension_->OnSetting(id_field, value);
}
// Ignore unknown settings for extensibility.
return true;
}
// Validation succeeded. Pass on to visitor.
visitor_->OnSetting(setting_id, 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_.data(),
current_frame_buffer_.len());
reader.Seek(GetFrameHeaderSize()); // Skip frame header.
// Use frame-specific handlers.
switch (current_frame_type_) {
case RST_STREAM: {
uint32_t error_code = ERROR_CODE_NO_ERROR;
bool successful_read = reader.ReadUInt32(&error_code);
DCHECK(successful_read);
DCHECK(reader.IsDoneReading());
visitor_->OnRstStream(current_frame_stream_id_,
ParseErrorCode(error_code));
} break;
case PING: {
SpdyPingId id = 0;
bool is_ack = current_frame_flags_ & PING_FLAG_ACK;
bool successful_read = true;
successful_read = reader.ReadUInt64(&id);
DCHECK(successful_read);
DCHECK(reader.IsDoneReading());
visitor_->OnPing(id, is_ack);
} break;
case WINDOW_UPDATE: {
uint32_t delta_window_size = 0;
bool successful_read = true;
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(reader.IsDoneReading());
visitor_->OnBlocked(current_frame_stream_id_);
} break;
case PRIORITY: {
uint32_t stream_dependency;
uint32_t parent_stream_id;
bool exclusive;
uint8_t serialized_weight;
bool successful_read = reader.ReadUInt32(&stream_dependency);
DCHECK(successful_read);
UnpackStreamDependencyValues(stream_dependency, &exclusive,
&parent_stream_id);
successful_read = reader.ReadUInt8(&serialized_weight);
DCHECK(successful_read);
DCHECK(reader.IsDoneReading());
// Per RFC 7540 section 6.3, serialized weight value is
// actual value - 1.
int weight = serialized_weight + 1;
visitor_->OnPriority(current_frame_stream_id_, parent_stream_id, weight,
exclusive);
} break;
case EXTENSION:
if (extension_ == nullptr) {
SPDY_BUG << "Reached EXTENSION frame processing with a null "
<< "extension!";
break;
}
extension_->OnFramePayload(current_frame_buffer_.data(),
current_frame_buffer_.len());
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_.len();
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_.len() == header_size) {
// Parse out the last good stream id.
SpdyFrameReader reader(current_frame_buffer_.data(),
current_frame_buffer_.len());
reader.Seek(GetFrameHeaderSize()); // Seek past frame header.
bool successful_read = reader.ReadUInt31(&current_frame_stream_id_);
DCHECK(successful_read);
// Parse status code.
uint32_t error_code = ERROR_CODE_NO_ERROR;
successful_read = reader.ReadUInt32(&error_code);
DCHECK(successful_read);
// Finished parsing the GOAWAY header, call frame handler.
visitor_->OnGoAway(current_frame_stream_id_, ParseErrorCode(error_code));
}
}
// 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(nullptr, 0);
CHANGE_STATE(SPDY_FRAME_COMPLETE);
}
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_);
if (altsvc_scratch_ == nullptr) {
size_t capacity = current_frame_length_ - GetFrameHeaderSize();
altsvc_scratch_.reset(new CharBuffer(capacity));
}
altsvc_scratch_->CopyFrom(data, len);
remaining_data_length_ -= len;
if (remaining_data_length_ > 0) {
return len;
}
SpdyFrameReader reader(altsvc_scratch_->data(), altsvc_scratch_->len());
SpdyStringPiece origin;
bool successful_read = reader.ReadStringPiece16(&origin);
if (!successful_read) {
set_error(SPDY_INVALID_CONTROL_FRAME);
return 0;
}
SpdyStringPiece value(altsvc_scratch_->data() + reader.GetBytesConsumed(),
altsvc_scratch_->len() - reader.GetBytesConsumed());
SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
bool success =
SpdyAltSvcWireFormat::ParseHeaderFieldValue(value, &altsvc_vector);
if (!success) {
set_error(SPDY_INVALID_CONTROL_FRAME);
return 0;
}
visitor_->OnAltSvc(current_frame_stream_id_, origin, altsvc_vector);
CHANGE_STATE(SPDY_FRAME_COMPLETE);
return len;
}
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;
}
static_assert(kPadLengthFieldSize == 1,
"Unexpected pad length field size.");
remaining_padding_payload_length_ =
*reinterpret_cast<const uint8_t*>(data);
++data;
--len;
--remaining_data_length_;
visitor_->OnStreamPadding(current_frame_stream_id_, kPadLengthFieldSize);
} 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_PADDING);
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) {
visitor_->OnStreamPadding(current_frame_stream_id_, amount_to_discard);
}
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_->OnStreamEnd(current_frame_stream_id_);
}
CHANGE_STATE(SPDY_FRAME_COMPLETE);
}
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);
}
}
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::ProcessExtensionFramePayload(const char* data, size_t len) {
DCHECK_EQ(SPDY_EXTENSION_FRAME_PAYLOAD, state_);
DCHECK(extension_ != nullptr);
size_t original_len = len;
if (remaining_data_length_ > 0) {
size_t amount_to_forward = std::min(remaining_data_length_, len);
if (amount_to_forward && state_ != SPDY_IGNORE_REMAINING_PAYLOAD) {
// Only inform the visitor if there is data.
extension_->OnFramePayload(data, amount_to_forward);
}
remaining_data_length_ -= amount_to_forward;
len -= amount_to_forward;
}
if (remaining_data_length_ == 0) {
CHANGE_STATE(SPDY_FRAME_COMPLETE);
}
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_FRAME_COMPLETE);
}
return original_len - len;
}
bool SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data,
size_t header_length,
SpdyHeaderBlock* block) const {
SpdyFrameReader reader(header_data, header_length);
// Read number of headers.
uint32_t num_headers;
if (!reader.ReadUInt32(&num_headers)) {
DVLOG(1) << "Unable to read number of headers.";
return false;
}
// Read each header.
for (uint32_t index = 0; index < num_headers; ++index) {
SpdyStringPiece temp;
// Read header name.
if (!reader.ReadStringPiece32(&temp)) {
DVLOG(1) << "Unable to read header name (" << index + 1 << " of "
<< num_headers << ").";
return false;
}
const char* begin = temp.data();
const char* end = begin;
std::advance(end, temp.size());
if (std::any_of(begin, end, isupper)) {
DVLOG(1) << "Malformed header: Header name " << temp
<< " contains upper-case characters.";
return false;
}
std::string name(temp);
// Read header value.
if (!reader.ReadStringPiece32(&temp)) {
DVLOG(1) << "Unable to read header value (" << index + 1 << " of "
<< num_headers << ").";
return false;
}
std::string value(temp);
// Ensure no duplicates.
if (block->find(name) != block->end()) {
DVLOG(1) << "Duplicate header '" << name << "' (" << index + 1 << " of "
<< num_headers << ").";
return false;
}
// Store header.
(*block)[name] = value;
}
if (reader.GetBytesConsumed() != header_length) {
SPDY_BUG << "Buffer expected to consist entirely of headers, but only "
<< reader.GetBytesConsumed() << " bytes consumed, from "
<< header_length;
return false;
}
return true;
}
SpdyFramer::SpdyHeaderFrameIterator::SpdyHeaderFrameIterator(
SpdyFramer* framer,
std::unique_ptr<SpdyHeadersIR> headers_ir)
: headers_ir_(std::move(headers_ir)),
framer_(framer),
debug_total_size_(0),
is_first_frame_(true),
has_next_frame_(true) {
encoder_ =
framer_->GetHpackEncoder()->EncodeHeaderSet(headers_ir_->header_block());
}
SpdyFramer::SpdyHeaderFrameIterator::~SpdyHeaderFrameIterator() {}
SpdySerializedFrame SpdyFramer::SpdyHeaderFrameIterator::NextFrame() {
if (!has_next_frame_) {
SPDY_BUG << "SpdyFramer::SpdyHeaderFrameIterator::NextFrame called without "
<< "a next frame.";
return SpdySerializedFrame();
}
size_t size_without_block =
is_first_frame_ ? framer_->GetHeaderFrameSizeSansBlock(*headers_ir_)
: framer_->GetContinuationMinimumSize();
auto encoding = base::MakeUnique<string>();
encoder_->Next(kMaxControlFrameSize - size_without_block, encoding.get());
has_next_frame_ = encoder_->HasNext();
if (framer_->debug_visitor_ != nullptr) {
debug_total_size_ += size_without_block;
debug_total_size_ += encoding->size();
if (!has_next_frame_) {
// TODO(birenroy) are these (here and below) still necessary?
// HTTP2 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.
size_t debug_payload_len =
framer_->GetSerializedLength(&headers_ir_->header_block());
framer_->debug_visitor_->OnSendCompressedFrame(headers_ir_->stream_id(),
HEADERS, debug_payload_len,
debug_total_size_);
}
}
if (is_first_frame_) {
is_first_frame_ = false;
headers_ir_->set_end_headers(!has_next_frame_);
return framer_->SerializeHeadersGivenEncoding(*headers_ir_, *encoding);
} else {
SpdyContinuationIR continuation_ir(headers_ir_->stream_id());
continuation_ir.set_end_headers(!has_next_frame_);
continuation_ir.take_encoding(std::move(encoding));
return framer_->SerializeContinuation(continuation_ir);
}
}
void SpdyFramer::SerializeDataBuilderHelper(const SpdyDataIR& data_ir,
uint8_t* flags,
int* num_padding_fields,
size_t* size_with_padding) const {
if (data_ir.fin()) {
*flags = DATA_FLAG_FIN;
}
if (data_ir.padded()) {
*flags = *flags | DATA_FLAG_PADDED;
++*num_padding_fields;
}
*size_with_padding = *num_padding_fields + data_ir.data_len() +
data_ir.padding_payload_len() +
GetDataFrameMinimumSize();
}
SpdySerializedFrame SpdyFramer::SerializeData(const SpdyDataIR& data_ir) const {
uint8_t flags = DATA_FLAG_NONE;
int num_padding_fields = 0;
size_t size_with_padding = 0;
SerializeDataBuilderHelper(data_ir, &flags, &num_padding_fields,
&size_with_padding);
SpdyFrameBuilder builder(size_with_padding);
builder.BeginNewFrame(*this, DATA, flags, data_ir.stream_id());
if (data_ir.padded()) {
builder.WriteUInt8(data_ir.padding_payload_len() & 0xff);
}
builder.WriteBytes(data_ir.data(), data_ir.data_len());
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();
}
void SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthFieldBuilderHelper(
const SpdyDataIR& data_ir,
uint8_t* flags,
size_t* frame_size,
size_t* num_padding_fields) const {
*flags = DATA_FLAG_NONE;
if (data_ir.fin()) {
*flags = DATA_FLAG_FIN;
}
*frame_size = GetDataFrameMinimumSize();
if (data_ir.padded()) {
*flags = *flags | DATA_FLAG_PADDED;
++(*num_padding_fields);
*frame_size = *frame_size + *num_padding_fields;
}
}
SpdySerializedFrame SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField(
const SpdyDataIR& data_ir) const {
uint8_t flags = DATA_FLAG_NONE;
size_t frame_size = 0;
size_t num_padding_fields = 0;
SerializeDataFrameHeaderWithPaddingLengthFieldBuilderHelper(
data_ir, &flags, &frame_size, &num_padding_fields);
SpdyFrameBuilder builder(frame_size);
if (!skip_rewritelength_) {
builder.BeginNewFrame(*this, DATA, flags, data_ir.stream_id());
if (data_ir.padded()) {
builder.WriteUInt8(data_ir.padding_payload_len() & 0xff);
}
builder.OverwriteLength(*this, num_padding_fields + data_ir.data_len() +
data_ir.padding_payload_len());
} else {
builder.BeginNewFrame(*this, DATA, flags, data_ir.stream_id(),
num_padding_fields + data_ir.data_len() +
data_ir.padding_payload_len());
if (data_ir.padded()) {
builder.WriteUInt8(data_ir.padding_payload_len() & 0xff);
}
}
DCHECK_EQ(frame_size, builder.length());
return builder.take();
}
SpdySerializedFrame SpdyFramer::SerializeRstStream(
const SpdyRstStreamIR& rst_stream) const {
size_t expected_length = GetRstStreamSize();
SpdyFrameBuilder builder(expected_length);
builder.BeginNewFrame(*this, RST_STREAM, 0, rst_stream.stream_id());
builder.WriteUInt32(rst_stream.error_code());
DCHECK_EQ(expected_length, builder.length());
return builder.take();
}
void SpdyFramer::SerializeSettingsBuilderHelper(const SpdySettingsIR& settings,
uint8_t* flags,
const SettingsMap* values,
size_t* size) const {
if (settings.is_ack()) {
*flags = *flags | SETTINGS_FLAG_ACK;
}
*size =
GetSettingsMinimumSize() + (values->size() * kOneSettingParameterSize);
}
SpdySerializedFrame SpdyFramer::SerializeSettings(
const SpdySettingsIR& settings) const {
uint8_t flags = 0;
// Size, in bytes, of this SETTINGS frame.
size_t size = 0;
const SettingsMap* values = &(settings.values());
SerializeSettingsBuilderHelper(settings, &flags, values, &size);
SpdyFrameBuilder builder(size);
builder.BeginNewFrame(*this, SETTINGS, flags, 0);
// If this is an ACK, payload should be empty.
if (settings.is_ack()) {
return builder.take();
}
DCHECK_EQ(GetSettingsMinimumSize(), builder.length());
for (SettingsMap::const_iterator it = values->begin(); it != values->end();
++it) {
int setting_id = it->first;
DCHECK_GE(setting_id, 0);
builder.WriteUInt16(static_cast<uint16_t>(setting_id));
builder.WriteUInt32(it->second);
}
DCHECK_EQ(size, builder.length());
return builder.take();
}
SpdySerializedFrame SpdyFramer::SerializePing(const SpdyPingIR& ping) const {
SpdyFrameBuilder builder(GetPingSize());
uint8_t 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();
expected_length += goaway.description().size();
SpdyFrameBuilder builder(expected_length);
// Serialize the GOAWAY frame.
builder.BeginNewFrame(*this, GOAWAY, 0, 0);
// GOAWAY frames specify the last good stream id.
builder.WriteUInt32(goaway.last_good_stream_id());
// GOAWAY frames also specify the error code.
builder.WriteUInt32(goaway.error_code());
// GOAWAY frames may also specify opaque data.
if (!goaway.description().empty()) {
builder.WriteBytes(goaway.description().data(),
goaway.description().size());
}
DCHECK_EQ(expected_length, builder.length());
return builder.take();
}
void SpdyFramer::SerializeHeadersBuilderHelper(const SpdyHeadersIR& headers,
uint8_t* flags,
size_t* size,
string* hpack_encoding,
int* weight,
size_t* length_field) {
if (headers.fin()) {
*flags = *flags | CONTROL_FLAG_FIN;
}
// This will get overwritten if we overflow into a CONTINUATION frame.
*flags = *flags | HEADERS_FLAG_END_HEADERS;
if (headers.has_priority()) {
*flags = *flags | HEADERS_FLAG_PRIORITY;
}
if (headers.padded()) {
*flags = *flags | HEADERS_FLAG_PADDED;
}
*size = GetHeadersMinimumSize();
if (headers.padded()) {
*size = *size + kPadLengthFieldSize;
*size = *size + headers.padding_payload_len();
}
if (headers.has_priority()) {
*weight = ClampHttp2Weight(headers.weight());
*size = *size + 5;
}
GetHpackEncoder()->EncodeHeaderSet(headers.header_block(), hpack_encoding);
*size = *size + hpack_encoding->size();
if (*size > kMaxControlFrameSize) {
*size = *size + GetNumberRequiredContinuationFrames(*size) *
GetContinuationMinimumSize();
*flags = *flags & ~HEADERS_FLAG_END_HEADERS;
}
// Compute frame length field.
if (headers.padded()) {
*length_field = *length_field + 1; // Padding length field.
}
if (headers.has_priority()) {
*length_field = *length_field + 4; // Dependency field.
*length_field = *length_field + 1; // Weight field.
}
*length_field = *length_field + headers.padding_payload_len();
*length_field = *length_field + hpack_encoding->size();
// If the HEADERS frame with payload would exceed the max frame size, then
// WritePayloadWithContinuation() will serialize CONTINUATION frames as
// necessary.
*length_field =
std::min(*length_field, kMaxControlFrameSize - GetFrameHeaderSize());
}
SpdySerializedFrame SpdyFramer::SerializeHeaders(const SpdyHeadersIR& headers) {
uint8_t flags = 0;
// The size of this frame, including padding (if there is any) and
// variable-length header block.
size_t size = 0;
string hpack_encoding;
int weight = 0;
size_t length_field = 0;
SerializeHeadersBuilderHelper(headers, &flags, &size, &hpack_encoding,
&weight, &length_field);
SpdyFrameBuilder builder(size);
if (!skip_rewritelength_) {
builder.BeginNewFrame(*this, HEADERS, flags, headers.stream_id());
} else {
builder.BeginNewFrame(*this, HEADERS, flags, headers.stream_id(),
length_field);
}
DCHECK_EQ(GetHeadersMinimumSize(), builder.length());
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()) {
builder.WriteUInt32(PackStreamDependencyValues(headers.exclusive(),
headers.parent_stream_id()));
// Per RFC 7540 section 6.3, serialized weight value is actual value - 1.
builder.WriteUInt8(weight - 1);
}
WritePayloadWithContinuation(&builder, hpack_encoding, headers.stream_id(),
HEADERS, padding_payload_len);
if (debug_visitor_) {
// HTTP2 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(&(headers.header_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());
builder.BeginNewFrame(*this, WINDOW_UPDATE, kNoFlags,
window_update.stream_id());
builder.WriteUInt32(window_update.delta());
DCHECK_EQ(GetWindowUpdateSize(), builder.length());
return builder.take();
}
SpdySerializedFrame SpdyFramer::SerializeBlocked(
const SpdyBlockedIR& blocked) const {
SpdyFrameBuilder builder(GetBlockedSize());
builder.BeginNewFrame(*this, BLOCKED, kNoFlags, blocked.stream_id());
return builder.take();
}
void SpdyFramer::SerializePushPromiseBuilderHelper(
const SpdyPushPromiseIR& push_promise,
uint8_t* flags,
string* hpack_encoding,
size_t* size) {
*flags = 0;
// This will get overwritten if we overflow into a CONTINUATION frame.
*flags = *flags | PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
// The size of this frame, including variable-length name-value block.
*size = GetPushPromiseMinimumSize();
if (push_promise.padded()) {
*flags = *flags | PUSH_PROMISE_FLAG_PADDED;
*size = *size + kPadLengthFieldSize;
*size = *size + push_promise.padding_payload_len();
}
GetHpackEncoder()->EncodeHeaderSet(push_promise.header_block(),
hpack_encoding);
*size = *size + hpack_encoding->size();
if (*size > kMaxControlFrameSize) {
*size = *size + GetNumberRequiredContinuationFrames(*size) *
GetContinuationMinimumSize();
*flags = *flags & ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
}
}
SpdySerializedFrame SpdyFramer::SerializePushPromise(
const SpdyPushPromiseIR& push_promise) {
uint8_t flags = 0;
size_t size = 0;
string hpack_encoding;
SerializePushPromiseBuilderHelper(push_promise, &flags, &hpack_encoding,
&size);
SpdyFrameBuilder builder(size);
if (!skip_rewritelength_) {
builder.BeginNewFrame(*this, PUSH_PROMISE, flags, push_promise.stream_id());
} else {
size_t length = std::min(size, kMaxControlFrameSize) - GetFrameHeaderSize();
builder.BeginNewFrame(*this, PUSH_PROMISE, flags, push_promise.stream_id(),
length);
}
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_) {
// HTTP2 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(&(push_promise.header_block()));
debug_visitor_->OnSendCompressedFrame(push_promise.stream_id(),
PUSH_PROMISE,
payload_len,
builder.length());
}
return builder.take();
}
SpdySerializedFrame SpdyFramer::SerializeHeadersGivenEncoding(
const SpdyHeadersIR& headers,
const string& encoding) const {
size_t frame_size = GetHeaderFrameSizeSansBlock(headers) + encoding.size();
SpdyFrameBuilder builder(frame_size);
builder.BeginNewFrame(*this, HEADERS, SerializeHeaderFrameFlags(headers),
headers.stream_id());
DCHECK_EQ(GetFrameHeaderSize(), builder.length());
if (headers.padded()) {
builder.WriteUInt8(headers.padding_payload_len());
}
if (headers.has_priority()) {
int weight = ClampHttp2Weight(headers.weight());
builder.WriteUInt32(PackStreamDependencyValues(headers.exclusive(),
headers.parent_stream_id()));
// Per RFC 7540 section 6.3, serialized weight value is actual value - 1.
builder.WriteUInt8(weight - 1);
}
builder.WriteBytes(&encoding[0], encoding.size());
if (headers.padding_payload_len() > 0) {
string padding(headers.padding_payload_len(), 0);
builder.WriteBytes(padding.data(), padding.length());
}
return builder.take();
}
SpdySerializedFrame SpdyFramer::SerializeContinuation(
const SpdyContinuationIR& continuation) const {
const string& encoding = continuation.encoding();
size_t frame_size = GetContinuationMinimumSize() + encoding.size();
SpdyFrameBuilder builder(frame_size);
uint8_t flags = continuation.end_headers() ? HEADERS_FLAG_END_HEADERS : 0;
builder.BeginNewFrame(*this, CONTINUATION, flags, continuation.stream_id());
DCHECK_EQ(GetFrameHeaderSize(), builder.length());
builder.WriteBytes(encoding.data(), encoding.size());
return builder.take();
}
void SpdyFramer::SerializeAltSvcBuilderHelper(const SpdyAltSvcIR& altsvc_ir,
string* value,
size_t* size) const {
*size = GetAltSvcMinimumSize();
*size = *size + altsvc_ir.origin().length();
*value = SpdyAltSvcWireFormat::SerializeHeaderFieldValue(
altsvc_ir.altsvc_vector());
*size = *size + value->length();
}
SpdySerializedFrame SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc_ir) {
string value;
size_t size = 0;
SerializeAltSvcBuilderHelper(altsvc_ir, &value, &size);
SpdyFrameBuilder builder(size);
builder.BeginNewFrame(*this, ALTSVC, kNoFlags, altsvc_ir.stream_id());
builder.WriteUInt16(altsvc_ir.origin().length());
builder.WriteBytes(altsvc_ir.origin().data(), altsvc_ir.origin().length());
builder.WriteBytes(value.data(), value.length());
DCHECK_LT(GetAltSvcMinimumSize(), builder.length());
return builder.take();
}
SpdySerializedFrame SpdyFramer::SerializePriority(
const SpdyPriorityIR& priority) const {
size_t size = GetPrioritySize();
SpdyFrameBuilder builder(size);
builder.BeginNewFrame(*this, PRIORITY, kNoFlags, priority.stream_id());
builder.WriteUInt32(PackStreamDependencyValues(priority.exclusive(),
priority.parent_stream_id()));
// Per RFC 7540 section 6.3, serialized weight value is actual value - 1.
builder.WriteUInt8(priority.weight() - 1);
DCHECK_EQ(GetPrioritySize(), builder.length());
return builder.take();
}
namespace {
class FrameSerializationVisitor : public SpdyFrameVisitor {
public:
explicit FrameSerializationVisitor(SpdyFramer* framer)
: framer_(framer), frame_() {}
~FrameSerializationVisitor() override {}
SpdySerializedFrame ReleaseSerializedFrame() { return std::move(frame_); }
void VisitData(const SpdyDataIR& data) override {
frame_ = framer_->SerializeData(data);
}
void VisitRstStream(const SpdyRstStreamIR& rst_stream) override {
frame_ = framer_->SerializeRstStream(rst_stream);
}
void VisitSettings(const SpdySettingsIR& settings) override {
frame_ = framer_->SerializeSettings(settings);
}
void VisitPing(const SpdyPingIR& ping) override {
frame_ = framer_->SerializePing(ping);
}
void VisitGoAway(const SpdyGoAwayIR& goaway) override {
frame_ = framer_->SerializeGoAway(goaway);
}
void VisitHeaders(const SpdyHeadersIR& headers) override {
frame_ = framer_->SerializeHeaders(headers);
}
void VisitWindowUpdate(const SpdyWindowUpdateIR& window_update) override {
frame_ = framer_->SerializeWindowUpdate(window_update);
}
void VisitBlocked(const SpdyBlockedIR& blocked) override {
frame_ = framer_->SerializeBlocked(blocked);
}
void VisitPushPromise(const SpdyPushPromiseIR& push_promise) override {
frame_ = framer_->SerializePushPromise(push_promise);
}
void VisitContinuation(const SpdyContinuationIR& continuation) override {
frame_ = framer_->SerializeContinuation(continuation);
}
void VisitAltSvc(const SpdyAltSvcIR& altsvc) override {
frame_ = framer_->SerializeAltSvc(altsvc);
}
void VisitPriority(const SpdyPriorityIR& priority) override {
frame_ = framer_->SerializePriority(priority);
}
private:
SpdyFramer* framer_;
SpdySerializedFrame frame_;
};
// TODO(diannahu): Use also in frame serialization.
class FlagsSerializationVisitor : public SpdyFrameVisitor {
public:
void VisitData(const SpdyDataIR& data) override {
flags_ = DATA_FLAG_NONE;
if (data.fin()) {
flags_ |= DATA_FLAG_FIN;
}
if (data.padded()) {
flags_ |= DATA_FLAG_PADDED;
}
}
void VisitRstStream(const SpdyRstStreamIR& rst_stream) override {
flags_ = kNoFlags;
}
void VisitSettings(const SpdySettingsIR& settings) override {
flags_ = kNoFlags;
if (settings.is_ack()) {
flags_ |= SETTINGS_FLAG_ACK;
}
}
void VisitPing(const SpdyPingIR& ping) override {
flags_ = kNoFlags;
if (ping.is_ack()) {
flags_ |= PING_FLAG_ACK;
}
}
void VisitGoAway(const SpdyGoAwayIR& goaway) override { flags_ = kNoFlags; }
// TODO(diannahu): The END_HEADERS flag is incorrect for HEADERS that require
// CONTINUATION frames.
void VisitHeaders(const SpdyHeadersIR& headers) override {
flags_ = HEADERS_FLAG_END_HEADERS;
if (headers.fin()) {
flags_ |= CONTROL_FLAG_FIN;
}
if (headers.padded()) {
flags_ |= HEADERS_FLAG_PADDED;
}
if (headers.has_priority()) {
flags_ |= HEADERS_FLAG_PRIORITY;
}
}
void VisitWindowUpdate(const SpdyWindowUpdateIR& window_update) override {
flags_ = kNoFlags;
}
void VisitBlocked(const SpdyBlockedIR& blocked) override {
flags_ = kNoFlags;
}
// TODO(diannahu): The END_PUSH_PROMISE flag is incorrect for PUSH_PROMISEs
// that require CONTINUATION frames.
void VisitPushPromise(const SpdyPushPromiseIR& push_promise) override {
flags_ = PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
if (push_promise.padded()) {
flags_ |= PUSH_PROMISE_FLAG_PADDED;
}
}
// TODO(diannahu): The END_HEADERS flag is incorrect for CONTINUATIONs that
// require CONTINUATION frames.
void VisitContinuation(const SpdyContinuationIR& continuation) override {
flags_ = HEADERS_FLAG_END_HEADERS;
}
void VisitAltSvc(const SpdyAltSvcIR& altsvc) override { flags_ = kNoFlags; }
void VisitPriority(const SpdyPriorityIR& priority) override {
flags_ = kNoFlags;
}
uint8_t flags() const { return flags_; }
private:
uint8_t flags_ = kNoFlags;
};
} // namespace
SpdySerializedFrame SpdyFramer::SerializeFrame(const SpdyFrameIR& frame) {
FrameSerializationVisitor visitor(this);
frame.Visit(&visitor);
return visitor.ReleaseSerializedFrame();
}
uint8_t SpdyFramer::GetSerializedFlags(const SpdyFrameIR& frame) {
FlagsSerializationVisitor visitor;
frame.Visit(&visitor);
return visitor.flags();
}
bool SpdyFramer::SerializeData(const SpdyDataIR& data_ir,
ZeroCopyOutputBuffer* output) const {
uint8_t flags = DATA_FLAG_NONE;
int num_padding_fields = 0;
size_t size_with_padding = 0;
SerializeDataBuilderHelper(data_ir, &flags, &num_padding_fields,
&size_with_padding);
SpdyFrameBuilder builder(size_with_padding, output);
bool ok = builder.BeginNewFrame(*this, DATA, flags, data_ir.stream_id());
if (data_ir.padded()) {
ok = ok && builder.WriteUInt8(data_ir.padding_payload_len() & 0xff);
}
ok = ok && builder.WriteBytes(data_ir.data(), data_ir.data_len());
if (data_ir.padding_payload_len() > 0) {
string padding;
padding = string(data_ir.padding_payload_len(), 0);
ok = ok && builder.WriteBytes(padding.data(), padding.length());
}
DCHECK_EQ(size_with_padding, builder.length());
return ok;
}
bool SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField(
const SpdyDataIR& data_ir,
ZeroCopyOutputBuffer* output) const {
uint8_t flags = DATA_FLAG_NONE;
size_t frame_size = 0;
size_t num_padding_fields = 0;
SerializeDataFrameHeaderWithPaddingLengthFieldBuilderHelper(
data_ir, &flags, &frame_size, &num_padding_fields);
SpdyFrameBuilder builder(frame_size, output);
bool ok = true;
if (!skip_rewritelength_) {
ok = builder.BeginNewFrame(*this, DATA, flags, data_ir.stream_id());
if (data_ir.padded()) {
ok = ok && builder.WriteUInt8(data_ir.padding_payload_len() & 0xff);
}
ok = ok && builder.OverwriteLength(*this,
num_padding_fields + data_ir.data_len() +
data_ir.padding_payload_len());
} else {
ok = ok && builder.BeginNewFrame(*this, DATA, flags, data_ir.stream_id(),
num_padding_fields + data_ir.data_len() +
data_ir.padding_payload_len());
if (data_ir.padded()) {
ok = ok && builder.WriteUInt8(data_ir.padding_payload_len() & 0xff);
}
}
DCHECK_EQ(frame_size, builder.length());
return ok;
}
bool SpdyFramer::SerializeRstStream(const SpdyRstStreamIR& rst_stream,
ZeroCopyOutputBuffer* output) const {
size_t expected_length = GetRstStreamSize();
SpdyFrameBuilder builder(expected_length, output);
bool ok = builder.BeginNewFrame(*this, RST_STREAM, 0, rst_stream.stream_id());
ok = ok && builder.WriteUInt32(rst_stream.error_code());
DCHECK_EQ(expected_length, builder.length());
return ok;
}
bool SpdyFramer::SerializeSettings(const SpdySettingsIR& settings,
ZeroCopyOutputBuffer* output) const {
uint8_t flags = 0;
// Size, in bytes, of this SETTINGS frame.
size_t size = 0;
const SettingsMap* values = &(settings.values());
SerializeSettingsBuilderHelper(settings, &flags, values, &size);
SpdyFrameBuilder builder(size, output);
bool ok = builder.BeginNewFrame(*this, SETTINGS, flags, 0);
// If this is an ACK, payload should be empty.
if (settings.is_ack()) {
return ok;
}
DCHECK_EQ(GetSettingsMinimumSize(), builder.length());
for (SettingsMap::const_iterator it = values->begin(); it != values->end();
++it) {
int setting_id = it->first;
DCHECK_GE(setting_id, 0);
ok = ok && builder.WriteUInt16(static_cast<uint16_t>(setting_id)) &&
builder.WriteUInt32(it->second);
}
DCHECK_EQ(size, builder.length());
return ok;
}
bool SpdyFramer::SerializePing(const SpdyPingIR& ping,
ZeroCopyOutputBuffer* output) const {
SpdyFrameBuilder builder(GetPingSize(), output);
uint8_t flags = 0;
if (ping.is_ack()) {
flags |= PING_FLAG_ACK;
}
bool ok = builder.BeginNewFrame(*this, PING, flags, 0);
ok = ok && builder.WriteUInt64(ping.id());
DCHECK_EQ(GetPingSize(), builder.length());
return ok;
}
bool SpdyFramer::SerializeGoAway(const SpdyGoAwayIR& goaway,
ZeroCopyOutputBuffer* output) const {
// Compute the output buffer size, take opaque data into account.
size_t expected_length = GetGoAwayMinimumSize();
expected_length += goaway.description().size();
SpdyFrameBuilder builder(expected_length, output);
// Serialize the GOAWAY frame.
bool ok = builder.BeginNewFrame(*this, GOAWAY, 0, 0);
// GOAWAY frames specify the last good stream id.
ok = ok && builder.WriteUInt32(goaway.last_good_stream_id()) &&
// GOAWAY frames also specify the error status code.
builder.WriteUInt32(goaway.error_code());
// GOAWAY frames may also specify opaque data.
if (!goaway.description().empty()) {
ok = ok && builder.WriteBytes(goaway.description().data(),
goaway.description().size());
}
DCHECK_EQ(expected_length, builder.length());
return ok;
}
bool SpdyFramer::SerializeHeaders(const SpdyHeadersIR& headers,
ZeroCopyOutputBuffer* output) {
uint8_t flags = 0;
// The size of this frame, including padding (if there is any) and
// variable-length header block.
size_t size = 0;
string hpack_encoding;
int weight = 0;
size_t length_field = 0;
SerializeHeadersBuilderHelper(headers, &flags, &size, &hpack_encoding,
&weight, &length_field);
bool ok = true;
SpdyFrameBuilder builder(size, output);
if (!skip_rewritelength_) {
ok = builder.BeginNewFrame(*this, HEADERS, flags, headers.stream_id());
} else {
ok = ok && builder.BeginNewFrame(*this, HEADERS, flags, headers.stream_id(),
length_field);
}
DCHECK_EQ(GetHeadersMinimumSize(), builder.length());
int padding_payload_len = 0;
if (headers.padded()) {
ok = ok && builder.WriteUInt8(headers.padding_payload_len());
padding_payload_len = headers.padding_payload_len();
}
if (headers.has_priority()) {
ok = ok &&
builder.WriteUInt32(PackStreamDependencyValues(
headers.exclusive(), headers.parent_stream_id())) &&
// Per RFC 7540 section 6.3, serialized weight value is weight - 1.
builder.WriteUInt8(weight - 1);
}
ok = ok && WritePayloadWithContinuation(&builder, hpack_encoding,
headers.stream_id(), HEADERS,
padding_payload_len);
if (debug_visitor_) {
// HTTP2 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(&(headers.header_block()));
debug_visitor_->OnSendCompressedFrame(headers.stream_id(), HEADERS,
payload_len, builder.length());
}
return ok;
}
bool SpdyFramer::SerializeWindowUpdate(const SpdyWindowUpdateIR& window_update,
ZeroCopyOutputBuffer* output) const {
SpdyFrameBuilder builder(GetWindowUpdateSize(), output);
bool ok = builder.BeginNewFrame(*this, WINDOW_UPDATE, kNoFlags,
window_update.stream_id());
ok = ok && builder.WriteUInt32(window_update.delta());
DCHECK_EQ(GetWindowUpdateSize(), builder.length());
return ok;
}
bool SpdyFramer::SerializeBlocked(const SpdyBlockedIR& blocked,
ZeroCopyOutputBuffer* output) const {
SpdyFrameBuilder builder(GetBlockedSize(), output);
return builder.BeginNewFrame(*this, BLOCKED, kNoFlags, blocked.stream_id());
}
bool SpdyFramer::SerializePushPromise(const SpdyPushPromiseIR& push_promise,
ZeroCopyOutputBuffer* output) {
uint8_t flags = 0;
size_t size = 0;
string hpack_encoding;
SerializePushPromiseBuilderHelper(push_promise, &flags, &hpack_encoding,
&size);
bool ok = true;
SpdyFrameBuilder builder(size, output);
if (!skip_rewritelength_) {
ok = builder.BeginNewFrame(*this, PUSH_PROMISE, flags,
push_promise.stream_id());
} else {
size_t length = std::min(size, kMaxControlFrameSize) - GetFrameHeaderSize();
ok = builder.BeginNewFrame(*this, PUSH_PROMISE, flags,
push_promise.stream_id(), length);
}
int padding_payload_len = 0;
if (push_promise.padded()) {
ok = ok && 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 {
ok = ok && builder.WriteUInt32(push_promise.promised_stream_id());
DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length());
}
ok = ok && WritePayloadWithContinuation(&builder, hpack_encoding,
push_promise.stream_id(),
PUSH_PROMISE, padding_payload_len);
if (debug_visitor_) {
// HTTP2 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(&(push_promise.header_block()));
debug_visitor_->OnSendCompressedFrame(
push_promise.stream_id(), PUSH_PROMISE, payload_len, builder.length());
}
return ok;
}
bool SpdyFramer::SerializeContinuation(const SpdyContinuationIR& continuation,
ZeroCopyOutputBuffer* output) const {
const string& encoding = continuation.encoding();
size_t frame_size = GetContinuationMinimumSize() + encoding.size();
SpdyFrameBuilder builder(frame_size, output);
uint8_t flags = continuation.end_headers() ? HEADERS_FLAG_END_HEADERS : 0;
bool ok = builder.BeginNewFrame(*this, CONTINUATION, flags,
continuation.stream_id());
DCHECK_EQ(GetFrameHeaderSize(), builder.length());
ok = ok && builder.WriteBytes(encoding.data(), encoding.size());
return ok;
}
bool SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc_ir,
ZeroCopyOutputBuffer* output) {
string value;
size_t size = 0;
SerializeAltSvcBuilderHelper(altsvc_ir, &value, &size);
SpdyFrameBuilder builder(size, output);
bool ok =
builder.BeginNewFrame(*this, ALTSVC, kNoFlags, altsvc_ir.stream_id()) &&
builder.WriteUInt16(altsvc_ir.origin().length()) &&
builder.WriteBytes(altsvc_ir.origin().data(),
altsvc_ir.origin().length()) &&
builder.WriteBytes(value.data(), value.length());
DCHECK_LT(GetAltSvcMinimumSize(), builder.length());
return ok;
}
bool SpdyFramer::SerializePriority(const SpdyPriorityIR& priority,
ZeroCopyOutputBuffer* output) const {
size_t size = GetPrioritySize();
SpdyFrameBuilder builder(size, output);
bool ok =
builder.BeginNewFrame(*this, PRIORITY, kNoFlags, priority.stream_id());
ok = ok &&
builder.WriteUInt32(PackStreamDependencyValues(
priority.exclusive(), priority.parent_stream_id())) &&
// Per RFC 7540 section 6.3, serialized weight value is actual value - 1.
builder.WriteUInt8(priority.weight() - 1);
DCHECK_EQ(GetPrioritySize(), builder.length());
return ok;
}
namespace {
class FrameSerializationVisitorWithOutput : public SpdyFrameVisitor {
public:
explicit FrameSerializationVisitorWithOutput(SpdyFramer* framer,
ZeroCopyOutputBuffer* output)
: framer_(framer), output_(output), result_(false) {}
~FrameSerializationVisitorWithOutput() override {}
bool Result() { return result_; }
void VisitData(const SpdyDataIR& data) override {
result_ = framer_->SerializeData(data, output_);
}
void VisitRstStream(const SpdyRstStreamIR& rst_stream) override {
result_ = framer_->SerializeRstStream(rst_stream, output_);
}
void VisitSettings(const SpdySettingsIR& settings) override {
result_ = framer_->SerializeSettings(settings, output_);
}
void VisitPing(const SpdyPingIR& ping) override {
result_ = framer_->SerializePing(ping, output_);
}
void VisitGoAway(const SpdyGoAwayIR& goaway) override {
result_ = framer_->SerializeGoAway(goaway, output_);
}
void VisitHeaders(const SpdyHeadersIR& headers) override {
result_ = framer_->SerializeHeaders(headers, output_);
}
void VisitWindowUpdate(const SpdyWindowUpdateIR& window_update) override {
result_ = framer_->SerializeWindowUpdate(window_update, output_);
}
void VisitBlocked(const SpdyBlockedIR& blocked) override {
result_ = framer_->SerializeBlocked(blocked, output_);
}
void VisitPushPromise(const SpdyPushPromiseIR& push_promise) override {
result_ = framer_->SerializePushPromise(push_promise, output_);
}
void VisitContinuation(const SpdyContinuationIR& continuation) override {
result_ = framer_->SerializeContinuation(continuation, output_);
}
void VisitAltSvc(const SpdyAltSvcIR& altsvc) override {
result_ = framer_->SerializeAltSvc(altsvc, output_);
}
void VisitPriority(const SpdyPriorityIR& priority) override {
result_ = framer_->SerializePriority(priority, output_);
}
private:
SpdyFramer* framer_;
ZeroCopyOutputBuffer* output_;
bool result_;
};
} // namespace
bool SpdyFramer::SerializeFrame(const SpdyFrameIR& frame,
ZeroCopyOutputBuffer* output) {
FrameSerializationVisitorWithOutput visitor(this, output);
frame.Visit(&visitor);
return visitor.Result();
}
size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) {
DCHECK_GT(size, kMaxControlFrameSize);
size_t overflow = size - kMaxControlFrameSize;
size_t payload_size = kMaxControlFrameSize - GetContinuationMinimumSize();
// This is ceiling(overflow/payload_size) using integer arithmetics.
return (overflow - 1) / payload_size + 1;
}
size_t SpdyFramer::GetHeaderFrameSizeSansBlock(
const SpdyHeadersIR& header_ir) const {
size_t min_size = GetFrameHeaderSize();
if (header_ir.padded()) {
min_size += 1;
min_size += header_ir.padding_payload_len();
}
if (header_ir.has_priority()) {
min_size += 5;
}
return min_size;
}
uint8_t SpdyFramer::SerializeHeaderFrameFlags(
const SpdyHeadersIR& header_ir) const {
uint8_t flags = 0;
if (header_ir.fin()) {
flags |= CONTROL_FLAG_FIN;
}
if (header_ir.end_headers()) {
flags |= HEADERS_FLAG_END_HEADERS;
}
if (header_ir.padded()) {
flags |= HEADERS_FLAG_PADDED;
}
if (header_ir.has_priority()) {
flags |= HEADERS_FLAG_PRIORITY;
}
return flags;
}
bool SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder,
const string& hpack_encoding,
SpdyStreamId stream_id,
SpdyFrameType type,
int padding_payload_len) {
uint8_t end_flag = 0;
uint8_t 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);
bool ret = builder->WriteBytes(&hpack_encoding[0],
hpack_encoding.size() - bytes_remaining);
if (padding_payload_len > 0) {
string padding = string(padding_payload_len, 0);
ret &= builder->WriteBytes(padding.data(), padding.length());
}
if (bytes_remaining > 0 && !skip_rewritelength_) {
ret &= builder->OverwriteLength(
*this, kMaxControlFrameSize - GetFrameHeaderSize());
}
// Tack on CONTINUATION frames for the overflow.
while (bytes_remaining > 0 && ret) {
size_t bytes_to_write = std::min(
bytes_remaining, kMaxControlFrameSize - GetContinuationMinimumSize());
// Write CONTINUATION frame prefix.
if (bytes_remaining == bytes_to_write) {
flags |= end_flag;
}
if (!skip_rewritelength_) {
ret &= builder->BeginNewFrame(*this, CONTINUATION, flags, stream_id);
} else {
ret &= builder->BeginNewFrame(*this, CONTINUATION, flags, stream_id,
bytes_to_write);
}
// Write payload fragment.
ret &= builder->WriteBytes(
&hpack_encoding[hpack_encoding.size() - bytes_remaining],
bytes_to_write);
bytes_remaining -= bytes_to_write;
}
return ret;
}
HpackEncoder* SpdyFramer::GetHpackEncoder() {
if (hpack_encoder_.get() == nullptr) {
hpack_encoder_.reset(new HpackEncoder(ObtainHpackHuffmanTable()));
if (!compression_enabled()) {
hpack_encoder_->DisableCompression();
}
}
return hpack_encoder_.get();
}
HpackDecoderInterface* SpdyFramer::GetHpackDecoder() {
if (hpack_decoder_.get() == nullptr) {
if (FLAGS_chromium_http2_flag_spdy_use_hpack_decoder3) {
hpack_decoder_.reset(new HpackDecoder3());
} else {
hpack_decoder_.reset(new HpackDecoder());
}
}
return hpack_decoder_.get();
}
void SpdyFramer::SetDecoderHeaderTableDebugVisitor(
std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) {
if (decoder_adapter_ != nullptr) {
decoder_adapter_->SetDecoderHeaderTableDebugVisitor(std::move(visitor));
} else {
GetHpackDecoder()->SetHeaderTableDebugVisitor(std::move(visitor));
}
}
void SpdyFramer::SetEncoderHeaderTableDebugVisitor(
std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) {
GetHpackEncoder()->SetHeaderTableDebugVisitor(std::move(visitor));
}
size_t SpdyFramer::EstimateMemoryUsage() const {
return SpdyEstimateMemoryUsage(current_frame_buffer_) +
SpdyEstimateMemoryUsage(settings_scratch_) +
SpdyEstimateMemoryUsage(altsvc_scratch_) +
SpdyEstimateMemoryUsage(hpack_encoder_) +
SpdyEstimateMemoryUsage(hpack_decoder_) +
SpdyEstimateMemoryUsage(decoder_adapter_);
}
void SpdyFramer::UpdateHeaderEncoderTableSize(uint32_t value) {
GetHpackEncoder()->ApplyHeaderTableSizeSetting(value);
}
void SpdyFramer::UpdateHeaderDecoderTableSize(uint32_t value) {
GetHpackDecoder()->ApplyHeaderTableSizeSetting(value);
}
size_t SpdyFramer::header_encoder_table_size() const {
if (hpack_encoder_ == nullptr) {
return kDefaultHeaderTableSizeSetting;
} else {
return hpack_encoder_->CurrentHeaderTableSizeSetting();
}
}
void SpdyFramer::SerializeHeaderBlockWithoutCompression(
SpdyFrameBuilder* builder,
const SpdyHeaderBlock& header_block) const {
// Serialize number of headers.
builder->WriteUInt32(header_block.size());
// Serialize each header.
for (const auto& header : header_block) {
builder->WriteStringPiece32(base::ToLowerASCII(header.first));
builder->WriteStringPiece32(header.second);
}
}
} // namespace net