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 |