This source file includes following definitions.
- CalculateDictionaryId
- FromWireFormat
- id_
- GetWireFormat
- ConvertFlagsAndIdForSpdy2
- OnGoAwayFrameData
- OnRstStreamFrameData
- end_stream_when_done_
- Reset
- GetDataFrameMinimumSize
- GetControlFrameHeaderSize
- GetSynStreamMinimumSize
- GetSynReplyMinimumSize
- GetRstStreamMinimumSize
- GetSettingsMinimumSize
- GetPingSize
- GetGoAwayMinimumSize
- GetHeadersMinimumSize
- GetWindowUpdateSize
- GetBlockedSize
- GetPushPromiseMinimumSize
- GetContinuationMinimumSize
- GetFrameMinimumSize
- GetFrameMaximumSize
- GetDataFrameMaximumPayload
- StateToString
- set_error
- ErrorCodeToString
- StatusCodeToString
- FrameTypeToString
- ProcessInput
- ProcessCommonHeader
- ProcessControlFrameHeader
- UpdateCurrentFrameBuffer
- GetSerializedLength
- WriteHeaderBlock
- WriteZ
- WriteLengthZ
- WriteHeaderBlockToZ
- ProcessControlFrameBeforeHeaderBlock
- ProcessControlFrameHeaderBlock
- ProcessSettingsFramePayload
- DeliverHpackBlockAsSpdy3Block
- ProcessSetting
- ProcessControlFramePayload
- ProcessGoAwayFramePayload
- ProcessRstStreamFramePayload
- ProcessFramePaddingLength
- ProcessFramePadding
- ProcessDataFramePayload
- ParseHeaderBlockInBuffer
- SerializeData
- SerializeDataFrameHeader
- SerializeSynStream
- SerializeSynReply
- SerializeRstStream
- SerializeSettings
- SerializeBlocked
- SerializePing
- SerializeGoAway
- SerializeHeaders
- SerializeWindowUpdate
- SerializePushPromise
- SerializeContinuation
- ReleaseSerializedFrame
- VisitData
- VisitSynStream
- VisitSynReply
- VisitRstStream
- VisitSettings
- VisitPing
- VisitGoAway
- VisitHeaders
- VisitWindowUpdate
- VisitBlocked
- VisitPushPromise
- VisitContinuation
- SerializeFrame
- GetSerializedLength
- GetHeaderCompressor
- GetHeaderDecompressor
- IncrementallyDecompressControlFrameHeaderData
- IncrementallyDeliverControlFrameHeaderData
- SerializeNameValueBlockWithoutCompression
- SerializeNameValueBlock
#include "net/spdy/spdy_framer.h"
#include "base/lazy_instance.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/stats_counters.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 std::string;
using std::vector;
namespace net {
namespace {
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);
}
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;
};
base::LazyInstance<DictionaryIds>::Leaky g_dictionary_ids;
const uint8 kNoFlags = 0;
}
const SpdyStreamId SpdyFramer::kInvalidStream = -1;
const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024;
const size_t SpdyFramer::kControlFrameBufferSize = 18;
#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(int version,
uint32 wire) {
if (version < 3) {
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(int version) const {
uint32 wire = htonl(id_ & 0x00ffffff) | htonl(flags_ << 24);
if (version < 3) {
ConvertFlagsAndIdForSpdy2(&wire);
}
return wire;
}
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]);
}
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),
hpack_decoder_(ObtainHpackHuffmanTable()),
visitor_(NULL),
debug_visitor_(NULL),
display_protocol_("SPDY"),
spdy_version_(version),
syn_frame_processed_(false),
probable_http_response_(false),
expect_continuation_(0),
end_stream_when_done_(false) {
DCHECK_GE(spdy_version_, SPDY_MIN_VERSION);
DCHECK_LE(spdy_version_, SPDY_MAX_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();
remaining_padding_payload_length_ = 0;
remaining_padding_length_fields_ = 0;
}
size_t SpdyFramer::GetDataFrameMinimumSize() const {
return 8;
}
size_t SpdyFramer::GetControlFrameHeaderSize() const {
switch (protocol_version()) {
case SPDY2:
case SPDY3:
case SPDY4:
return 8;
}
LOG(DFATAL) << "Unhandled SPDY version.";
return 0;
}
size_t SpdyFramer::GetSynStreamMinimumSize() const {
if (protocol_version() <= SPDY3) {
return GetControlFrameHeaderSize() + 10;
} else {
return GetControlFrameHeaderSize() + 4;
}
}
size_t SpdyFramer::GetSynReplyMinimumSize() const {
size_t size = GetControlFrameHeaderSize();
if (protocol_version() <= SPDY3) {
size += 4;
}
if (protocol_version() < SPDY3) {
size += 2;
}
return size;
}
size_t SpdyFramer::GetRstStreamMinimumSize() const {
if (protocol_version() <= SPDY3) {
return GetControlFrameHeaderSize() + 8;
} else {
return GetControlFrameHeaderSize() + 4;
}
}
size_t SpdyFramer::GetSettingsMinimumSize() const {
if (protocol_version() <= SPDY3) {
return GetControlFrameHeaderSize() + 4;
} else {
return GetControlFrameHeaderSize();
}
}
size_t SpdyFramer::GetPingSize() const {
if (protocol_version() <= SPDY3) {
return GetControlFrameHeaderSize() + 4;
} else {
return GetControlFrameHeaderSize() + 8;
}
}
size_t SpdyFramer::GetGoAwayMinimumSize() const {
size_t size = GetControlFrameHeaderSize();
size += 4;
if (protocol_version() >= 3) {
size += 4;
}
return size;
}
size_t SpdyFramer::GetHeadersMinimumSize() const {
size_t size = GetControlFrameHeaderSize();
if (protocol_version() <= SPDY3) {
size += 4;
}
if (protocol_version() <= SPDY2) {
size += 2;
}
return size;
}
size_t SpdyFramer::GetWindowUpdateSize() const {
if (protocol_version() <= SPDY3) {
return GetControlFrameHeaderSize() + 8;
} else {
return GetControlFrameHeaderSize() + 4;
}
}
size_t SpdyFramer::GetBlockedSize() const {
DCHECK_LE(4, protocol_version());
return GetControlFrameHeaderSize();
}
size_t SpdyFramer::GetPushPromiseMinimumSize() const {
DCHECK_LE(4, protocol_version());
return GetControlFrameHeaderSize() + 4;
}
size_t SpdyFramer::GetContinuationMinimumSize() const {
return GetControlFrameHeaderSize();
}
size_t SpdyFramer::GetFrameMinimumSize() const {
return std::min(GetDataFrameMinimumSize(), GetControlFrameHeaderSize());
}
size_t SpdyFramer::GetFrameMaximumSize() const {
if (protocol_version() <= SPDY3) {
return ((1<<24) - 1) + 8;
} else {
return (1<<14) - 1;
}
}
size_t SpdyFramer::GetDataFrameMaximumPayload() const {
return GetFrameMaximumSize() - GetDataFrameMinimumSize();
}
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_PADDING_LENGTH:
return "SPDY_READ_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";
}
return "UNKNOWN_STATE";
}
void SpdyFramer::set_error(SpdyError error) {
DCHECK(visitor_);
error_code_ = error;
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";
}
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 NOOP:
return "NOOP";
case PING:
return "PING";
case GOAWAY:
return "GOAWAY";
case HEADERS:
return "HEADERS";
case WINDOW_UPDATE:
return "WINDOW_UPDATE";
case CREDENTIAL:
return "CREDENTIAL";
case BLOCKED:
return "BLOCKED";
case PUSH_PROMISE:
return "PUSH_PROMISE";
case CONTINUATION:
return "CONTINUATION";
}
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: {
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_CONTROL_FRAME_PAYLOAD: {
size_t bytes_read = ProcessControlFramePayload(data, len);
len -= bytes_read;
data += bytes_read;
break;
}
case SPDY_READ_PADDING_LENGTH: {
size_t bytes_read = ProcessFramePaddingLength(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:
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_;
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) {
DCHECK_EQ(state_, SPDY_READING_COMMON_HEADER);
size_t original_len = len;
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()) {
return original_len - len;
}
scoped_ptr<SpdyFrameReader> reader(
new SpdyFrameReader(current_frame_buffer_.get(),
current_frame_buffer_length_));
uint16 version = 0;
bool is_control_frame = false;
uint16 control_frame_type_field = DATA;
current_frame_type_ = DATA;
if (protocol_version() <= SPDY3) {
bool successful_read = reader->ReadUInt16(&version);
DCHECK(successful_read);
is_control_frame = (version & kControlFlagMask) != 0;
version &= ~kControlFlagMask;
if (is_control_frame) {
successful_read = reader->ReadUInt16(&control_frame_type_field);
} 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 {
version = protocol_version();
uint16 length_field = 0;
bool successful_read = reader->ReadUInt16(&length_field);
DCHECK(successful_read);
uint8 control_frame_type_field_uint8 = DATA;
successful_read = reader->ReadUInt8(&control_frame_type_field_uint8);
DCHECK(successful_read);
control_frame_type_field = control_frame_type_field_uint8;
is_control_frame = (control_frame_type_field != 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();
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());
if (remaining_data_length_ > 1000000u) {
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 (!is_control_frame) {
if (protocol_version() > SPDY3) {
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_PAD_LOW | DATA_FLAG_PAD_HIGH;
} 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_PADDING_LENGTH);
} else {
if (current_frame_flags_ & DATA_FLAG_FIN) {
visitor_->OnStreamFrameData(
current_frame_stream_id_, NULL, 0, true);
}
CHANGE_STATE(SPDY_AUTO_RESET);
}
}
} else if (version != protocol_version()) {
DVLOG(1) << "Unsupported SPDY version " << version
<< " (expected " << protocol_version() << ")";
set_error(SPDY_UNSUPPORTED_VERSION);
} else {
ProcessControlFrameHeader(control_frame_type_field);
}
return original_len - len;
}
void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
DCHECK_EQ(SPDY_NO_ERROR, error_code_);
DCHECK_LE(GetControlFrameHeaderSize(), current_frame_buffer_length_);
if (protocol_version() <= SPDY3) {
if (control_frame_type_field == NOOP) {
current_frame_type_ = NOOP;
DVLOG(1) << "NOOP control frame found. Ignoring.";
CHANGE_STATE(SPDY_AUTO_RESET);
return;
}
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)) {
DLOG(WARNING) << "Invalid control frame type " << control_frame_type_field
<< " (protocol version: " << protocol_version() << ")";
set_error(SPDY_INVALID_CONTROL_FRAME);
return;
}
current_frame_type_ = SpdyConstants::ParseFrameType(protocol_version(),
control_frame_type_field);
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:
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:
{
size_t values_prefix_size = (protocol_version() <= SPDY3 ? 4 : 0);
size_t setting_size = (protocol_version() <= SPDY3 ? 8 : 5);
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:
{
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) {
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)) {
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()) {
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) {
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
}
break;
case CONTINUATION:
if (current_frame_length_ < GetContinuationMinimumSize()) {
set_error(SPDY_INVALID_CONTROL_FRAME);
} else if (current_frame_flags_ & ~HEADERS_FLAG_END_HEADERS) {
set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
}
break;
default:
LOG(WARNING) << "Valid " << display_protocol_
<< " control frame with unhandled type: "
<< current_frame_type_;
DLOG(FATAL);
set_error(SPDY_INVALID_CONTROL_FRAME);
break;
}
if (state_ == SPDY_ERROR) {
return;
}
if (current_frame_length_ > GetControlFrameBufferMaxSize()) {
DLOG(WARNING) << "Received control frame 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;
}
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 &&
current_frame_flags_ & HEADERS_FLAG_PRIORITY) {
frame_size_without_variable_data += 4;
}
break;
case PUSH_PROMISE:
frame_size_without_variable_data = GetPushPromiseMinimumSize();
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)) {
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) {
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 int spdy_version,
const SpdyHeaderBlock* headers) {
const size_t num_name_value_pairs_size
= (spdy_version < 3) ? 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) {
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 int spdy_version,
const SpdyHeaderBlock* headers) {
if (spdy_version < 3) {
frame->WriteUInt16(headers->size());
} else {
frame->WriteUInt32(headers->size());
}
SpdyHeaderBlock::const_iterator it;
for (it = headers->begin(); it != headers->end(); ++it) {
if (spdy_version < 3) {
frame->WriteString(it->first);
frame->WriteString(it->second);
} else {
frame->WriteStringPiece32(it->first);
frame->WriteStringPiece32(it->second);
}
}
}
#if !defined(USE_SYSTEM_ZLIB)
enum ZDataClass {
kZStandardData = Z_CLASS_STANDARD,
kZCookieData = Z_CLASS_COOKIE,
kZHuffmanOnlyData = Z_CLASS_HUFFMAN_ONLY,
};
static void WriteZ(const base::StringPiece& data,
ZDataClass clas,
z_stream* out) {
int rv;
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()) {
DCHECK_EQ(Z_OK, rv);
}
DCHECK_EQ(0u, out->avail_in);
DCHECK_LT(0u, out->avail_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] = n;
n >>= 8;
}
WriteZ(base::StringPiece(buf, length), clas, out);
}
void SpdyFramer::WriteHeaderBlockToZ(const SpdyHeaderBlock* headers,
z_stream* z) const {
unsigned length_length = 4;
if (spdy_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") {
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()) {
cookie_values.push_back(cookie_data.substr(0, i));
cookie_length += i + 2 ;
cookie_data.remove_prefix(i + 1);
} else {
cookie_values.push_back(cookie_data);
cookie_length += cookie_data.size();
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;
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 {
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
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());
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;
}
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:
{
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) {
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;
}
const bool has_priority =
(current_frame_flags_ & HEADERS_FLAG_PRIORITY) != 0;
uint32 priority = 0;
if (protocol_version() > SPDY3 && has_priority) {
successful_read = reader.ReadUInt31(&priority);
DCHECK(successful_read);
}
DCHECK(reader.IsDoneReading());
if (debug_visitor_) {
SpdyFrameType reported_type = current_frame_type_;
if (protocol_version() > SPDY3 && has_priority) {
reported_type = SYN_STREAM;
}
debug_visitor_->OnReceiveCompressedFrame(
current_frame_stream_id_,
reported_type,
current_frame_length_);
}
if (current_frame_type_ == SYN_REPLY) {
visitor_->OnSynReply(
current_frame_stream_id_,
(current_frame_flags_ & CONTROL_FLAG_FIN) != 0);
} else if (protocol_version() > SPDY3 &&
current_frame_flags_ & HEADERS_FLAG_PRIORITY) {
visitor_->OnSynStream(
current_frame_stream_id_,
0,
priority,
current_frame_flags_ & CONTROL_FLAG_FIN,
false);
} else {
visitor_->OnHeaders(
current_frame_stream_id_,
(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;
}
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:
{
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;
}
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_);
if (is_hpack_header_block) {
if (!hpack_decoder_.HandleControlFrameHeadersData(current_frame_stream_id_,
data,
process_bytes)) {
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;
if (remaining_data_length_ == 0 && processed_successfully) {
if (expect_continuation_ == 0) {
if (is_hpack_header_block) {
if (!hpack_decoder_.HandleControlFrameHeadersComplete(
current_frame_stream_id_)) {
set_error(SPDY_DECOMPRESS_FAILURE);
processed_successfully = false;
} else {
DeliverHpackBlockAsSpdy3Block();
return process_bytes;
}
} else {
visitor_->OnControlFrameHeaderData(current_frame_stream_id_, NULL, 0);
}
if ((current_frame_flags_ & CONTROL_FLAG_FIN) || end_stream_when_done_) {
end_stream_when_done_ = false;
visitor_->OnStreamFrameData(current_frame_stream_id_, NULL, 0, true);
}
}
if (processed_successfully)
CHANGE_STATE(SPDY_AUTO_RESET);
}
if (!processed_successfully) {
return data_len;
}
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 = protocol_version() <= SPDY3 ? 8 : 5;
while (unprocessed_bytes > 0) {
size_t processing = std::min(
unprocessed_bytes,
static_cast<size_t>(setting_size - settings_scratch_.setting_buf_len));
if (processing == setting_size) {
if (!ProcessSetting(data + processed_bytes)) {
set_error(SPDY_INVALID_CONTROL_FRAME);
return processed_bytes;
}
} else {
memcpy(settings_scratch_.setting_buf + settings_scratch_.setting_buf_len,
data + processed_bytes,
processing);
settings_scratch_.setting_buf_len += processing;
if (settings_scratch_.setting_buf_len == setting_size) {
if (!ProcessSetting(settings_scratch_.setting_buf)) {
set_error(SPDY_INVALID_CONTROL_FRAME);
return processed_bytes;
}
settings_scratch_.setting_buf_len = 0;
}
}
unprocessed_bytes -= processing;
processed_bytes += processing;
}
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(0u, remaining_data_length_);
const SpdyNameValueBlock& block = hpack_decoder_.decoded_block();
if (block.empty()) {
ProcessControlFrameHeaderBlock(NULL, 0, false);
return;
}
SpdyFrameBuilder builder(
GetSerializedLength(protocol_version(), &block));
SerializeNameValueBlockWithoutCompression(&builder, block);
scoped_ptr<SpdyFrame> frame(builder.take());
remaining_data_length_ = frame->size();
ProcessControlFrameHeaderBlock(frame->data(), frame->size(), false);
}
bool SpdyFramer::ProcessSetting(const char* data) {
int id_field;
SpdySettingsIds id;
uint8 flags = 0;
uint32 value;
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 = *(reinterpret_cast<const uint8*>(data));
value = ntohl(*(reinterpret_cast<const uint32*>(data + 1)));
}
if (!SpdyConstants::IsValidSettingId(protocol_version(), id_field)) {
DLOG(WARNING) << "Unknown SETTINGS ID: " << id_field;
return false;
}
id = SpdyConstants::ParseSettingId(protocol_version(), id_field);
if (protocol_version() <= SPDY3) {
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;
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;
}
}
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());
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;
default:
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;
}
if (len > remaining_data_length_) {
len = remaining_data_length_;
}
size_t original_len = len;
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) {
UpdateCurrentFrameBuffer(&data, &len, unread_header_bytes);
if (current_frame_buffer_length_ == header_size) {
SpdyFrameReader reader(current_frame_buffer_.get(),
current_frame_buffer_length_);
reader.Seek(GetControlFrameHeaderSize());
bool successful_read = reader.ReadUInt31(¤t_frame_stream_id_);
DCHECK(successful_read);
SpdyGoAwayStatus status = GOAWAY_OK;
if (protocol_version() >= SPDY3) {
uint32 status_raw = GOAWAY_OK;
successful_read = reader.ReadUInt32(&status_raw);
DCHECK(successful_read);
if (status_raw < GOAWAY_NUM_STATUS_CODES) {
status = static_cast<SpdyGoAwayStatus>(status_raw);
} else {
DCHECK(false);
}
}
visitor_->OnGoAway(current_frame_stream_id_, status);
}
}
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) {
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;
}
if (len > remaining_data_length_) {
len = remaining_data_length_;
}
size_t original_len = len;
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) {
UpdateCurrentFrameBuffer(&data, &len, unread_header_bytes);
if (current_frame_buffer_length_ == header_size) {
SpdyFrameReader reader(current_frame_buffer_.get(),
current_frame_buffer_length_);
reader.Seek(GetControlFrameHeaderSize());
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 (status_raw > RST_STREAM_INVALID &&
status_raw < RST_STREAM_NUM_STATUS_CODES) {
status = static_cast<SpdyRstStreamStatus>(status_raw);
} else {
}
visitor_->OnRstStream(current_frame_stream_id_, status);
}
}
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) {
visitor_->OnRstStreamFrameData(NULL, 0);
CHANGE_STATE(SPDY_AUTO_RESET);
}
return original_len;
}
size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) {
DCHECK_EQ(SPDY_READ_PADDING_LENGTH, state_);
size_t original_len = len;
if (remaining_padding_length_fields_ == 0) {
DCHECK_EQ(remaining_padding_payload_length_, 0u);
bool pad_low = false;
bool pad_high = false;
if (current_frame_flags_ & net::DATA_FLAG_PAD_LOW) {
pad_low = true;
++remaining_padding_length_fields_;
}
if (current_frame_flags_ & net::DATA_FLAG_PAD_HIGH) {
pad_high = true;
++remaining_padding_length_fields_;
}
if ((pad_high && !pad_low) ||
remaining_data_length_ < remaining_padding_length_fields_) {
set_error(SPDY_INVALID_DATA_FRAME_FLAGS);
return 0;
}
}
while (len != 0 && remaining_padding_length_fields_ != 0) {
remaining_padding_payload_length_ =
(remaining_padding_payload_length_ << 8) +
*reinterpret_cast<const uint8*>(data);
++data;
--len;
--remaining_padding_length_fields_;
--remaining_data_length_;
}
if (remaining_padding_length_fields_ == 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 (amount_to_discard) {
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_ && current_frame_flags_ & DATA_FLAG_FIN) {
visitor_->OnStreamFrameData(current_frame_stream_id_, NULL, 0, true);
}
}
if (remaining_data_length_ == 0) {
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) {
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_ && current_frame_flags_ & DATA_FLAG_FIN) {
visitor_->OnStreamFrameData(current_frame_stream_id_, NULL, 0, true);
}
}
if (remaining_data_length_ == remaining_padding_payload_length_) {
CHANGE_STATE(SPDY_CONSUME_PADDING);
}
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);
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;
}
}
for (uint32 index = 0; index < num_headers; ++index) {
base::StringPiece temp;
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();
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();
if (block->find(name) != block->end()) {
DVLOG(1) << "Duplicate header '" << name << "' (" << index + 1 << " of "
<< num_headers << ").";
return 0;
}
(*block)[name] = value;
}
return reader.GetBytesConsumed();
}
SpdySerializedFrame* SpdyFramer::SerializeData(const SpdyDataIR& datair) const {
uint8 flags = DATA_FLAG_NONE;
if (datair.fin()) {
flags = DATA_FLAG_FIN;
}
if (protocol_version() > SPDY3) {
int num_padding_fields = 0;
if (datair.pad_low()) {
flags |= DATA_FLAG_PAD_LOW;
++num_padding_fields;
}
if (datair.pad_high()) {
flags |= DATA_FLAG_PAD_HIGH;
++num_padding_fields;
}
const size_t size_with_padding = num_padding_fields +
datair.data().length() + datair.padding_payload_len() +
GetDataFrameMinimumSize();
SpdyFrameBuilder builder(size_with_padding);
builder.WriteDataFrameHeader(*this, datair.stream_id(), flags);
if (datair.pad_high()) {
builder.WriteUInt8(datair.padding_payload_len() >> 8);
}
if (datair.pad_low()) {
builder.WriteUInt8(datair.padding_payload_len() & 0xff);
}
builder.WriteBytes(datair.data().data(), datair.data().length());
if (datair.padding_payload_len() > 0) {
string padding = string(datair.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() + datair.data().length();
SpdyFrameBuilder builder(size);
builder.WriteDataFrameHeader(*this, datair.stream_id(), flags);
builder.WriteBytes(datair.data().data(), datair.data().length());
DCHECK_EQ(size, builder.length());
return builder.take();
}
}
SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeader(
const SpdyDataIR& data) const {
const size_t kSize = GetDataFrameMinimumSize();
uint8 flags = DATA_FLAG_NONE;
if (data.fin()) {
flags = DATA_FLAG_FIN;
}
if (protocol_version() > SPDY3) {
if (data.pad_low()) {
flags |= DATA_FLAG_PAD_LOW;
}
if (data.pad_high()) {
flags |= DATA_FLAG_PAD_HIGH;
}
}
SpdyFrameBuilder builder(kSize);
builder.WriteDataFrameHeader(*this, data.stream_id(), flags);
builder.OverwriteLength(*this, data.data().length());
DCHECK_EQ(kSize, builder.length());
return builder.take();
}
SpdySerializedFrame* SpdyFramer::SerializeSynStream(
const SpdySynStreamIR& syn_stream) {
uint8 flags = 0;
if (syn_stream.fin()) {
flags |= CONTROL_FLAG_FIN;
}
if (syn_stream.unidirectional()) {
flags |= CONTROL_FLAG_UNIDIRECTIONAL;
}
if (protocol_version() > SPDY3) {
flags |= HEADERS_FLAG_PRIORITY;
flags |= HEADERS_FLAG_END_HEADERS;
}
uint8 priority = syn_stream.priority();
if (priority > GetLowestPriority()) {
DLOG(DFATAL) << "Priority out-of-bounds.";
priority = GetLowestPriority();
}
size_t size = GetSynStreamMinimumSize();
string hpack_encoding;
if (protocol_version() > SPDY3) {
hpack_encoder_.EncodeHeaderSet(syn_stream.name_value_block(),
&hpack_encoding);
size += hpack_encoding.size();
} else {
size += GetSerializedLength(syn_stream.name_value_block());
}
SpdyFrameBuilder builder(size);
if (protocol_version() <= SPDY3) {
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);
} else {
builder.WriteFramePrefix(*this,
HEADERS,
flags,
syn_stream.stream_id());
builder.WriteUInt32(priority);
}
DCHECK_EQ(GetSynStreamMinimumSize(), builder.length());
if (protocol_version() > SPDY3) {
builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size());
} else {
SerializeNameValueBlock(&builder, syn_stream);
}
if (debug_visitor_) {
const size_t payload_len = protocol_version() > SPDY3 ?
hpack_encoding.size() :
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) {
uint8 flags = 0;
if (syn_reply.fin()) {
flags |= CONTROL_FLAG_FIN;
}
if (protocol_version() > SPDY3) {
flags |= HEADERS_FLAG_END_HEADERS;
}
size_t size = GetSynReplyMinimumSize();
string hpack_encoding;
if (protocol_version() > SPDY3) {
hpack_encoder_.EncodeHeaderSet(syn_reply.name_value_block(),
&hpack_encoding);
size += hpack_encoding.size();
} else {
size += GetSerializedLength(syn_reply.name_value_block());
}
SpdyFrameBuilder builder(size);
if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, SYN_REPLY, flags);
builder.WriteUInt32(syn_reply.stream_id());
} else {
builder.WriteFramePrefix(*this,
HEADERS,
flags,
syn_reply.stream_id());
}
if (protocol_version() < SPDY3) {
builder.WriteUInt16(0);
}
DCHECK_EQ(GetSynReplyMinimumSize(), builder.length());
if (protocol_version() > SPDY3) {
builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size());
} else {
SerializeNameValueBlock(&builder, syn_reply);
}
if (debug_visitor_) {
const size_t payload_len = protocol_version() > SPDY3 ?
hpack_encoding.size() :
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 {
uint16 expected_length = GetRstStreamMinimumSize();
if (protocol_version() > SPDY3) {
expected_length += rst_stream.description().size();
}
SpdyFrameBuilder builder(expected_length);
if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, RST_STREAM, 0);
builder.WriteUInt32(rst_stream.stream_id());
} else {
builder.WriteFramePrefix(*this, RST_STREAM, 0, rst_stream.stream_id());
}
builder.WriteUInt32(rst_stream.status());
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 = (protocol_version() <= SPDY3 ? 8 : 5);
const size_t size = GetSettingsMinimumSize() +
(values->size() * setting_size);
SpdyFrameBuilder builder(size);
if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, SETTINGS, flags);
} else {
builder.WriteFramePrefix(*this, SETTINGS, flags, 0);
}
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) {
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,
SpdyConstants::SerializeSettingId(protocol_version(), it->first));
uint32 id_and_flags_wire = flags_and_id.GetWireFormat(protocol_version());
builder.WriteBytes(&id_and_flags_wire, 4);
} else {
builder.WriteUInt8(SpdyConstants::SerializeSettingId(protocol_version(),
it->first));
}
builder.WriteUInt32(it->second.value);
}
DCHECK_EQ(size, builder.length());
return builder.take();
}
SpdyFrame* SpdyFramer::SerializeBlocked(const SpdyBlockedIR& blocked) const {
DCHECK_LT(SPDY3, protocol_version());
SpdyFrameBuilder builder(GetBlockedSize());
builder.WriteFramePrefix(*this, BLOCKED, kNoFlags, blocked.stream_id());
return builder.take();
}
SpdySerializedFrame* SpdyFramer::SerializePing(const SpdyPingIR& ping) const {
SpdyFrameBuilder builder(GetPingSize());
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.WriteFramePrefix(*this, PING, flags, 0);
builder.WriteUInt64(ping.id());
}
DCHECK_EQ(GetPingSize(), builder.length());
return builder.take();
}
SpdySerializedFrame* SpdyFramer::SerializeGoAway(
const SpdyGoAwayIR& goaway) const {
uint16 expected_length = GetGoAwayMinimumSize();
if (protocol_version() > SPDY3) {
expected_length += goaway.description().size();
}
SpdyFrameBuilder builder(expected_length);
if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, GOAWAY, kNoFlags);
} else {
builder.WriteFramePrefix(*this, GOAWAY, 0, 0);
}
builder.WriteUInt32(goaway.last_good_stream_id());
if (protocol_version() >= SPDY3) {
builder.WriteUInt32(goaway.status());
}
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) {
if (headers.end_headers()) {
flags |= HEADERS_FLAG_END_HEADERS;
}
if (headers.has_priority()) {
flags |= HEADERS_FLAG_PRIORITY;
}
}
size_t size = GetHeadersMinimumSize();
uint32 priority = headers.priority();
if (headers.has_priority()) {
if (priority > GetLowestPriority()) {
DLOG(DFATAL) << "Priority out-of-bounds.";
priority = GetLowestPriority();
}
size += 4;
}
string hpack_encoding;
if (protocol_version() > SPDY3) {
hpack_encoder_.EncodeHeaderSet(headers.name_value_block(), &hpack_encoding);
size += hpack_encoding.size();
} else {
size += GetSerializedLength(headers.name_value_block());
}
SpdyFrameBuilder builder(size);
if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, HEADERS, flags);
builder.WriteUInt32(headers.stream_id());
} else {
builder.WriteFramePrefix(*this,
HEADERS,
flags,
headers.stream_id());
if (headers.has_priority()) {
builder.WriteUInt32(priority);
}
}
if (protocol_version() <= SPDY2) {
builder.WriteUInt16(0);
}
DCHECK_EQ(GetHeadersMinimumSize(), builder.length());
if (protocol_version() > SPDY3) {
builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size());
} else {
SerializeNameValueBlock(&builder, headers);
}
if (debug_visitor_) {
const size_t payload_len = protocol_version() > SPDY3 ?
hpack_encoding.size() :
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());
if (protocol_version() <= SPDY3) {
builder.WriteControlFrameHeader(*this, WINDOW_UPDATE, kNoFlags);
builder.WriteUInt32(window_update.stream_id());
} else {
builder.WriteFramePrefix(*this,
WINDOW_UPDATE,
kNoFlags,
window_update.stream_id());
}
builder.WriteUInt32(window_update.delta());
DCHECK_EQ(GetWindowUpdateSize(), builder.length());
return builder.take();
}
SpdyFrame* SpdyFramer::SerializePushPromise(
const SpdyPushPromiseIR& push_promise) {
DCHECK_LT(SPDY3, protocol_version());
uint8 flags = 0;
if (push_promise.end_push_promise()) {
flags |= PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
}
size_t size = GetPushPromiseMinimumSize();
string hpack_encoding;
if (protocol_version() > SPDY3) {
hpack_encoder_.EncodeHeaderSet(push_promise.name_value_block(),
&hpack_encoding);
size += hpack_encoding.size();
} else {
size += GetSerializedLength(push_promise.name_value_block());
}
SpdyFrameBuilder builder(size);
builder.WriteFramePrefix(*this, PUSH_PROMISE, flags,
push_promise.stream_id());
builder.WriteUInt32(push_promise.promised_stream_id());
DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length());
if (protocol_version() > SPDY3) {
builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size());
} else {
SerializeNameValueBlock(&builder, push_promise);
}
if (debug_visitor_) {
const size_t payload_len = protocol_version() > SPDY3 ?
hpack_encoding.size() :
GetSerializedLength(protocol_version(),
&(push_promise.name_value_block()));
debug_visitor_->OnSendCompressedFrame(push_promise.stream_id(),
PUSH_PROMISE, payload_len, builder.length());
}
return builder.take();
}
SpdyFrame* SpdyFramer::SerializeContinuation(
const SpdyContinuationIR& continuation) {
CHECK_LT(SPDY3, protocol_version());
uint8 flags = 0;
if (continuation.end_headers()) {
flags |= HEADERS_FLAG_END_HEADERS;
}
size_t size = GetContinuationMinimumSize();
string hpack_encoding;
hpack_encoder_.EncodeHeaderSet(continuation.name_value_block(),
&hpack_encoding);
size += hpack_encoding.size();
SpdyFrameBuilder builder(size);
builder.WriteFramePrefix(*this, CONTINUATION, flags,
continuation.stream_id());
DCHECK_EQ(GetContinuationMinimumSize(), builder.length());
builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size());
if (debug_visitor_) {
const size_t payload_len = hpack_encoding.size();
debug_visitor_->OnSendCompressedFrame(continuation.stream_id(),
CONTINUATION, payload_len, builder.length());
}
return builder.take();
}
namespace {
class FrameSerializationVisitor : public SpdyFrameVisitor {
public:
explicit FrameSerializationVisitor(SpdyFramer* framer) : framer_(framer) {}
virtual ~FrameSerializationVisitor() {}
SpdySerializedFrame* ReleaseSerializedFrame() { return frame_.release(); }
virtual void VisitData(const SpdyDataIR& data) OVERRIDE {
frame_.reset(framer_->SerializeData(data));
}
virtual void VisitSynStream(const SpdySynStreamIR& syn_stream) OVERRIDE {
frame_.reset(framer_->SerializeSynStream(syn_stream));
}
virtual void VisitSynReply(const SpdySynReplyIR& syn_reply) OVERRIDE {
frame_.reset(framer_->SerializeSynReply(syn_reply));
}
virtual void VisitRstStream(const SpdyRstStreamIR& rst_stream) OVERRIDE {
frame_.reset(framer_->SerializeRstStream(rst_stream));
}
virtual void VisitSettings(const SpdySettingsIR& settings) OVERRIDE {
frame_.reset(framer_->SerializeSettings(settings));
}
virtual void VisitPing(const SpdyPingIR& ping) OVERRIDE {
frame_.reset(framer_->SerializePing(ping));
}
virtual void VisitGoAway(const SpdyGoAwayIR& goaway) OVERRIDE {
frame_.reset(framer_->SerializeGoAway(goaway));
}
virtual void VisitHeaders(const SpdyHeadersIR& headers) OVERRIDE {
frame_.reset(framer_->SerializeHeaders(headers));
}
virtual void VisitWindowUpdate(
const SpdyWindowUpdateIR& window_update) OVERRIDE {
frame_.reset(framer_->SerializeWindowUpdate(window_update));
}
virtual void VisitBlocked(const SpdyBlockedIR& blocked) OVERRIDE {
frame_.reset(framer_->SerializeBlocked(blocked));
}
virtual void VisitPushPromise(
const SpdyPushPromiseIR& push_promise) OVERRIDE {
frame_.reset(framer_->SerializePushPromise(push_promise));
}
virtual void VisitContinuation(
const SpdyContinuationIR& continuation) OVERRIDE {
frame_.reset(framer_->SerializeContinuation(continuation));
}
private:
SpdyFramer* framer_;
scoped_ptr<SpdySerializedFrame> frame_;
};
}
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();
return 2 * deflateBound(compressor, uncompressed_length);
}
#if defined(USE_SYSTEM_ZLIB)
static const int kCompressorLevel = 0;
#else
static const int kCompressorLevel = 9;
#endif
static const int kCompressorWindowSizeInBits = 11;
static const int kCompressorMemLevel = 1;
z_stream* SpdyFramer::GetHeaderCompressor() {
if (header_compressor_.get())
return header_compressor_.get();
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();
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();
}
bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData(
SpdyStreamId stream_id,
const char* data,
size_t len) {
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;
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;
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);
}
}
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) {
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) {
set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE);
}
}
return read_successfully;
}
void SpdyFramer::SerializeNameValueBlockWithoutCompression(
SpdyFrameBuilder* builder,
const SpdyNameValueBlock& name_value_block) const {
if (protocol_version() <= SPDY2) {
builder->WriteUInt16(name_value_block.size());
} else {
builder->WriteUInt32(name_value_block.size());
}
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());
}
const size_t uncompressed_len = GetSerializedLength(
protocol_version(), &(frame.name_value_block()));
SpdyFrameBuilder uncompressed_builder(uncompressed_len);
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;
}
base::StatsCounter compressed_frames("spdy.CompressedFrames");
base::StatsCounter pre_compress_bytes("spdy.PreCompressSize");
base::StatsCounter post_compress_bytes("spdy.PostCompressSize");
const int compressed_max_size =
2 * deflateBound(compressor, uncompressed_len);
#if defined(USE_SYSTEM_ZLIB)
compressor->next_in = reinterpret_cast<Bytef*>(uncompressed_payload->data());
compressor->avail_in = uncompressed_len;
#endif
compressor->next_out = reinterpret_cast<Bytef*>(
builder->GetWritableBuffer(compressed_max_size));
compressor->avail_out = compressed_max_size;
#if defined(USE_SYSTEM_ZLIB)
int rv = deflate(compressor, Z_SYNC_FLUSH);
if (rv != Z_OK) {
LOG(WARNING) << "deflate failure: " << rv;
return;
}
#else
WriteHeaderBlockToZ(&frame.name_value_block(), compressor);
#endif
int compressed_size = compressed_max_size - compressor->avail_out;
builder->Seek(compressed_size);
builder->RewriteLength(*this);
pre_compress_bytes.Add(uncompressed_len);
post_compress_bytes.Add(compressed_size);
compressed_frames.Increment();
}
}