Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(643)

Unified Diff: net/spdy/spdy_framer.cc

Issue 9618002: SPDY - integration of spdy/3 code. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/spdy/spdy_framer.cc
===================================================================
--- net/spdy/spdy_framer.cc (revision 125802)
+++ net/spdy/spdy_framer.cc (working copy)
@@ -12,6 +12,7 @@
#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"
#if defined(USE_SYSTEM_ZLIB)
@@ -24,63 +25,22 @@
namespace spdy {
-SpdyCredential::SpdyCredential() : slot(0) { }
-SpdyCredential::~SpdyCredential() { }
-
// Compute the id of our dictionary so that we know we're using the
// right one when asked for it.
-uLong CalculateDictionaryId() {
+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*>(SpdyFramer::kDictionary),
- SpdyFramer::kDictionarySize);
+ reinterpret_cast<const Bytef*>(dictionary),
+ dictionary_size);
}
-// Adler ID for the SPDY header compressor dictionary.
-const uLong kDictionaryId = CalculateDictionaryId();
+// Adler ID for the SPDY header compressor dictionaries.
+const uLong kV2DictionaryId = CalculateDictionaryId(kV2Dictionary,
+ kV2DictionarySize);
+const uLong kV3DictionaryId = CalculateDictionaryId(kV3Dictionary,
+ kV3DictionarySize);
-int DecompressHeaderBlockInZStream(z_stream* decompressor) {
- int rv = inflate(decompressor, Z_SYNC_FLUSH);
- if (rv == Z_NEED_DICT) {
- // Need to try again with the right dictionary.
- if (decompressor->adler == kDictionaryId) {
- rv = inflateSetDictionary(decompressor,
- (const Bytef*)SpdyFramer::kDictionary,
- SpdyFramer::kDictionarySize);
- if (rv == Z_OK)
- rv = inflate(decompressor, Z_SYNC_FLUSH);
- }
- }
- return rv;
-}
-
-// Retrieve serialized length of SpdyHeaderBlock.
-size_t GetSerializedLength(const SpdyHeaderBlock* headers) {
- size_t total_length = SpdyControlFrame::kNumNameValuePairsSize;
- SpdyHeaderBlock::const_iterator it;
- for (it = headers->begin(); it != headers->end(); ++it) {
- // We add space for the length of the name and the length of the value as
- // well as the length of the name and the length of the value.
- total_length += SpdyControlFrame::kLengthOfNameSize +
- it->first.size() +
- SpdyControlFrame::kLengthOfValueSize +
- it->second.size();
- }
- return total_length;
-}
-
-// Serializes a SpdyHeaderBlock.
-void WriteHeaderBlock(SpdyFrameBuilder* frame, const SpdyHeaderBlock* headers) {
- frame->WriteUInt16(headers->size()); // Number of headers.
- SpdyHeaderBlock::const_iterator it;
- for (it = headers->begin(); it != headers->end(); ++it) {
- bool wrote_header;
- wrote_header = frame->WriteString(it->first);
- wrote_header &= frame->WriteString(it->second);
- DCHECK(wrote_header);
- }
-}
-
// Creates a FlagsAndLength.
FlagsAndLength CreateFlagsAndLength(SpdyControlFlags flags, size_t length) {
DCHECK_EQ(0u, length & ~static_cast<size_t>(kLengthMask));
@@ -103,11 +63,11 @@
// TODO(mbelshe): We should make this stream-based so there are no limits.
size_t SpdyFramer::kControlFrameBufferMaxSize = 64 * 1024;
-int SpdyFramer::spdy_version_ = kSpdyProtocolVersion;
-
const SpdyStreamId SpdyFramer::kInvalidStream = -1;
const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024;
+size_t SpdyFramer::kUncompressedControlFrameBufferInitialSize = 18 + 16;
Ryan Hamilton 2012/03/09 19:09:58 Can you add a comment explaining why this is the r
ramant (doing other things) 2012/03/10 01:14:09 Done.
+
#ifdef DEBUG_SPDY_STATE_CHANGES
#define CHANGE_STATE(newstate) \
{ \
@@ -122,7 +82,42 @@
#define CHANGE_STATE(newstate) (state_ = newstate)
#endif
-SpdyFramer::SpdyFramer()
+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) {
+ DCHECK_GT(static_cast<uint32>(1 << 24), id);
+}
+
+uint32 SettingsFlagsAndId::GetWireFormat(int version) const {
+ uint32 wire = htonl(id_ & 0x00ffffff) | htonl(flags_ << 24);
+ if (version < 3) {
+ ConvertFlagsAndIdForSpdy2(&wire);
+ }
+ return wire;
+}
+
+// SPDY 2 had a bug in it with respect to byte ordering of id/flags field.
+// This method is used to preserve buggy behavior and works on both
+// little-endian and big-endian hosts.
+// This method is also bidirectional (can be used to translate SPDY 2 to SPDY 3
+// as well as vice versa).
+void SettingsFlagsAndId::ConvertFlagsAndIdForSpdy2(uint32* val) {
+ uint8* wire_array = reinterpret_cast<uint8*>(val);
+ std::swap(wire_array[0], wire_array[3]);
+ std::swap(wire_array[1], wire_array[2]);
+}
+
+SpdyCredential::SpdyCredential() : slot(0) { }
+SpdyCredential::~SpdyCredential() { }
+
+SpdyFramer::SpdyFramer(int version)
: state_(SPDY_RESET),
error_code_(SPDY_NO_ERROR),
remaining_data_(0),
@@ -134,7 +129,12 @@
validate_control_frame_sizes_(true),
enable_compression_(compression_default_),
visitor_(NULL),
- display_protocol_("SPDY") {
+ display_protocol_("SPDY"),
+ spdy_version_(version),
+ syn_frame_processed_(false),
+ probable_http_response_(false) {
+ DCHECK_GE(3, version);
+ DCHECK_LE(2, version);
}
SpdyFramer::~SpdyFramer() {
@@ -155,6 +155,7 @@
remaining_control_payload_ = 0;
remaining_control_header_ = 0;
current_frame_len_ = 0;
+ settings_scratch_.Reset();
// TODO(hkhalil): Remove once initial_size == kControlFrameBufferInitialSize.
size_t initial_size = kControlFrameBufferInitialSize;
if (!enable_compression_) {
@@ -192,6 +193,8 @@
return "SPDY_CONTROL_FRAME_HEADER_BLOCK";
case SPDY_CREDENTIAL_FRAME_PAYLOAD:
return "SPDY_CREDENTIAL_FRAME_PAYLOAD";
+ case SPDY_SETTINGS_FRAME_PAYLOAD:
+ return "SPDY_SETTINGS_FRAME_PAYLOAD";
}
return "UNKNOWN_STATE";
}
@@ -304,6 +307,10 @@
// 1. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK
// 2. SPDY_CONTROL_FRAME_HEADER_BLOCK
//
+ // SETTINGS frames take a slightly modified route:
+ // 1. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK
+ // 2. SPDY_SETTINGS_FRAME_PAYLOAD
+ //
// All other control frames will use the alternate route directly to
// SPDY_CONTROL_FRAME_PAYLOAD
int bytes_read = ProcessControlFrameBeforeHeaderBlock(data, len);
@@ -312,6 +319,13 @@
continue;
}
+ case SPDY_SETTINGS_FRAME_PAYLOAD: {
+ int bytes_read = ProcessSettingsFramePayload(data, len);
+ len -= bytes_read;
+ data += bytes_read;
+ continue;
+ }
+
case SPDY_CONTROL_FRAME_HEADER_BLOCK: {
int bytes_read = ProcessControlFrameHeaderBlock(data, len);
len -= bytes_read;
@@ -323,7 +337,6 @@
size_t bytes_read = ProcessCredentialFramePayload(data, len);
len -= bytes_read;
data += bytes_read;
- continue;
}
case SPDY_CONTROL_FRAME_PAYLOAD: {
@@ -342,6 +355,8 @@
continue;
}
default:
+ LOG(ERROR) << "Invalid value for " << display_protocol_
+ << " framer state: " << state_;
// This ensures that we don't infinite-loop if state_ gets an
// invalid value somehow, such as due to a SpdyFramer getting deleted
// from a callback it calls.
@@ -383,8 +398,16 @@
// This is just a sanity check for help debugging early frame errors.
if (remaining_data_ > 1000000u) {
- LOG(WARNING) << "Unexpectedly large frame. " << display_protocol_
- << " session is likely corrupt.";
+ // The strncmp for 5 is safe because we only hit this point if we
+ // have SpdyFrame::kHeaderSize (8) bytes
+ if (!syn_frame_processed_ &&
+ strncmp(current_frame_buffer_, "HTTP/", 5) == 0) {
+ LOG(WARNING) << "Unexpected HTTP response to spdy request";
+ probable_http_response_ = true;
+ } else {
+ LOG(WARNING) << "Unexpectedly large frame. " << display_protocol_
+ << " session is likely corrupt.";
+ }
}
// if we're here, then we have the common header all received.
@@ -407,6 +430,8 @@
// We check version before we check validity: version can never be 'invalid',
// it can only be unsupported.
if (current_control_frame.version() != spdy_version_) {
+ DLOG(INFO) << "Unsupported SPDY version " << current_control_frame.version()
+ << " (expected " << spdy_version_ << ")";
set_error(SPDY_UNSUPPORTED_VERSION);
return;
}
@@ -445,9 +470,15 @@
set_error(SPDY_INVALID_CONTROL_FRAME);
break;
case SETTINGS:
+ // Make sure that we have an integral number of 8-byte key/value pairs,
+ // plus a 4-byte length field.
if (current_control_frame.length() <
- SpdySettingsControlFrame::size() - SpdyControlFrame::kHeaderSize)
+ SpdySettingsControlFrame::size() - SpdyControlFrame::kHeaderSize ||
+ (current_control_frame.length() % 8 != 4)) {
+ DLOG(WARNING) << "Invalid length for SETTINGS frame: "
+ << current_control_frame.length();
set_error(SPDY_INVALID_CONTROL_FRAME);
+ }
break;
case GOAWAY:
if (current_control_frame.length() !=
@@ -485,12 +516,6 @@
}
}
- // We only support version 1 of this protocol.
- if (current_control_frame.version() != spdy_version_) {
- set_error(SPDY_UNSUPPORTED_VERSION);
- return;
- }
-
remaining_control_payload_ = current_control_frame.length();
if (remaining_control_payload_ >
kControlFrameBufferMaxSize - SpdyFrame::kHeaderSize) {
@@ -504,37 +529,54 @@
return;
}
- int32 frame_size_without_header_block;
+ // Determine the frame size without variable-length data.
+ int32 frame_size_without_variable_data;
switch (current_control_frame.type()) {
case SYN_STREAM:
- frame_size_without_header_block = SpdySynStreamControlFrame::size();
+ syn_frame_processed_ = true;
+ frame_size_without_variable_data = SpdySynStreamControlFrame::size();
break;
case SYN_REPLY:
- frame_size_without_header_block = SpdySynReplyControlFrame::size();
+ syn_frame_processed_ = true;
+ frame_size_without_variable_data = SpdySynReplyControlFrame::size();
+ // SPDY 2 had two bytes of unused space preceeding payload.
+ if (spdy_version_ < 3) {
+ frame_size_without_variable_data += 2;
+ }
break;
case HEADERS:
- frame_size_without_header_block = SpdyHeadersControlFrame::size();
+ frame_size_without_variable_data = SpdyHeadersControlFrame::size();
+ // SPDY 2 had two bytes of unused space preceeding payload.
+ if (spdy_version_ < 3) {
+ frame_size_without_variable_data += 2;
+ }
break;
+ case SETTINGS:
+ frame_size_without_variable_data = SpdySettingsControlFrame::size();
+ break;
default:
- frame_size_without_header_block = -1;
- LOG_IF(DFATAL, remaining_control_payload_ + SpdyFrame::kHeaderSize >
- current_frame_capacity_)
- << display_protocol_
- << " control frame buffer too small for fixed-length frame.";
- ExpandControlFrameBuffer(remaining_control_payload_);
+ frame_size_without_variable_data = -1;
break;
}
- if (frame_size_without_header_block > 0) {
+ if (frame_size_without_variable_data == -1) {
+ LOG_IF(ERROR, remaining_control_payload_ + SpdyFrame::kHeaderSize >
+ current_frame_capacity_)
+ << display_protocol_
+ << " control frame buffer too small for fixed-length frame.";
+ // TODO(hkhalil): Remove ExpandControlFrameBuffer().
+ ExpandControlFrameBuffer(remaining_control_payload_);
+ }
+ if (frame_size_without_variable_data > 0) {
// We have a control frame with a header block. We need to parse the
// remainder of the control frame's header before we can parse the header
// block. The start of the header block varies with the control type.
- DCHECK_GE(static_cast<uint32>(frame_size_without_header_block),
- current_frame_len_);
- remaining_control_header_ = frame_size_without_header_block -
+ DCHECK_GE(frame_size_without_variable_data,
+ static_cast<int32>(current_frame_len_));
+ remaining_control_header_ = frame_size_without_variable_data -
current_frame_len_;
remaining_control_payload_ += SpdyFrame::kHeaderSize -
- frame_size_without_header_block;
+ frame_size_without_variable_data;
CHANGE_STATE(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK);
return;
}
@@ -553,6 +595,46 @@
return bytes_to_read;
}
+size_t SpdyFramer::GetSerializedLength(const SpdyHeaderBlock* headers) const {
+ 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) {
+ // We add space for the length of the name and the length of the value as
+ // well as the length of the name and the length of the value.
+ total_length += length_of_name_size + it->first.size() +
+ length_of_value_size + it->second.size();
+ }
+ return total_length;
+}
+
+void SpdyFramer::WriteHeaderBlock(SpdyFrameBuilder* frame,
+ const SpdyHeaderBlock* headers) const {
+ if (spdy_version_ < 3) {
+ frame->WriteUInt16(headers->size()); // Number of headers.
+ } else {
+ frame->WriteUInt32(headers->size()); // Number of headers.
+ }
+ SpdyHeaderBlock::const_iterator it;
+ for (it = headers->begin(); it != headers->end(); ++it) {
+ bool wrote_header;
+ if (spdy_version_ < 3) {
+ wrote_header = frame->WriteString(it->first);
+ wrote_header &= frame->WriteString(it->second);
+ } else {
+ wrote_header = frame->WriteStringPiece32(it->first);
+ wrote_header &= frame->WriteStringPiece32(it->second);
+ }
+ DCHECK(wrote_header);
+ }
+}
+
+
size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
size_t len) {
DCHECK_EQ(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, state_);
@@ -567,10 +649,15 @@
SpdyControlFrame control_frame(current_frame_buffer_, false);
DCHECK(control_frame.type() == SYN_STREAM ||
control_frame.type() == SYN_REPLY ||
- control_frame.type() == HEADERS);
+ control_frame.type() == HEADERS ||
+ control_frame.type() == SETTINGS);
visitor_->OnControl(&control_frame);
- CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
+ if (control_frame.type() == SETTINGS) {
+ CHANGE_STATE(SPDY_SETTINGS_FRAME_PAYLOAD);
+ } else {
+ CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
+ }
}
}
return original_len - len;
@@ -625,6 +712,110 @@
return process_bytes;
}
+size_t SpdyFramer::ProcessSettingsFramePayload(const char* data,
+ size_t data_len) {
+ DCHECK_EQ(SPDY_SETTINGS_FRAME_PAYLOAD, state_);
+ SpdyControlFrame control_frame(current_frame_buffer_, false);
+ DCHECK_EQ(control_frame.type(), SETTINGS);
+ size_t unprocessed_bytes = std::min(data_len, remaining_control_payload_);
+ size_t processed_bytes = 0;
+ DCHECK_GT(unprocessed_bytes, 0u);
+
+ // Loop over our incoming data.
+ while (unprocessed_bytes > 0) {
+ // Process up to one setting at a time.
+ size_t processing = std::min(
+ unprocessed_bytes,
+ static_cast<size_t>(8 - settings_scratch_.setting_buf_len));
+
+ // Check if we have a complete setting in our input.
+ if (processing == 8) {
+ // Parse the setting directly out of the input without buffering.
+ if (!ProcessSetting(data + processed_bytes)) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ return processed_bytes;
+ }
+ } else {
+ // Continue updating settings_scratch_.setting_buf.
+ memcpy(settings_scratch_.setting_buf + settings_scratch_.setting_buf_len,
+ data + processed_bytes,
+ processing);
+ settings_scratch_.setting_buf_len += processing;
+
+ // Check if we have a complete setting buffered.
+ if (settings_scratch_.setting_buf_len == 8) {
+ if (!ProcessSetting(settings_scratch_.setting_buf)) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ return processed_bytes;
+ }
+ // Reset settings_scratch_.setting_buf for our next setting.
+ settings_scratch_.setting_buf_len = 0;
+ }
+ }
+
+ // Iterate.
+ unprocessed_bytes -= processing;
+ processed_bytes += processing;
+ }
+
+ // Check if we're done handling this SETTINGS frame.
+ remaining_control_payload_ -= processed_bytes;
+ if (remaining_control_payload_ == 0) {
+ CHANGE_STATE(SPDY_AUTO_RESET);
+ }
+
+ return processed_bytes;
+}
+
+bool SpdyFramer::ProcessSetting(const char* data) {
+ // Extract fields.
+ // Maintain behavior of old SPDY 2 bug with byte ordering of flags/id.
+ const uint32 id_and_flags_wire = *(reinterpret_cast<const uint32*>(data));
+ SettingsFlagsAndId id_and_flags =
+ SettingsFlagsAndId::FromWireFormat(spdy_version_, id_and_flags_wire);
+ uint8 flags = id_and_flags.flags();
+ uint32 value = ntohl(*(reinterpret_cast<const uint32*>(data + 4)));
+
+ // Validate id.
+ switch (id_and_flags.id()) {
+ case SETTINGS_UPLOAD_BANDWIDTH:
+ case SETTINGS_DOWNLOAD_BANDWIDTH:
+ case SETTINGS_ROUND_TRIP_TIME:
+ case SETTINGS_MAX_CONCURRENT_STREAMS:
+ case SETTINGS_CURRENT_CWND:
+ case SETTINGS_DOWNLOAD_RETRANS_RATE:
+ case SETTINGS_INITIAL_WINDOW_SIZE:
+ // Valid values.
+ break;
+ default:
+ DLOG(WARNING) << "Unknown SETTINGS ID: " << id_and_flags.id();
+ return false;
+ }
+ SpdySettingsIds id = static_cast<SpdySettingsIds>(id_and_flags.id());
+
+ // Detect duplciates.
+ if (static_cast<uint32>(id) <= settings_scratch_.last_setting_id) {
+ DLOG(WARNING) << "Duplicate entry or invalid ordering for id " << id
+ << " in " << display_protocol_ << " SETTINGS frame "
+ << "(last settikng id was "
+ << settings_scratch_.last_setting_id << ").";
+ return false;
+ }
+ settings_scratch_.last_setting_id = id;
+
+ // Validate flags.
+ uint8 kFlagsMask = SETTINGS_FLAG_PLEASE_PERSIST | SETTINGS_FLAG_PERSISTED;
+ if ((flags & ~(kFlagsMask)) != 0) {
+ DLOG(WARNING) << "Unknown SETTINGS flags provided for id " << id << ": "
+ << flags;
+ return false;
+ }
+
+ // Validation succeeded. Pass on to visitor.
+ visitor_->OnSetting(id, flags, value);
+ return true;
+}
+
size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
size_t original_len = len;
if (remaining_control_payload_) {
@@ -729,95 +920,62 @@
current_frame_buffer_ = new_buffer;
}
-/* static */
bool SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data,
size_t header_length,
SpdyHeaderBlock* block) {
- SpdyFrameBuilder builder(header_data, header_length);
- void* iter = NULL;
- uint16 num_headers;
- if (builder.ReadUInt16(&iter, &num_headers)) {
- for (int index = 0; index < num_headers; ++index) {
- std::string name;
- std::string value;
- if (!builder.ReadString(&iter, &name))
- return false;
- if (!builder.ReadString(&iter, &value))
- return false;
- if (block->find(name) == block->end()) {
- (*block)[name] = value;
- } else {
- return false;
- }
+ SpdyFrameReader reader(header_data, header_length);
+
+ // Read number of headers.
+ uint32 num_headers;
+ if (spdy_version_ < 3) {
+ uint16 temp;
+ if (!reader.ReadUInt16(&temp)) {
+ DLOG(INFO) << "Unable to read number of headers.";
+ return false;
}
- return true;
+ num_headers = temp;
+ } else {
+ if (!reader.ReadUInt32(&num_headers)) {
+ DLOG(INFO) << "Unable to read number of headers.";
+ return false;
+ }
}
- return false;
-}
-bool SpdyFramer::ParseHeaderBlock(const SpdyFrame* frame,
- SpdyHeaderBlock* block) {
- SpdyControlFrame control_frame(frame->data(), false);
- uint32 type = control_frame.type();
- if (type != SYN_STREAM && type != SYN_REPLY && type != HEADERS)
- return false;
+ // Read each header.
+ for (uint32 index = 0; index < num_headers; ++index) {
+ base::StringPiece temp;
- // Find the header data within the control frame.
- scoped_ptr<SpdyFrame> decompressed_frame(DecompressFrame(*frame));
- if (!decompressed_frame.get())
- return false;
+ // Read header name.
+ if ((spdy_version_ < 3) ? !reader.ReadStringPiece16(&temp)
+ : !reader.ReadStringPiece32(&temp)) {
+ DLOG(INFO) << "Unable to read header name (" << index + 1 << " of "
+ << num_headers << ").";
+ return false;
+ }
+ std::string name;
+ temp.CopyToString(&name);
- const char *header_data = NULL;
- int header_length = 0;
+ // Read header value.
+ if ((spdy_version_ < 3) ? !reader.ReadStringPiece16(&temp)
+ : !reader.ReadStringPiece32(&temp)) {
+ DLOG(INFO) << "Unable to read header value (" << index + 1 << " of "
+ << num_headers << ").";
+ return false;
+ }
+ std::string value;
+ temp.CopyToString(&value);
- switch (type) {
- case SYN_STREAM:
- {
- SpdySynStreamControlFrame syn_frame(decompressed_frame->data(), false);
- header_data = syn_frame.header_block();
- header_length = syn_frame.header_block_len();
- }
- break;
- case SYN_REPLY:
- {
- SpdySynReplyControlFrame syn_frame(decompressed_frame->data(), false);
- header_data = syn_frame.header_block();
- header_length = syn_frame.header_block_len();
- }
- break;
- case HEADERS:
- {
- SpdyHeadersControlFrame header_frame(decompressed_frame->data(), false);
- header_data = header_frame.header_block();
- header_length = header_frame.header_block_len();
- }
- break;
- }
+ // Ensure no duplicates.
+ if (block->find(name) != block->end()) {
+ DLOG(INFO) << "Duplicate header '" << name << "' (" << index + 1 << " of "
+ << num_headers << ").";
+ return false;
+ }
- SpdyFrameBuilder builder(header_data, header_length);
- void* iter = NULL;
- uint16 num_headers;
- if (builder.ReadUInt16(&iter, &num_headers)) {
- int index;
- for (index = 0; index < num_headers; ++index) {
- std::string name;
- std::string value;
- if (!builder.ReadString(&iter, &name))
- break;
- if (!builder.ReadString(&iter, &value))
- break;
- if (!name.size() || !value.size())
- return false;
- if (block->find(name) == block->end()) {
- (*block)[name] = value;
- } else {
- return false;
- }
- }
- return index == num_headers &&
- iter == header_data + header_length;
+ // Store header.
+ (*block)[name] = value;
}
- return false;
+ return true;
}
/* static */
@@ -826,16 +984,20 @@
DCHECK_EQ(frame->type(), SETTINGS);
DCHECK(settings);
- SpdyFrameBuilder parser(frame->header_block(), frame->header_block_len());
- void* iter = NULL;
+ SpdyFrameReader parser(frame->header_block(), frame->header_block_len());
for (size_t index = 0; index < frame->num_entries(); ++index) {
- uint32 id;
+ uint32 id_and_flags_wire;
uint32 value;
- if (!parser.ReadUInt32(&iter, &id))
+ // SettingsFlagsAndId accepts off-the-wire (network byte order) data, so we
+ // use ReadBytes() instead of ReadUInt32() as the latter calls ntohl().
+ if (!parser.ReadBytes(&id_and_flags_wire, 4)) {
return false;
- if (!parser.ReadUInt32(&iter, &value))
+ }
+ if (!parser.ReadUInt32(&value))
return false;
- settings->insert(settings->end(), std::make_pair(id, value));
+ SettingsFlagsAndId id_and_flags =
+ SettingsFlagsAndId::FromWireFormat(frame->version(), id_and_flags_wire);
+ settings->insert(settings->end(), std::make_pair(id_and_flags, value));
}
return true;
}
@@ -845,32 +1007,35 @@
SpdyCredential* credential) {
DCHECK(credential);
- void* iter = NULL;
- SpdyFrameBuilder parser(data, len);
- if (!parser.ReadUInt16(&iter, &credential->slot))
+ SpdyFrameReader parser(data, len);
+ base::StringPiece temp;
+ if (!parser.ReadUInt16(&credential->slot)) {
return false;
+ }
- uint32 proof_len;
- const char* proof_data;
- if (!parser.ReadReadLen32PrefixedData(&iter, &proof_data, &proof_len))
+ if (!parser.ReadStringPiece32(&temp)) {
return false;
- credential->proof.assign(proof_data, proof_len);
+ }
+ temp.CopyToString(&credential->proof);
- while (parser.IteratorHasRoomFor(iter, 1)) {
- uint32 cert_len;
- const char* cert_data;
- if (!parser.ReadReadLen32PrefixedData(&iter, &cert_data, &cert_len))
+ while (!parser.IsDoneReading()) {
+ if (!parser.ReadStringPiece32(&temp)) {
return false;
- credential->certs.push_back("");
- credential->certs.back().assign(cert_data, cert_len);
+ }
+ std::string cert;
+ temp.CopyToString(&cert);
+ credential->certs.push_back(cert);
}
return true;
}
SpdySynStreamControlFrame* SpdyFramer::CreateSynStream(
- SpdyStreamId stream_id, SpdyStreamId associated_stream_id, int priority,
- SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) {
- DCHECK_GT(stream_id, static_cast<SpdyStreamId>(0));
+ SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ SpdyControlFlags flags,
+ bool compressed,
+ const SpdyHeaderBlock* headers) {
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
DCHECK_EQ(0u, associated_stream_id & ~kStreamIdMask);
@@ -889,7 +1054,13 @@
frame.WriteBytes(&flags_length, sizeof(flags_length));
frame.WriteUInt32(stream_id);
frame.WriteUInt32(associated_stream_id);
- frame.WriteUInt16(ntohs(priority) << 6); // Priority.
+ // Cap as appropriate.
+ if (priority > GetLowestPriority()) {
+ DLOG(ERROR) << "Priority out-of-bounds.";
+ priority = GetLowestPriority();
+ }
+ // Priority is 2 bits for <spdy3, 3 bits otherwise.
+ frame.WriteUInt16(ntohs(priority) << (spdy_version_ < 3 ? 6 : 5));
WriteHeaderBlock(&frame, headers);
scoped_ptr<SpdySynStreamControlFrame> syn_frame(
@@ -901,14 +1072,21 @@
return syn_frame.release();
}
-SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(SpdyStreamId stream_id,
- SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) {
+SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(
+ SpdyStreamId stream_id,
+ SpdyControlFlags flags,
+ bool compressed,
+ const SpdyHeaderBlock* headers) {
DCHECK_GT(stream_id, 0u);
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
// Find our length.
size_t expected_frame_size = SpdySynReplyControlFrame::size() +
GetSerializedLength(headers);
+ // In SPDY 2, there were 2 unused bytes before payload.
+ if (spdy_version_ < 3) {
+ expected_frame_size += 2;
+ }
// Create our FlagsAndLength.
FlagsAndLength flags_length = CreateFlagsAndLength(
@@ -920,7 +1098,9 @@
frame.WriteUInt16(SYN_REPLY);
frame.WriteBytes(&flags_length, sizeof(flags_length));
frame.WriteUInt32(stream_id);
- frame.WriteUInt16(0); // Unused
+ if (spdy_version_ < 3) {
+ frame.WriteUInt16(0); // Unused
+ }
WriteHeaderBlock(&frame, headers);
scoped_ptr<SpdySynReplyControlFrame> reply_frame(
@@ -932,9 +1112,9 @@
return reply_frame.release();
}
-/* static */
-SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream(SpdyStreamId stream_id,
- SpdyStatusCodes status) {
+SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream(
+ SpdyStreamId stream_id,
+ SpdyStatusCodes status) const {
DCHECK_GT(stream_id, 0u);
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
DCHECK_NE(status, INVALID);
@@ -949,9 +1129,8 @@
return reinterpret_cast<SpdyRstStreamControlFrame*>(frame.take());
}
-/* static */
SpdySettingsControlFrame* SpdyFramer::CreateSettings(
- const SpdySettings& values) {
+ const SpdySettings& values) const {
SpdyFrameBuilder frame(SpdySettingsControlFrame::size() + 8 * values.size());
frame.WriteUInt16(kControlFlagMask | spdy_version_);
frame.WriteUInt16(SETTINGS);
@@ -962,24 +1141,15 @@
frame.WriteUInt32(values.size());
SpdySettings::const_iterator it = values.begin();
while (it != values.end()) {
- frame.WriteUInt32(it->first.id_);
+ uint32 id_and_flags_wire = it->first.GetWireFormat(spdy_version_);
+ frame.WriteBytes(&id_and_flags_wire, 4);
frame.WriteUInt32(it->second);
++it;
}
return reinterpret_cast<SpdySettingsControlFrame*>(frame.take());
}
-/* static */
-SpdyNoOpControlFrame* SpdyFramer::CreateNopFrame() {
- SpdyFrameBuilder frame(SpdyNoOpControlFrame::size());
- frame.WriteUInt16(kControlFlagMask | spdy_version_);
- frame.WriteUInt16(NOOP);
- frame.WriteUInt32(0);
- return reinterpret_cast<SpdyNoOpControlFrame*>(frame.take());
-}
-
-/* static */
-SpdyPingControlFrame* SpdyFramer::CreatePingFrame(uint32 unique_id) {
+SpdyPingControlFrame* SpdyFramer::CreatePingFrame(uint32 unique_id) const {
SpdyFrameBuilder frame(SpdyPingControlFrame::size());
frame.WriteUInt16(kControlFlagMask | spdy_version_);
frame.WriteUInt16(PING);
@@ -989,9 +1159,8 @@
return reinterpret_cast<SpdyPingControlFrame*>(frame.take());
}
-/* static */
SpdyGoAwayControlFrame* SpdyFramer::CreateGoAway(
- SpdyStreamId last_accepted_stream_id) {
+ SpdyStreamId last_accepted_stream_id) const {
DCHECK_EQ(0u, last_accepted_stream_id & ~kStreamIdMask);
SpdyFrameBuilder frame(SpdyGoAwayControlFrame::size());
@@ -1003,8 +1172,11 @@
return reinterpret_cast<SpdyGoAwayControlFrame*>(frame.take());
}
-SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(SpdyStreamId stream_id,
- SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) {
+SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(
+ SpdyStreamId stream_id,
+ SpdyControlFlags flags,
+ bool compressed,
+ const SpdyHeaderBlock* headers) {
// Basically the same as CreateSynReply().
DCHECK_GT(stream_id, 0u);
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
@@ -1012,6 +1184,10 @@
// Find our length.
size_t expected_frame_size = SpdyHeadersControlFrame::size() +
GetSerializedLength(headers);
+ // In SPDY 2, there were 2 unused bytes before payload.
+ if (spdy_version_ < 3) {
+ expected_frame_size += 2;
+ }
// Create our FlagsAndLength.
FlagsAndLength flags_length = CreateFlagsAndLength(
@@ -1023,7 +1199,9 @@
frame.WriteUInt16(HEADERS);
frame.WriteBytes(&flags_length, sizeof(flags_length));
frame.WriteUInt32(stream_id);
- frame.WriteUInt16(0); // Unused
+ if (spdy_version_ < 3) {
+ frame.WriteUInt16(0); // Unused
+ }
WriteHeaderBlock(&frame, headers);
DCHECK_EQ(static_cast<size_t>(frame.length()), expected_frame_size);
@@ -1036,14 +1214,14 @@
return headers_frame.release();
}
-/* static */
SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate(
SpdyStreamId stream_id,
- uint32 delta_window_size) {
+ uint32 delta_window_size) const {
DCHECK_GT(stream_id, 0u);
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
DCHECK_GT(delta_window_size, 0u);
- DCHECK_LE(delta_window_size, spdy::kSpdyStreamMaximumWindowSize);
+ DCHECK_LE(delta_window_size,
+ static_cast<uint32>(spdy::kSpdyStreamMaximumWindowSize));
SpdyFrameBuilder frame(SpdyWindowUpdateControlFrame::size());
frame.WriteUInt16(kControlFlagMask | spdy_version_);
@@ -1056,25 +1234,24 @@
return reinterpret_cast<SpdyWindowUpdateControlFrame*>(frame.take());
}
-/* static */
SpdyCredentialControlFrame* SpdyFramer::CreateCredentialFrame(
- const SpdyCredential& credential) {
+ const SpdyCredential& credential) const {
// Calculate the size of the frame by adding the size of the
// variable length data to the size of the fixed length data.
size_t frame_size = SpdyCredentialControlFrame::size() +
credential.proof.length();
DCHECK_EQ(SpdyCredentialControlFrame::size(), 14u);
- for (vector<std::string>::const_iterator cert = credential.certs.begin();
+ for (std::vector<std::string>::const_iterator cert = credential.certs.begin();
cert != credential.certs.end();
- cert++) {
+ ++cert) {
frame_size += sizeof(uint32); // size of the cert_length field
- frame_size += cert->length(); // size of the cert_data field
+ frame_size += cert->length(); // size of the cert_data field
}
size_t payload_size = frame_size - SpdyFrame::kHeaderSize;
SpdyFrameBuilder frame(frame_size);
// Create our FlagsAndLength.
- SpdyControlFlags flags = spdy::CONTROL_FLAG_NONE;
+ SpdyControlFlags flags = CONTROL_FLAG_NONE;
FlagsAndLength flags_length = CreateFlagsAndLength(flags, payload_size);
frame.WriteUInt16(kControlFlagMask | spdy_version_);
@@ -1083,9 +1260,9 @@
frame.WriteUInt16(credential.slot);
frame.WriteUInt32(credential.proof.size());
frame.WriteBytes(credential.proof.c_str(), credential.proof.size());
- for (vector<std::string>::const_iterator cert = credential.certs.begin();
+ for (std::vector<std::string>::const_iterator cert = credential.certs.begin();
cert != credential.certs.end();
- cert++) {
+ ++cert) {
frame.WriteUInt32(cert->length());
frame.WriteBytes(cert->c_str(), cert->length());
}
@@ -1095,7 +1272,6 @@
SpdyDataFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id,
const char* data,
uint32 len, SpdyDataFlags flags) {
- DCHECK_GT(stream_id, 0u);
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
SpdyFrameBuilder frame(SpdyDataFrame::size() + len);
@@ -1112,10 +1288,10 @@
scoped_ptr<SpdyFrame> data_frame(frame.take());
SpdyDataFrame* rv;
if (flags & DATA_FLAG_COMPRESSED) {
- rv = reinterpret_cast<SpdyDataFrame*>(CompressFrame(*data_frame.get()));
- } else {
- rv = reinterpret_cast<SpdyDataFrame*>(data_frame.release());
+ LOG(DFATAL) << "DATA_FLAG_COMPRESSED invalid for " << display_protocol_
+ << ".";
}
+ rv = reinterpret_cast<SpdyDataFrame*>(data_frame.release());
if (flags & DATA_FLAG_FIN) {
CleanupCompressorForStream(stream_id);
@@ -1131,46 +1307,18 @@
static const int kCompressorWindowSizeInBits = 11;
static const int kCompressorMemLevel = 1;
-// This is just a hacked dictionary to use for shrinking HTTP-like headers.
-// TODO(mbelshe): Use a scientific methodology for computing the dictionary.
-const char SpdyFramer::kDictionary[] =
- "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
- "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
- "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
- "-agent10010120020120220320420520630030130230330430530630740040140240340440"
- "5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
- "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
- "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
- "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
- "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
- "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
- "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
- "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
- ".1statusversionurl";
-const int SpdyFramer::kDictionarySize = arraysize(kDictionary);
-
SpdyFrame* SpdyFramer::CompressFrame(const SpdyFrame& frame) {
if (frame.is_control_frame()) {
return CompressControlFrame(
reinterpret_cast<const SpdyControlFrame&>(frame));
}
- return CompressDataFrame(reinterpret_cast<const SpdyDataFrame&>(frame));
+ return NULL;
}
-SpdyFrame* SpdyFramer::DecompressFrame(const SpdyFrame& frame) {
- if (frame.is_control_frame()) {
- return DecompressControlFrame(
- reinterpret_cast<const SpdyControlFrame&>(frame));
- }
- return DecompressDataFrame(reinterpret_cast<const SpdyDataFrame&>(frame));
-}
-
bool SpdyFramer::IsCompressible(const SpdyFrame& frame) const {
// The important frames to compress are those which contain large
// amounts of compressible data - namely the headers in the SYN_STREAM
// and SYN_REPLY.
- // TODO(mbelshe): Reconcile this with the spec when the spec is
- // explicit about which frames compress and which do not.
if (frame.is_control_frame()) {
const SpdyControlFrame& control_frame =
reinterpret_cast<const SpdyControlFrame&>(frame);
@@ -1178,9 +1326,8 @@
control_frame.type() == SYN_REPLY;
}
- const SpdyDataFrame& data_frame =
- reinterpret_cast<const SpdyDataFrame&>(frame);
- return (data_frame.flags() & DATA_FLAG_COMPRESSED) != 0;
+ // We don't compress Data frames.
+ return false;
}
z_stream* SpdyFramer::GetHeaderCompressor() {
@@ -1196,10 +1343,15 @@
kCompressorWindowSizeInBits,
kCompressorMemLevel,
Z_DEFAULT_STRATEGY);
- if (success == Z_OK)
+ if (success == Z_OK) {
+ const char* dictionary = (spdy_version_ < 3) ? kV2Dictionary
+ : kV3Dictionary;
+ const int dictionary_size = (spdy_version_ < 3) ? kV2DictionarySize
+ : kV3DictionarySize;
success = deflateSetDictionary(header_compressor_.get(),
- reinterpret_cast<const Bytef*>(kDictionary),
- kDictionarySize);
+ reinterpret_cast<const Bytef*>(dictionary),
+ dictionary_size);
+ }
if (success != Z_OK) {
LOG(WARNING) << "deflateSetDictionary failure: " << success;
header_compressor_.reset(NULL);
@@ -1224,27 +1376,6 @@
return header_decompressor_.get();
}
-z_stream* SpdyFramer::GetStreamCompressor(SpdyStreamId stream_id) {
- CompressorMap::iterator it = stream_compressors_.find(stream_id);
- if (it != stream_compressors_.end())
- return it->second; // Already initialized.
-
- scoped_ptr<z_stream> compressor(new z_stream);
- memset(compressor.get(), 0, sizeof(z_stream));
-
- int success = deflateInit2(compressor.get(),
- kCompressorLevel,
- Z_DEFLATED,
- kCompressorWindowSizeInBits,
- kCompressorMemLevel,
- Z_DEFAULT_STRATEGY);
- if (success != Z_OK) {
- LOG(WARNING) << "deflateInit failure: " << success;
- return NULL;
- }
- return stream_compressors_[stream_id] = compressor.release();
-}
-
z_stream* SpdyFramer::GetStreamDecompressor(SpdyStreamId stream_id) {
CompressorMap::iterator it = stream_decompressors_.find(stream_id);
if (it != stream_decompressors_.end())
@@ -1288,6 +1419,11 @@
*payload_length = syn_frame.header_block_len();
*header_length = frame_size;
*payload = frame.data() + *header_length;
+ // SPDY 2 had two bytes of unused space preceeding payload.
+ if (spdy_version_ < 3) {
+ *header_length += 2;
+ *payload += 2;
+ }
}
break;
case HEADERS:
@@ -1298,6 +1434,11 @@
*payload_length = headers_frame.header_block_len();
*header_length = frame_size;
*payload = frame.data() + *header_length;
+ // SPDY 2 had two bytes of unused space preceeding payload.
+ if (spdy_version_ < 3) {
+ *header_length += 2;
+ *payload += 2;
+ }
}
break;
default:
@@ -1318,40 +1459,7 @@
z_stream* compressor = GetHeaderCompressor();
if (!compressor)
return NULL;
- return reinterpret_cast<SpdyControlFrame*>(
- CompressFrameWithZStream(frame, compressor));
-}
-SpdyDataFrame* SpdyFramer::CompressDataFrame(const SpdyDataFrame& frame) {
- z_stream* compressor = GetStreamCompressor(frame.stream_id());
- if (!compressor)
- return NULL;
- return reinterpret_cast<SpdyDataFrame*>(
- CompressFrameWithZStream(frame, compressor));
-}
-
-SpdyControlFrame* SpdyFramer::DecompressControlFrame(
- const SpdyControlFrame& frame) {
- z_stream* decompressor = GetHeaderDecompressor();
- if (!decompressor) {
- LOG(DFATAL) << "Couldn't get decompressor for handling control frame.";
- set_error(SPDY_DECOMPRESS_FAILURE);
- return NULL;
- }
- return reinterpret_cast<SpdyControlFrame*>(
- DecompressFrameWithZStream(frame, decompressor));
-}
-
-SpdyDataFrame* SpdyFramer::DecompressDataFrame(const SpdyDataFrame& frame) {
- z_stream* decompressor = GetStreamDecompressor(frame.stream_id());
- if (!decompressor)
- return NULL;
- return reinterpret_cast<SpdyDataFrame*>(
- DecompressFrameWithZStream(frame, decompressor));
-}
-
-SpdyFrame* SpdyFramer::CompressFrameWithZStream(const SpdyFrame& frame,
- z_stream* compressor) {
int payload_length;
int header_length;
const char* payload;
@@ -1361,7 +1469,7 @@
base::StatsCounter post_compress_bytes("spdy.PostCompressSize");
if (!enable_compression_)
- return DuplicateFrame(frame);
+ return reinterpret_cast<SpdyControlFrame*>(DuplicateFrame(frame));
if (!GetFrameBoundaries(frame, &payload_length, &header_length, &payload))
return NULL;
@@ -1369,7 +1477,13 @@
// Create an output frame.
int compressed_max_size = deflateBound(compressor, payload_length);
int new_frame_size = header_length + compressed_max_size;
- scoped_ptr<SpdyFrame> new_frame(new SpdyFrame(new_frame_size));
+ if ((frame.type() == SYN_REPLY || frame.type() == HEADERS) &&
+ spdy_version_ < 3) {
+ new_frame_size += 2;
+ }
+ DCHECK_GE(new_frame_size,
+ static_cast<int>(frame.length() + SpdyFrame::kHeaderSize));
+ scoped_ptr<SpdyControlFrame> new_frame(new SpdyControlFrame(new_frame_size));
memcpy(new_frame->data(), frame.data(),
frame.length() + SpdyFrame::kHeaderSize);
@@ -1380,6 +1494,9 @@
compressor->avail_out = compressed_max_size;
// Data packets have a 'compressed' flag.
+ // TODO(hkhalil): Remove post code-yellow. It's impossible to execute this
+ // branch given that SpdyControlFrame::is_control_frame always returns true.
+ DCHECK(new_frame->is_control_frame());
if (!new_frame->is_control_frame()) {
SpdyDataFrame* data_frame =
reinterpret_cast<SpdyDataFrame*>(new_frame.get());
@@ -1416,85 +1533,6 @@
return new_frame.release();
}
-SpdyFrame* SpdyFramer::DecompressFrameWithZStream(const SpdyFrame& frame,
- z_stream* decompressor) {
- int payload_length;
- int header_length;
- const char* payload;
-
- base::StatsCounter decompressed_frames("spdy.DecompressedFrames");
- base::StatsCounter pre_decompress_bytes("spdy.PreDeCompressSize");
- base::StatsCounter post_decompress_bytes("spdy.PostDeCompressSize");
-
- if (!enable_compression_)
- return DuplicateFrame(frame);
-
- if (!GetFrameBoundaries(frame, &payload_length, &header_length, &payload))
- return NULL;
-
- if (!frame.is_control_frame()) {
- const SpdyDataFrame& data_frame =
- reinterpret_cast<const SpdyDataFrame&>(frame);
- if ((data_frame.flags() & DATA_FLAG_COMPRESSED) == 0)
- return DuplicateFrame(frame);
- }
-
- // Create an output frame. Assume it does not need to be longer than
- // the input data.
- size_t decompressed_max_size = kControlFrameBufferInitialSize;
- int new_frame_size = header_length + decompressed_max_size;
- if (frame.length() > decompressed_max_size)
- return NULL;
- scoped_ptr<SpdyFrame> new_frame(new SpdyFrame(new_frame_size));
- memcpy(new_frame->data(), frame.data(),
- frame.length() + SpdyFrame::kHeaderSize);
-
- decompressor->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(payload));
- decompressor->avail_in = payload_length;
- decompressor->next_out = reinterpret_cast<Bytef*>(new_frame->data()) +
- header_length;
- decompressor->avail_out = decompressed_max_size;
-
- int rv = inflate(decompressor, Z_SYNC_FLUSH);
- if (rv == Z_NEED_DICT) {
- // Need to try again with the right dictionary.
- if (decompressor->adler == kDictionaryId) {
- rv = inflateSetDictionary(decompressor,
- (const Bytef*)SpdyFramer::kDictionary,
- SpdyFramer::kDictionarySize);
- if (rv == Z_OK)
- rv = inflate(decompressor, Z_SYNC_FLUSH);
- }
- }
- if (rv != Z_OK) { // How can we know that it decompressed everything?
- LOG(WARNING) << "inflate failure: " << rv;
- return NULL;
- }
-
- // Unset the compressed flag for data frames.
- if (!new_frame->is_control_frame()) {
- SpdyDataFrame* data_frame =
- reinterpret_cast<SpdyDataFrame*>(new_frame.get());
- data_frame->set_flags(data_frame->flags() & ~DATA_FLAG_COMPRESSED);
- }
-
- int decompressed_size = decompressed_max_size - decompressor->avail_out;
- new_frame->set_length(
- header_length + decompressed_size - SpdyFrame::kHeaderSize);
-
- // If there is data left, then the frame didn't fully decompress. This
- // means that there is stranded data at the end of this frame buffer which
- // will be ignored.
- DCHECK_EQ(decompressor->avail_in, 0u);
-
- pre_decompress_bytes.Add(frame.length());
- post_decompress_bytes.Add(new_frame->length());
-
- decompressed_frames.Increment();
-
- return new_frame.release();
-}
-
// Incrementally decompress the control frame's header block, feeding the
// result to the visitor in chunks. Continue this until the visitor
// indicates that it cannot process any more data, or (more commonly) we
@@ -1521,12 +1559,31 @@
while (decomp->avail_in > 0 && processed_successfully) {
decomp->next_out = reinterpret_cast<Bytef*>(buffer);
decomp->avail_out = arraysize(buffer);
- int rv = DecompressHeaderBlockInZStream(decomp);
- if (rv != Z_OK && rv != Z_BUF_ERROR) {
- set_error(SPDY_DECOMPRESS_FAILURE);
- DLOG(WARNING) << "inflate failure: " << rv;
- processed_successfully = false;
- } else {
+
+ int rv = inflate(decomp, Z_SYNC_FLUSH);
+ if (rv == Z_NEED_DICT) {
+ const char* dictionary = (spdy_version_ < 3) ? kV2Dictionary
+ : kV3Dictionary;
+ const int dictionary_size = (spdy_version_ < 3) ? kV2DictionarySize
+ : kV3DictionarySize;
+ const uLong dictionary_id = (spdy_version_ < 3) ? kV2DictionaryId
+ : kV3DictionaryId;
+ // Need to try again with the right dictionary.
+ if (decomp->adler == dictionary_id) {
+ rv = inflateSetDictionary(decomp,
+ reinterpret_cast<const Bytef*>(dictionary),
+ dictionary_size);
+ if (rv == Z_OK)
+ rv = inflate(decomp, Z_SYNC_FLUSH);
+ }
+ }
+
+ // Inflate will generate a Z_BUF_ERROR if it runs out of input
+ // without producing any output. The input is consumed and
+ // buffered internally by zlib so we can detect this condition by
+ // checking if avail_in is 0 after the call to inflate.
+ bool input_exhausted = ((rv == Z_BUF_ERROR) && (decomp->avail_in == 0));
+ if ((rv == Z_OK) || input_exhausted) {
size_t decompressed_len = arraysize(buffer) - decomp->avail_out;
if (decompressed_len > 0) {
processed_successfully = visitor_->OnControlFrameHeaderData(
@@ -1537,6 +1594,10 @@
// visitor.
set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE);
}
+ } else {
+ DLOG(WARNING) << "inflate failure: " << rv << " " << len;
+ set_error(SPDY_DECOMPRESS_FAILURE);
+ processed_successfully = false;
}
}
return processed_successfully;
@@ -1546,7 +1607,6 @@
const SpdyControlFrame* control_frame, const char* data, size_t len) {
bool read_successfully = true;
const SpdyStreamId stream_id = GetControlFrameStreamId(control_frame);
- DCHECK_LT(0u, stream_id);
while (read_successfully && len > 0) {
size_t bytes_to_deliver = std::min(len, kHeaderDataChunkMaxSize);
read_successfully = visitor_->OnControlFrameHeaderData(stream_id, data,
@@ -1622,7 +1682,10 @@
case SETTINGS:
return SpdySettingsControlFrame::size();
case NOOP:
- return SpdyNoOpControlFrame::size();
+ // Even though NOOP is no longer supported, we still correctly report its
+ // size so that it can be handled correctly as incoming data if
+ // implementations so desire.
+ return SpdyFrame::kHeaderSize;
case PING:
return SpdyPingControlFrame::size();
case GOAWAY:
@@ -1682,42 +1745,16 @@
return stream_id;
}
-size_t SpdyFramer::BytesSafeToRead() const {
- switch (state_) {
- case SPDY_ERROR:
- case SPDY_DONE:
- case SPDY_AUTO_RESET:
- case SPDY_RESET:
- return 0;
- case SPDY_READING_COMMON_HEADER:
- DCHECK_LT(current_frame_len_,
- static_cast<size_t>(SpdyFrame::kHeaderSize));
- return SpdyFrame::kHeaderSize - current_frame_len_;
- // TODO(rtenneti): Add support for SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK
- // and SPDY_CONTROL_FRAME_HEADER_BLOCK.
- case SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK:
- case SPDY_CONTROL_FRAME_HEADER_BLOCK:
- return 0;
- case SPDY_CONTROL_FRAME_PAYLOAD:
- case SPDY_CREDENTIAL_FRAME_PAYLOAD:
- case SPDY_IGNORE_REMAINING_PAYLOAD:
- case SPDY_FORWARD_STREAM_FRAME:
- return remaining_data_;
- }
- // We should never get to here.
- return 0;
-}
-
void SpdyFramer::set_enable_compression(bool value) {
enable_compression_ = value;
}
+void SpdyFramer::set_validate_control_frame_sizes(bool value) {
+ validate_control_frame_sizes_ = value;
+}
+
void SpdyFramer::set_enable_compression_default(bool value) {
compression_default_ = value;
}
-void SpdyFramer::set_validate_control_frame_sizes(bool value) {
- validate_control_frame_sizes_ = value;
-}
-
} // namespace spdy

Powered by Google App Engine
This is Rietveld 408576698