diff options
author | jgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-19 05:55:42 +0000 |
---|---|---|
committer | jgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-19 05:55:42 +0000 |
commit | e3352df2a6cd878f3db12f405bcacbce4c0dee6e (patch) | |
tree | 51eb2ecbbfae4d37e08f9989068467c0dcf77023 | |
parent | c4432939ffbacd0b7aedded5ecb604a366c2310e (diff) | |
download | chromium_src-e3352df2a6cd878f3db12f405bcacbce4c0dee6e.zip chromium_src-e3352df2a6cd878f3db12f405bcacbce4c0dee6e.tar.gz chromium_src-e3352df2a6cd878f3db12f405bcacbce4c0dee6e.tar.bz2 |
SpdyFramer now uses HPACK for SPDY4 headers (re-apply)
SpdyFramer defers calling OnControlFrameHeadersData() until the entire HPACK
headers block has been decoded. At that point, the block is re-encoded to
legacy SPDY3 format and passed to the visitor.
This is a temporary measure suitable for testing only, intended to allow
a decoupled switch to HPACK prior to being fully wired for
SpdyHeadersHandlerInterface.
This lands server change 62461978 by jgraettinger.
BUG=339578
Review URL: https://codereview.chromium.org/201373002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@257857 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/quic/spdy_utils.cc | 2 | ||||
-rw-r--r-- | net/spdy/hpack_encoding_context.cc | 7 | ||||
-rw-r--r-- | net/spdy/hpack_huffman_table.cc | 7 | ||||
-rw-r--r-- | net/spdy/spdy_framer.cc | 215 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 32 | ||||
-rw-r--r-- | net/spdy/spdy_framer_test.cc | 729 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 49 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.h | 2 | ||||
-rw-r--r-- | net/tools/quic/spdy_utils.cc | 2 |
9 files changed, 542 insertions, 503 deletions
diff --git a/net/quic/spdy_utils.cc b/net/quic/spdy_utils.cc index 12bdca9..350819e 100644 --- a/net/quic/spdy_utils.cc +++ b/net/quic/spdy_utils.cc @@ -15,7 +15,7 @@ namespace net { // static string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) { - int length = SpdyFramer::GetSerializedLength(SPDY3, &headers, true); + int length = SpdyFramer::GetSerializedLength(SPDY3, &headers); SpdyFrameBuilder builder(length); SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers); scoped_ptr<SpdyFrame> block(builder.take()); diff --git a/net/spdy/hpack_encoding_context.cc b/net/spdy/hpack_encoding_context.cc index 44807ac..11a99bc 100644 --- a/net/spdy/hpack_encoding_context.cc +++ b/net/spdy/hpack_encoding_context.cc @@ -101,10 +101,13 @@ const size_t kStaticEntryCount = arraysize(kStaticTable); } // namespace -const uint32 HpackEncodingContext::kUntouched = HpackEntry::kUntouched; +// Must match HpackEntry::kUntouched. +const uint32 HpackEncodingContext::kUntouched = 0x7fffffff; HpackEncodingContext::HpackEncodingContext() - : settings_header_table_size_(kDefaultHeaderTableSizeSetting) {} + : settings_header_table_size_(kDefaultHeaderTableSizeSetting) { + DCHECK_EQ(HpackEncodingContext::kUntouched, HpackEntry::kUntouched); +} HpackEncodingContext::~HpackEncodingContext() {} diff --git a/net/spdy/hpack_huffman_table.cc b/net/spdy/hpack_huffman_table.cc index 5372b97..02910eb 100644 --- a/net/spdy/hpack_huffman_table.cc +++ b/net/spdy/hpack_huffman_table.cc @@ -22,9 +22,6 @@ namespace { const uint8 kDecodeTableRootBits = 9; // Maximum number of bits to index in successive decode tables. const uint8 kDecodeTableBranchBits = 6; -// Number of decode iterations required for a 32-bit code. -const int kDecodeIterations = static_cast<int>( - std::ceil((32.f - kDecodeTableRootBits) / kDecodeTableBranchBits)); bool SymbolLengthAndIdCompare(const HpackHuffmanSymbol& a, const HpackHuffmanSymbol& b) { @@ -254,6 +251,10 @@ void HpackHuffmanTable::EncodeString(StringPiece in, bool HpackHuffmanTable::DecodeString(HpackInputStream* in, size_t out_capacity, string* out) const { + // Number of decode iterations required for a 32-bit code. + const int kDecodeIterations = static_cast<int>( + std::ceil((32.f - kDecodeTableRootBits) / kDecodeTableBranchBits)); + out->clear(); // Current input, stored in the high |bits_available| bits of |bits|. diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index ad9016f..132a555 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -17,6 +17,7 @@ #include "net/spdy/spdy_bitmasks.h" #include "third_party/zlib/zlib.h" +using std::string; using std::vector; namespace net { @@ -125,6 +126,7 @@ bool SpdyFramerVisitorInterface::OnRstStreamFrameData( SpdyFramer::SpdyFramer(SpdyMajorVersion version) : current_frame_buffer_(new char[kControlFrameBufferSize]), enable_compression_(true), + hpack_decoder_(ObtainHpackHuffmanTable()), visitor_(NULL), debug_visitor_(NULL), display_protocol_("SPDY"), @@ -516,7 +518,8 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { } case SPDY_CONTROL_FRAME_HEADER_BLOCK: { - int bytes_read = ProcessControlFrameHeaderBlock(data, len); + int bytes_read = ProcessControlFrameHeaderBlock( + data, len, spdy_version_ >= 4 ? true : false); len -= bytes_read; data += bytes_read; break; @@ -988,17 +991,13 @@ size_t SpdyFramer::UpdateCurrentFrameBuffer(const char** data, size_t* len, } size_t SpdyFramer::GetSerializedLength(const int spdy_version, - const SpdyHeaderBlock* headers, - bool begin_block) { + const SpdyHeaderBlock* headers) { const size_t num_name_value_pairs_size = (spdy_version < 3) ? sizeof(uint16) : sizeof(uint32); const size_t length_of_name_size = num_name_value_pairs_size; const size_t length_of_value_size = num_name_value_pairs_size; - size_t total_length = 0; - if (begin_block) { - total_length += 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) { @@ -1407,7 +1406,8 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, // IncrementallyDeliverControlFrameHeaderData() or // IncrementallyDecompressControlFrameHeaderData() respectively. size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, - size_t data_len) { + size_t data_len, + bool is_hpack_header_block) { DCHECK_EQ(SPDY_CONTROL_FRAME_HEADER_BLOCK, state_); bool processed_successfully = true; @@ -1419,33 +1419,54 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, LOG(DFATAL) << "Unhandled frame type in ProcessControlFrameHeaderBlock."; } size_t process_bytes = std::min(data_len, remaining_data_length_); - if (process_bytes > 0) { - if (enable_compression_) { + if (is_hpack_header_block) { + if (!hpack_decoder_.HandleControlFrameHeadersData(current_frame_stream_id_, + data, + process_bytes)) { + // TODO(jgraettinger): Finer-grained HPACK error codes. + set_error(SPDY_DECOMPRESS_FAILURE); + processed_successfully = false; + } + } else if (process_bytes > 0) { + if (enable_compression_ && spdy_version_ < 4) { processed_successfully = IncrementallyDecompressControlFrameHeaderData( current_frame_stream_id_, data, process_bytes); } else { processed_successfully = IncrementallyDeliverControlFrameHeaderData( current_frame_stream_id_, data, process_bytes); } - - remaining_data_length_ -= process_bytes; } + remaining_data_length_ -= process_bytes; // Handle the case that there is no futher data in this frame. if (remaining_data_length_ == 0 && processed_successfully) { if (expect_continuation_ == 0) { - // The complete header block has been delivered. We send a zero-length - // OnControlFrameHeaderData() to indicate this. - visitor_->OnControlFrameHeaderData(current_frame_stream_id_, NULL, 0); - + if (is_hpack_header_block) { + if (!hpack_decoder_.HandleControlFrameHeadersComplete( + current_frame_stream_id_)) { + set_error(SPDY_DECOMPRESS_FAILURE); + processed_successfully = false; + } else { + // TODO(jgraettinger): To be removed with migration to + // SpdyHeadersHandlerInterface. Serializes the HPACK block as a SPDY3 + // block, delivered via reentrant call to + // ProcessControlFrameHeaderBlock(). + DeliverHpackBlockAsSpdy3Block(); + return process_bytes; + } + } else { + // The complete header block has been delivered. We send a zero-length + // OnControlFrameHeaderData() to indicate this. + visitor_->OnControlFrameHeaderData(current_frame_stream_id_, NULL, 0); + } // If this is a FIN, tell the caller. if ((current_frame_flags_ & CONTROL_FLAG_FIN) || end_stream_when_done_) { end_stream_when_done_ = false; visitor_->OnStreamFrameData(current_frame_stream_id_, NULL, 0, true); } } - - CHANGE_STATE(SPDY_AUTO_RESET); + if (processed_successfully) + CHANGE_STATE(SPDY_AUTO_RESET); } // Handle error. @@ -1513,6 +1534,26 @@ size_t SpdyFramer::ProcessSettingsFramePayload(const char* data, return processed_bytes; } +void SpdyFramer::DeliverHpackBlockAsSpdy3Block() { + DCHECK_GE(spdy_version_, SPDY4); + DCHECK_EQ(remaining_data_length_, 0u); + + const SpdyNameValueBlock& block = hpack_decoder_.decoded_block(); + if (block.empty()) { + // Special-case this to make tests happy. + ProcessControlFrameHeaderBlock(NULL, 0, false); + return; + } + SpdyFrameBuilder builder( + GetSerializedLength(protocol_version(), &block)); + + SerializeNameValueBlockWithoutCompression(&builder, block); + scoped_ptr<SpdyFrame> frame(builder.take()); + + remaining_data_length_ = frame->size(); + ProcessControlFrameHeaderBlock(frame->data(), frame->size(), false); +} + bool SpdyFramer::ProcessSetting(const char* data) { SpdySettingsIds id; uint8 flags = 0; @@ -1907,8 +1948,16 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream( } // The size of this frame, including variable-length name-value block. - const size_t size = GetSynStreamMinimumSize() - + GetSerializedLength(syn_stream.name_value_block(), true); + size_t size = GetSynStreamMinimumSize(); + + string hpack_encoding; + if (spdy_version_ >= 4) { + hpack_encoder_.EncodeHeaderSet(syn_stream.name_value_block(), + &hpack_encoding); + size += hpack_encoding.size(); + } else { + size += GetSerializedLength(syn_stream.name_value_block()); + } SpdyFrameBuilder builder(size); if (spdy_version_ < 4) { @@ -1925,11 +1974,16 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream( builder.WriteUInt32(priority); } DCHECK_EQ(GetSynStreamMinimumSize(), builder.length()); - SerializeNameValueBlock(&builder, syn_stream, true); + if (spdy_version_ >= 4) { + builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size()); + } else { + SerializeNameValueBlock(&builder, syn_stream); + } if (debug_visitor_) { - const size_t payload_len = GetSerializedLength( - protocol_version(), &(syn_stream.name_value_block()), true); + const size_t payload_len = spdy_version_ >= 4 ? hpack_encoding.size() : + GetSerializedLength(protocol_version(), + &(syn_stream.name_value_block())); // SPDY 4 reports this compression as a SYN_STREAM compression. debug_visitor_->OnSendCompressedFrame(syn_stream.stream_id(), SYN_STREAM, @@ -1953,8 +2007,16 @@ SpdySerializedFrame* SpdyFramer::SerializeSynReply( } // The size of this frame, including variable-length name-value block. - size_t size = GetSynReplyMinimumSize() - + GetSerializedLength(syn_reply.name_value_block(), true); + size_t size = GetSynReplyMinimumSize(); + + string hpack_encoding; + if (spdy_version_ >= 4) { + hpack_encoder_.EncodeHeaderSet(syn_reply.name_value_block(), + &hpack_encoding); + size += hpack_encoding.size(); + } else { + size += GetSerializedLength(syn_reply.name_value_block()); + } SpdyFrameBuilder builder(size); if (spdy_version_ < 4) { @@ -1970,11 +2032,16 @@ SpdySerializedFrame* SpdyFramer::SerializeSynReply( builder.WriteUInt16(0); // Unused. } DCHECK_EQ(GetSynReplyMinimumSize(), builder.length()); - SerializeNameValueBlock(&builder, syn_reply, true); + if (spdy_version_ >= 4) { + builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size()); + } else { + SerializeNameValueBlock(&builder, syn_reply); + } if (debug_visitor_) { - const size_t payload_len = GetSerializedLength( - protocol_version(), &(syn_reply.name_value_block()), true); + const size_t payload_len = spdy_version_ >= 4 ? hpack_encoding.size() : + GetSerializedLength(protocol_version(), + &(syn_reply.name_value_block())); debug_visitor_->OnSendCompressedFrame(syn_reply.stream_id(), SYN_REPLY, payload_len, @@ -2150,8 +2217,7 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( } // The size of this frame, including variable-length name-value block. - size_t size = GetHeadersMinimumSize() - + GetSerializedLength(headers.name_value_block(), true); + size_t size = GetHeadersMinimumSize(); uint32 priority = headers.priority(); if (headers.has_priority()) { @@ -2162,6 +2228,14 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( size += 4; } + string hpack_encoding; + if (spdy_version_ >= 4) { + hpack_encoder_.EncodeHeaderSet(headers.name_value_block(), &hpack_encoding); + size += hpack_encoding.size(); + } else { + size += GetSerializedLength(headers.name_value_block()); + } + SpdyFrameBuilder builder(size); if (spdy_version_ < 4) { builder.WriteControlFrameHeader(*this, HEADERS, flags); @@ -2180,11 +2254,16 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( } DCHECK_EQ(GetHeadersMinimumSize(), builder.length()); - SerializeNameValueBlock(&builder, headers, true); + if (spdy_version_ >= 4) { + builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size()); + } else { + SerializeNameValueBlock(&builder, headers); + } if (debug_visitor_) { - const size_t payload_len = GetSerializedLength( - protocol_version(), &(headers.name_value_block()), true); + const size_t payload_len = spdy_version_ >= 4 ? hpack_encoding.size() : + GetSerializedLength(protocol_version(), + &(headers.name_value_block())); debug_visitor_->OnSendCompressedFrame(headers.stream_id(), HEADERS, payload_len, @@ -2219,8 +2298,16 @@ SpdyFrame* SpdyFramer::SerializePushPromise( flags |= PUSH_PROMISE_FLAG_END_PUSH_PROMISE; } // The size of this frame, including variable-length name-value block. - size_t size = GetPushPromiseMinimumSize() - + GetSerializedLength(push_promise.name_value_block(), true); + size_t size = GetPushPromiseMinimumSize(); + + string hpack_encoding; + if (spdy_version_ >= 4) { + hpack_encoder_.EncodeHeaderSet(push_promise.name_value_block(), + &hpack_encoding); + size += hpack_encoding.size(); + } else { + size += GetSerializedLength(push_promise.name_value_block()); + } SpdyFrameBuilder builder(size); builder.WriteFramePrefix(*this, PUSH_PROMISE, flags, @@ -2228,11 +2315,16 @@ SpdyFrame* SpdyFramer::SerializePushPromise( builder.WriteUInt32(push_promise.promised_stream_id()); DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length()); - SerializeNameValueBlock(&builder, push_promise, true); + if (spdy_version_ >= 4) { + builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size()); + } else { + SerializeNameValueBlock(&builder, push_promise); + } if (debug_visitor_) { - const size_t payload_len = GetSerializedLength( - protocol_version(), &(push_promise.name_value_block()), true); + const size_t payload_len = spdy_version_ >= 4 ? hpack_encoding.size() : + GetSerializedLength(protocol_version(), + &(push_promise.name_value_block())); debug_visitor_->OnSendCompressedFrame(push_promise.stream_id(), PUSH_PROMISE, payload_len, builder.length()); } @@ -2240,27 +2332,33 @@ SpdyFrame* SpdyFramer::SerializePushPromise( return builder.take(); } +// TODO(jgraettinger): This implementation is incorrect. The continuation +// frame continues a previously-begun HPACK encoding; it doesn't begin a +// new one. Figure out whether it makes sense to keep SerializeContinuation(). SpdyFrame* SpdyFramer::SerializeContinuation( const SpdyContinuationIR& continuation) { + CHECK_GE(spdy_version_, 4); uint8 flags = 0; if (continuation.end_headers()) { flags |= HEADERS_FLAG_END_HEADERS; } // The size of this frame, including variable-length name-value block. - size_t size = GetContinuationMinimumSize() - + GetSerializedLength(continuation.name_value_block(), false); + size_t size = GetContinuationMinimumSize(); + string hpack_encoding; + hpack_encoder_.EncodeHeaderSet(continuation.name_value_block(), + &hpack_encoding); + size += hpack_encoding.size(); SpdyFrameBuilder builder(size); builder.WriteFramePrefix(*this, CONTINUATION, flags, continuation.stream_id()); DCHECK_EQ(GetContinuationMinimumSize(), builder.length()); - SerializeNameValueBlock(&builder, continuation, false); + builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size()); if (debug_visitor_) { - const size_t payload_len = GetSerializedLength( - protocol_version(), &(continuation.name_value_block()), false); + const size_t payload_len = hpack_encoding.size(); debug_visitor_->OnSendCompressedFrame(continuation.stream_id(), CONTINUATION, payload_len, builder.length()); } @@ -2330,10 +2428,10 @@ SpdySerializedFrame* SpdyFramer::SerializeFrame(const SpdyFrameIR& frame) { return visitor.ReleaseSerializedFrame(); } - size_t SpdyFramer::GetSerializedLength(const SpdyHeaderBlock& headers, - bool begin_block) { +size_t SpdyFramer::GetSerializedLength(const SpdyHeaderBlock& headers) { + CHECK_LT(spdy_version_, 4); const size_t uncompressed_length = - GetSerializedLength(protocol_version(), &headers, begin_block); + GetSerializedLength(protocol_version(), &headers); if (!enable_compression_) { return uncompressed_length; } @@ -2496,15 +2594,12 @@ bool SpdyFramer::IncrementallyDeliverControlFrameHeaderData( void SpdyFramer::SerializeNameValueBlockWithoutCompression( SpdyFrameBuilder* builder, - const SpdyNameValueBlock& name_value_block, - bool begin_block) const { + const SpdyNameValueBlock& name_value_block) const { // Serialize number of headers. - if (begin_block) { - if (protocol_version() < 3) { - builder->WriteUInt16(name_value_block.size()); - } else { - builder->WriteUInt32(name_value_block.size()); - } + if (protocol_version() < 3) { + builder->WriteUInt16(name_value_block.size()); + } else { + builder->WriteUInt32(name_value_block.size()); } // Serialize each header. @@ -2523,21 +2618,19 @@ void SpdyFramer::SerializeNameValueBlockWithoutCompression( void SpdyFramer::SerializeNameValueBlock( SpdyFrameBuilder* builder, - const SpdyFrameWithNameValueBlockIR& frame, - bool begin_block) { + const SpdyFrameWithNameValueBlockIR& frame) { + CHECK_LT(spdy_version_, 4); if (!enable_compression_) { return SerializeNameValueBlockWithoutCompression(builder, - frame.name_value_block(), - begin_block); + frame.name_value_block()); } // First build an uncompressed version to be fed into the compressor. const size_t uncompressed_len = GetSerializedLength( - protocol_version(), &(frame.name_value_block()), begin_block); + protocol_version(), &(frame.name_value_block())); SpdyFrameBuilder uncompressed_builder(uncompressed_len); SerializeNameValueBlockWithoutCompression(&uncompressed_builder, - frame.name_value_block(), - begin_block); + frame.name_value_block()); scoped_ptr<SpdyFrame> uncompressed_payload(uncompressed_builder.take()); z_stream* compressor = GetHeaderCompressor(); diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index 1643c2c..59b3d6f 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -16,6 +16,8 @@ #include "base/memory/scoped_ptr.h" #include "base/sys_byteorder.h" #include "net/base/net_export.h" +#include "net/spdy/hpack_decoder.h" +#include "net/spdy/hpack_encoder.h" #include "net/spdy/spdy_header_block.h" #include "net/spdy/spdy_protocol.h" @@ -322,8 +324,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { // Retrieve serialized length of SpdyHeaderBlock. // TODO(hkhalil): Remove, or move to quic code. static size_t GetSerializedLength(const int spdy_version, - const SpdyHeaderBlock* headers, - bool begin_block); + const SpdyHeaderBlock* headers); // Create a new Framer, provided a SPDY version. explicit SpdyFramer(SpdyMajorVersion version); @@ -418,6 +419,9 @@ class NET_EXPORT_PRIVATE SpdyFramer { // Serializes a CONTINUATION frame. The CONTINUATION frame is used // to continue a sequence of header block fragments. + // TODO(jgraettinger): This implementation is incorrect. The continuation + // frame continues a previously-begun HPACK encoding; it doesn't begin a + // new one. Figure out whether it makes sense to keep SerializeContinuation(). SpdySerializedFrame* SerializeContinuation( const SpdyContinuationIR& continuation); @@ -534,12 +538,23 @@ class NET_EXPORT_PRIVATE SpdyFramer { size_t ProcessCommonHeader(const char* data, size_t len); size_t ProcessControlFramePayload(const char* data, size_t len); size_t ProcessControlFrameBeforeHeaderBlock(const char* data, size_t len); - size_t ProcessControlFrameHeaderBlock(const char* data, size_t len); + // HPACK data is re-encoded as SPDY3 and re-entrantly delivered through + // |ProcessControlFrameHeaderBlock()|. |is_hpack_header_block| controls + // whether data is treated as HPACK- vs SPDY3-encoded. + size_t ProcessControlFrameHeaderBlock(const char* data, + size_t len, + bool is_hpack_header_block); size_t ProcessDataFramePayload(const char* data, size_t len); size_t ProcessGoAwayFramePayload(const char* data, size_t len); size_t ProcessRstStreamFramePayload(const char* data, size_t len); size_t ProcessSettingsFramePayload(const char* data, size_t len); + // TODO(jgraettinger): To be removed with migration to + // SpdyHeadersHandlerInterface. + // Serializes the last-processed header block of |hpack_decoder_| as + // a SPDY3 format block, and delivers it to the visitor via reentrant + // call to ProcessControlFrameHeaderBlock(). + void DeliverHpackBlockAsSpdy3Block(); // Helpers for above internal breakouts from ProcessInput. void ProcessControlFrameHeader(uint16 control_frame_type_field); @@ -548,7 +563,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { // Retrieve serialized length of SpdyHeaderBlock. If compression is enabled, a // maximum estimate is returned. - size_t GetSerializedLength(const SpdyHeaderBlock& headers, bool begin_block); + size_t GetSerializedLength(const SpdyHeaderBlock& headers); // Get (and lazily initialize) the ZLib state. z_stream* GetHeaderCompressor(); @@ -576,14 +591,12 @@ class NET_EXPORT_PRIVATE SpdyFramer { void SerializeNameValueBlockWithoutCompression( SpdyFrameBuilder* builder, - const SpdyNameValueBlock& name_value_block, - bool begin_block) const; + const SpdyNameValueBlock& name_value_block) const; // Compresses automatically according to enable_compression_. void SerializeNameValueBlock( SpdyFrameBuilder* builder, - const SpdyFrameWithNameValueBlockIR& frame, - bool begin_block); + const SpdyFrameWithNameValueBlockIR& frame); // Set the error code and moves the framer into the error state. void set_error(SpdyError error); @@ -649,6 +662,9 @@ class NET_EXPORT_PRIVATE SpdyFramer { scoped_ptr<z_stream> header_compressor_; scoped_ptr<z_stream> header_decompressor_; + HpackEncoder hpack_encoder_; + HpackDecoder hpack_decoder_; + SpdyFramerVisitorInterface* visitor_; SpdyFramerDebugVisitorInterface* debug_visitor_; diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index 5ca189f..8f5b63b 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc @@ -8,6 +8,7 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" +#include "net/spdy/hpack_output_stream.h" #include "net/spdy/mock_spdy_framer_visitor.h" #include "net/spdy/spdy_frame_builder.h" #include "net/spdy/spdy_framer.h" @@ -21,6 +22,8 @@ using std::string; using std::max; using std::min; using std::numeric_limits; +using testing::ElementsAre; +using testing::Pair; using testing::_; namespace net { @@ -190,13 +193,7 @@ class SpdyFramerTestUtil { } virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE { - SpdyFramer framer(version_); - framer.set_enable_compression(false); - SpdyContinuationIR continuation(stream_id); - scoped_ptr<SpdyFrame> frame(framer.SerializeContinuation(continuation)); - ResetBuffer(); - memcpy(buffer_.get(), frame->data(), framer.GetContinuationMinimumSize()); - size_ += framer.GetContinuationMinimumSize(); + LOG(FATAL); } char* ReleaseBuffer() { @@ -305,6 +302,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, if (len == 0) { ++zero_length_control_frame_header_data_count_; // Indicates end-of-header-block. + headers_.clear(); CHECK(header_buffer_valid_); size_t parsed_length = framer_.ParseHeaderBlockInBuffer( header_buffer_.get(), header_buffer_length_, &headers_); @@ -612,19 +610,19 @@ TEST_P(SpdyFramerTest, HeaderBlockInBuffer) { syn_stream.set_priority(1); syn_stream.SetHeader("alpha", "beta"); syn_stream.SetHeader("gamma", "charlie"); + syn_stream.SetHeader("cookie", "key1=value1; key2=value2"); scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream)); EXPECT_TRUE(frame.get() != NULL); - base::StringPiece serialized_headers = - GetSerializedHeaders(frame.get(), framer); - SpdyHeaderBlock new_headers; - EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(), - serialized_headers.size(), - &new_headers)); - SpdyHeaderBlock headers = syn_stream.name_value_block(); - EXPECT_EQ(headers.size(), new_headers.size()); - EXPECT_EQ(headers["alpha"], new_headers["alpha"]); - EXPECT_EQ(headers["gamma"], new_headers["gamma"]); + TestSpdyVisitor visitor(spdy_version_); + visitor.use_compression_ = false; + visitor.SimulateInFramer( + reinterpret_cast<unsigned char*>(frame->data()), + frame->size()); + + EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); + EXPECT_TRUE(CompareHeaderBlocks(&syn_stream.name_value_block(), + &visitor.headers_)); } // Test that if there's not a full frame, we fail to parse it. @@ -640,12 +638,14 @@ TEST_P(SpdyFramerTest, UndersizedHeaderBlockInBuffer) { scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream)); EXPECT_TRUE(frame.get() != NULL); - base::StringPiece serialized_headers = - GetSerializedHeaders(frame.get(), framer); - SpdyHeaderBlock new_headers; - EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(), - serialized_headers.size() - 2, - &new_headers)); + TestSpdyVisitor visitor(spdy_version_); + visitor.use_compression_ = false; + visitor.SimulateInFramer( + reinterpret_cast<unsigned char*>(frame->data()), + frame->size() - 2); + + EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); + EXPECT_EQ(0u, visitor.headers_.size()); } // Test that if we receive a SYN_REPLY with stream ID zero, we signal an error @@ -739,6 +739,12 @@ TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) { } TEST_P(SpdyFramerTest, DuplicateHeader) { + if (spdy_version_ >= 4) { + // TODO(jgraettinger): Punting on this because we haven't determined + // whether duplicate HPACK headers other than Cookie are an error. + // If they are, this will need to be updated to use HpackOutputStream. + return; + } SpdyFramer framer(spdy_version_); // Frame builder with plentiful buffer size. SpdyFrameBuilder frame(1024); @@ -789,15 +795,24 @@ TEST_P(SpdyFramerTest, MultiValueHeader) { frame.WriteUInt32(0); // associated stream id frame.WriteUInt16(0); // Priority. } else { - frame.WriteFramePrefix(framer, HEADERS, HEADERS_FLAG_PRIORITY, 3); + frame.WriteFramePrefix(framer, + HEADERS, + HEADERS_FLAG_PRIORITY | HEADERS_FLAG_END_HEADERS, + 3); frame.WriteUInt32(framer.GetHighestPriority()); } - string value("value1\0value2"); + string value("value1\0value2", 13); if (IsSpdy2()) { frame.WriteUInt16(1); // Number of headers. frame.WriteString("name"); frame.WriteString(value); + } else if (spdy_version_ >= 4) { + HpackOutputStream output_stream(1024); + output_stream.AppendLiteralHeaderNoIndexingWithName("name", value); + string buffer; + output_stream.TakeString(&buffer); + frame.WriteBytes(&buffer[0], buffer.size()); } else { frame.WriteUInt32(1); // Number of headers. frame.WriteStringPiece32("name"); @@ -806,19 +821,24 @@ TEST_P(SpdyFramerTest, MultiValueHeader) { // write the length frame.RewriteLength(framer); - SpdyHeaderBlock new_headers; framer.set_enable_compression(false); scoped_ptr<SpdyFrame> control_frame(frame.take()); - base::StringPiece serialized_headers = - GetSerializedHeaders(control_frame.get(), framer); - EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(), - serialized_headers.size(), - &new_headers)); - EXPECT_TRUE(new_headers.find("name") != new_headers.end()); - EXPECT_EQ(value, new_headers.find("name")->second); + + TestSpdyVisitor visitor(spdy_version_); + visitor.use_compression_ = false; + visitor.SimulateInFramer( + reinterpret_cast<unsigned char*>(control_frame->data()), + control_frame->size()); + + EXPECT_THAT(visitor.headers_, ElementsAre( + Pair("name", value))); } TEST_P(SpdyFramerTest, BasicCompression) { + if (spdy_version_ >= 4) { + // Deflate compression doesn't apply to HPACK. + return; + } scoped_ptr<TestSpdyVisitor> visitor(new TestSpdyVisitor(spdy_version_)); SpdyFramer framer(spdy_version_); framer.set_debug_visitor(visitor.get()); @@ -1033,23 +1053,14 @@ TEST_P(SpdyFramerTest, Basic) { // SYN_STREAM doesn't exist in SPDY4, so instead we send // HEADERS frames with PRIORITY and END_HEADERS set. const unsigned char kV4Input[] = { - 0x00, 0x1c, 0x08, 0x0c, // SYN_STREAM #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'h', 'h', 0x00, 0x00, - 0x00, 0x02, 'v', 'v', + 0x00, 0x0d, 0x08, 0x0c, // HEADERS: PRIORITY | END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x00, 0x00, 0x00, // Priority 0 + 0x82, // :method: GET - 0x00, 0x24, 0x08, 0x04, // HEADERS on Stream #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x02, - 'h', '2', 0x00, 0x00, - 0x00, 0x02, 'v', '2', - 0x00, 0x00, 0x00, 0x02, - 'h', '3', 0x00, 0x00, - 0x00, 0x02, 'v', '3', + 0x00, 0x09, 0x08, 0x04, // HEADERS: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x8c, // :status: 200 0x00, 0x14, 0x00, 0x00, // DATA on Stream #1 0x00, 0x00, 0x00, 0x01, @@ -1057,10 +1068,10 @@ TEST_P(SpdyFramerTest, Basic) { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x10, 0x08, 0x0c, // SYN Stream #3 - 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0d, 0x08, 0x0c, // HEADERS: PRIORITY | END_HEADERS + 0x00, 0x00, 0x00, 0x03, // Stream 3 + 0x00, 0x00, 0x00, 0x00, // Priority 0 + 0x82, // :method: GET 0x00, 0x10, 0x00, 0x00, // DATA on Stream #3 0x00, 0x00, 0x00, 0x03, @@ -1177,20 +1188,14 @@ TEST_P(SpdyFramerTest, FinOnDataFrame) { // SYN_STREAM and SYN_REPLY don't exist in SPDY4, so instead we send // HEADERS frames with PRIORITY(SYN_STREAM only) and END_HEADERS set. const unsigned char kV4Input[] = { - 0x00, 0x1c, 0x08, 0x0c, // SYN_STREAM #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'h', 'h', 0x00, 0x00, - 0x00, 0x02, 'v', 'v', + 0x00, 0x0d, 0x08, 0x0c, // HEADERS: PRIORITY | END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x00, 0x00, 0x00, // Priority 0 + 0x82, // :method: GET - 0x00, 0x18, 0x08, 0x04, // SYN REPLY Stream #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'a', 'a', 0x00, 0x00, - 0x00, 0x02, 'b', 'b', + 0x00, 0x09, 0x08, 0x04, // HEADERS: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x8c, // :status: 200 0x00, 0x14, 0x00, 0x00, // DATA on Stream #1 0x00, 0x00, 0x00, 0x01, @@ -1269,20 +1274,14 @@ TEST_P(SpdyFramerTest, FinOnSynReplyFrame) { // SYN_STREAM and SYN_REPLY don't exist in SPDY4, so instead we send // HEADERS frames with PRIORITY(SYN_STREAM only) and END_HEADERS set. const unsigned char kV4Input[] = { - 0x00, 0x1c, 0x08, 0x0c, // SYN_STREAM #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'h', 'h', 0x00, 0x00, - 0x00, 0x02, 'v', 'v', - - 0x00, 0x18, 0x08, 0x05, // SYN_REPLY #1, with FIN - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'a', 'a', 0x00, 0x00, - 0x00, 0x02, 'b', 'b', + 0x00, 0x0d, 0x08, 0x0c, // HEADERS: PRIORITY | END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x00, 0x00, 0x00, // Priority 0 + 0x82, // :method: GET + + 0x00, 0x09, 0x08, 0x05, // HEADERS: FIN | END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x8c, // :status: 200 }; TestSpdyVisitor visitor(spdy_version_); @@ -1311,6 +1310,10 @@ TEST_P(SpdyFramerTest, FinOnSynReplyFrame) { } TEST_P(SpdyFramerTest, HeaderCompression) { + if (spdy_version_ >= 4) { + // Deflate compression doesn't apply to HPACK. + return; + } SpdyFramer send_framer(spdy_version_); SpdyFramer recv_framer(spdy_version_); @@ -1687,17 +1690,14 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { 'a', 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x2c, 0x08, 0x0c, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x03, - 'b', 'a', 'r', 0x00, - 0x00, 0x00, 0x03, 'f', - 'o', 'o', 0x00, 0x00, - 0x00, 0x03, 'f', 'o', - 'o', 0x00, 0x00, 0x00, - 0x03, 'b', 'a', 'r' + 0x00, 0x1e, 0x08, 0x0c, // HEADERS: PRIORITY | END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x00, 0x00, 0x07, // Priority 7 + 0x40, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x40, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x03, 0x62, // oo.b + 0x61, 0x72, // ar }; SpdySynStreamIR syn_stream(1); syn_stream.set_priority(framer.GetLowestPriority()); @@ -1745,17 +1745,13 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { 'b', 'a', 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x29, 0x08, 0x0d, - 0x7f, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, - 'f', 'o', 'o', 0x00, - 0x00, 0x00, 0x03, 'f', - 'o', 'o', 0x00, 0x00, - 0x00, 0x03, 'b', 'a', - 'r' + 0x00, 0x1b, 0x08, 0x0d, // HEADERS: PRIORITY | FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x00, 0x00, 0x00, 0x00, // Priority 0 + 0x40, 0x00, 0x03, 0x66, // @..f + 0x6f, 0x6f, 0x40, 0x03, // oo@. + 0x66, 0x6f, 0x6f, 0x03, // foo. + 0x62, 0x61, 0x72, // bar }; SpdySynStreamIR syn_stream(0x7fffffff); syn_stream.set_associated_to_stream_id(0x7fffffff); @@ -1806,17 +1802,13 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { 0x00, 0x00, 0x00 }; const unsigned char kV4FrameData[] = { - 0x00, 0x29, 0x08, 0x0d, - 0x7f, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x03, - 'b', 'a', 'r', 0x00, - 0x00, 0x00, 0x03, 'f', - 'o', 'o', 0x00, 0x00, - 0x00, 0x03, 'f', 'o', - 'o', 0x00, 0x00, 0x00, - 0x00 + 0x00, 0x1b, 0x08, 0x0d, // HEADERS: PRIORITY | FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x00, 0x00, 0x00, 0x01, // Priority 1 + 0x40, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x40, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x00, // oo. }; SpdySynStreamIR syn_stream(0x7fffffff); syn_stream.set_associated_to_stream_id(0x7fffffff); @@ -1883,23 +1875,6 @@ TEST_P(SpdyFramerTest, CreateSynStreamCompressed) { 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, }; - const unsigned char kV4FrameData[] = { - 0x00, 0x39, 0x08, 0x0c, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x04, - 0x38, 0xea, 0xe3, 0xc6, - 0xa7, 0xc2, 0x02, 0xe5, - 0x0e, 0x50, 0xc2, 0x4b, - 0x4a, 0x04, 0xe5, 0x0b, - 0x66, 0x80, 0x00, 0x4a, - 0xcb, 0xcf, 0x07, 0x08, - 0x20, 0x10, 0x95, 0x96, - 0x9f, 0x0f, 0xa2, 0x00, - 0x02, 0x28, 0x29, 0xb1, - 0x08, 0x20, 0x80, 0x00, - 0x00, 0x00, 0x00, 0xff, - 0xff, - }; SpdySynStreamIR syn_stream(1); syn_stream.set_priority(priority); syn_stream.SetHeader("bar", "foo"); @@ -1910,7 +1885,7 @@ TEST_P(SpdyFramerTest, CreateSynStreamCompressed) { } else if (IsSpdy3()) { CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); } else { - CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + // Deflate compression doesn't apply to HPACK. } } } @@ -1948,16 +1923,13 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { 0x03, 'b', 'a', 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x28, 0x08, 0x04, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x03, - 'b', 'a', 'r', 0x00, - 0x00, 0x00, 0x03, 'f', - 'o', 'o', 0x00, 0x00, - 0x00, 0x03, 'f', 'o', - 'o', 0x00, 0x00, 0x00, - 0x03, 'b', 'a', 'r' + 0x00, 0x1a, 0x08, 0x04, // HEADER: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x40, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x03, 0x62, // oo.b + 0x61, 0x72, // ar }; SpdySynReplyIR syn_reply(1); syn_reply.SetHeader("bar", "foo"); @@ -2001,16 +1973,12 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x25, 0x08, 0x05, - 0x7f, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, - 'f', 'o', 'o', 0x00, - 0x00, 0x00, 0x03, 'f', - 'o', 'o', 0x00, 0x00, - 0x00, 0x03, 'b', 'a', - 'r' + 0x00, 0x17, 0x08, 0x05, // HEADER: FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x40, 0x00, 0x03, 0x66, // @..f + 0x6f, 0x6f, 0x40, 0x03, // oo@. + 0x66, 0x6f, 0x6f, 0x03, // foo. + 0x62, 0x61, 0x72, // bar }; SpdySynReplyIR syn_reply(0x7fffffff); syn_reply.set_fin(true); @@ -2055,16 +2023,12 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { 0x00 }; const unsigned char kV4FrameData[] = { - 0x00, 0x25, 0x08, 0x05, - 0x7f, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x03, - 'b', 'a', 'r', 0x00, - 0x00, 0x00, 0x03, 'f', - 'o', 'o', 0x00, 0x00, - 0x00, 0x03, 'f', 'o', - 'o', 0x00, 0x00, 0x00, - 0x00 + 0x00, 0x17, 0x08, 0x05, // HEADER: FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x40, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x40, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x00, // oo. }; SpdySynReplyIR syn_reply(0x7fffffff); syn_reply.set_fin(true); @@ -2125,22 +2089,6 @@ TEST_P(SpdyFramerTest, CreateSynReplyCompressed) { 0x00, 0x00, 0x00, 0xff, 0xff, }; - const unsigned char kV4FrameData[] = { - 0x00, 0x35, 0x08, 0x04, - 0x00, 0x00, 0x00, 0x01, - 0x38, 0xea, 0xe3, 0xc6, - 0xa7, 0xc2, 0x02, 0xe5, - 0x0e, 0x50, 0xc2, 0x4b, - 0x4a, 0x04, 0xe5, 0x0b, - 0x66, 0x80, 0x00, 0x4a, - 0xcb, 0xcf, 0x07, 0x08, - 0x20, 0x10, 0x95, 0x96, - 0x9f, 0x0f, 0xa2, 0x00, - 0x02, 0x28, 0x29, 0xb1, - 0x08, 0x20, 0x80, 0x00, - 0x00, 0x00, 0x00, 0xff, - 0xff, - }; SpdySynReplyIR syn_reply(1); syn_reply.SetHeader("bar", "foo"); syn_reply.SetHeader("foo", "bar"); @@ -2150,7 +2098,7 @@ TEST_P(SpdyFramerTest, CreateSynReplyCompressed) { } else if (IsSpdy3()) { CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); } else { - CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + // Deflate compression doesn't apply to HPACK. } } } @@ -2515,16 +2463,13 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { 0x03, 'b', 'a', 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x28, 0x08, 0x04, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x03, - 'b', 'a', 'r', 0x00, - 0x00, 0x00, 0x03, 'f', - 'o', 'o', 0x00, 0x00, - 0x00, 0x03, 'f', 'o', - 'o', 0x00, 0x00, 0x00, - 0x03, 'b', 'a', 'r' + 0x00, 0x1a, 0x08, 0x04, // Headers: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x40, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x03, 0x62, // oo.b + 0x61, 0x72, // ar }; SpdyHeadersIR headers_ir(1); headers_ir.SetHeader("bar", "foo"); @@ -2568,16 +2513,12 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { 'r' }; const unsigned char kV4FrameData[] = { - 0x00, 0x25, 0x08, 0x05, - 0x7f, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, - 'f', 'o', 'o', 0x00, - 0x00, 0x00, 0x03, 'f', - 'o', 'o', 0x00, 0x00, - 0x00, 0x03, 'b', 'a', - 'r' + 0x00, 0x17, 0x08, 0x05, // HEADER: FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x40, 0x00, 0x03, 0x66, // @..f + 0x6f, 0x6f, 0x40, 0x03, // oo@. + 0x66, 0x6f, 0x6f, 0x03, // foo. + 0x62, 0x61, 0x72, // bar }; SpdyHeadersIR headers_ir(0x7fffffff); headers_ir.set_fin(true); @@ -2622,16 +2563,12 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { 0x00 }; const unsigned char kV4FrameData[] = { - 0x00, 0x25, 0x08, 0x05, - 0x7f, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x03, - 'b', 'a', 'r', 0x00, - 0x00, 0x00, 0x03, 'f', - 'o', 'o', 0x00, 0x00, - 0x00, 0x03, 'f', 'o', - 'o', 0x00, 0x00, 0x00, - 0x00 + 0x00, 0x17, 0x08, 0x05, // HEADER: FIN | END_HEADERS + 0x7f, 0xff, 0xff, 0xff, // Stream 0x7fffffff + 0x40, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x40, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x00, // oo. }; SpdyHeadersIR headers_ir(0x7fffffff); headers_ir.set_fin(true); @@ -2692,22 +2629,6 @@ TEST_P(SpdyFramerTest, CreateHeadersCompressed) { 0x00, 0x00, 0x00, 0xff, 0xff, }; - const unsigned char kV4FrameData[] = { - 0x00, 0x35, 0x08, 0x04, - 0x00, 0x00, 0x00, 0x01, - 0x38, 0xea, 0xe3, 0xc6, - 0xa7, 0xc2, 0x02, 0xe5, - 0x0e, 0x50, 0xc2, 0x4b, - 0x4a, 0x04, 0xe5, 0x0b, - 0x66, 0x80, 0x00, 0x4a, - 0xcb, 0xcf, 0x07, 0x08, - 0x20, 0x10, 0x95, 0x96, - 0x9f, 0x0f, 0xa2, 0x00, - 0x02, 0x28, 0x29, 0xb1, - 0x08, 0x20, 0x80, 0x00, - 0x00, 0x00, 0x00, 0xff, - 0xff - }; SpdyHeadersIR headers_ir(1); headers_ir.SetHeader("bar", "foo"); headers_ir.SetHeader("foo", "bar"); @@ -2717,7 +2638,7 @@ TEST_P(SpdyFramerTest, CreateHeadersCompressed) { } else if (IsSpdy3()) { CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); } else { - CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + // Deflate compression doesn't apply to HPACK. } } } @@ -2829,64 +2750,24 @@ TEST_P(SpdyFramerTest, CreateBlocked) { CompareFrames(kDescription, *frame_serialized, *frame_created); } -TEST_P(SpdyFramerTest, CreatePushPromiseUncompressed) { +TEST_P(SpdyFramerTest, CreatePushPromise) { if (spdy_version_ < SPDY4) { return; } SpdyFramer framer(spdy_version_); - framer.set_enable_compression(false); const char kDescription[] = "PUSH_PROMISE frame"; const unsigned char kFrameData[] = { - 0x00, 0x2C, 0x0C, 0x04, // length = 44, type = 12, flags = 0 - 0x00, 0x00, 0x00, 0x2A, // stream id = 42 - 0x00, 0x00, 0x00, 0x39, // promised stream id = 57 - 0x00, 0x00, 0x00, 0x02, // start of uncompressed header block - 0x00, 0x00, 0x00, 0x03, - 'b', 'a', 'r', 0x00, - 0x00, 0x00, 0x03, 'f', - 'o', 'o', 0x00, 0x00, - 0x00, 0x03, 'f', 'o', - 'o', 0x00, 0x00, 0x00, - 0x03, 'b', 'a', 'r' // end of uncompressed header block - }; - - SpdyPushPromiseIR push_promise(42, 57); - push_promise.SetHeader("bar", "foo"); - push_promise.SetHeader("foo", "bar"); - scoped_ptr<SpdySerializedFrame> frame( - framer.SerializePushPromise(push_promise)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); -} - -TEST_P(SpdyFramerTest, CreatePushPromiseCompressed) { - if (spdy_version_ < SPDY4) { - return; - } - - SpdyFramer framer(spdy_version_); - framer.set_enable_compression(true); - - const char kDescription[] = "PUSH_PROMISE frame"; - - const unsigned char kFrameData[] = { - 0x00, 0x39, 0x0C, 0x04, // length = 57, type = 12, flags = 0 - 0x00, 0x00, 0x00, 0x2A, // stream id = 42 - 0x00, 0x00, 0x00, 0x39, // promised stream id = 57 - 0x38, 0xea, 0xe3, 0xc6, // start of compressed header block - 0xa7, 0xc2, 0x02, 0xe5, - 0x0e, 0x50, 0xc2, 0x4b, - 0x4a, 0x04, 0xe5, 0x0b, - 0x66, 0x80, 0x00, 0x4a, - 0xcb, 0xcf, 0x07, 0x08, - 0x20, 0x10, 0x95, 0x96, - 0x9f, 0x0f, 0xa2, 0x00, - 0x02, 0x28, 0x29, 0xb1, - 0x08, 0x20, 0x80, 0x00, - 0x00, 0x00, 0x00, 0xff, - 0xff // end of compressed header block + 0x00, 0x1e, 0x0c, 0x04, // PUSH_PROMISE: END_HEADERS + 0x00, 0x00, 0x00, 0x2a, // Stream 42 + 0x00, 0x00, 0x00, 0x39, // Promised stream 57 + 0x40, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x40, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x03, 0x62, // oo.b + 0x61, 0x72, // ar }; SpdyPushPromiseIR push_promise(42, 57); @@ -2988,6 +2869,10 @@ TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) { } TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) { + if (spdy_version_ >= 4) { + // TODO(jgraettinger): This test setup doesn't work with HPACK. + return; + } // First find the size of the header value in order to just reach the control // frame max size. SpdyFramer framer(spdy_version_); @@ -2999,7 +2884,7 @@ TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) { const size_t kBigValueSize = framer.GetControlFrameBufferMaxSize() - control_frame->size(); - // Create a frame at exatly that size. + // Create a frame at exactly that size. string big_value(kBigValueSize, 'x'); syn_stream.SetHeader("aa", big_value.c_str()); control_frame.reset(framer.SerializeSynStream(syn_stream)); @@ -3019,6 +2904,10 @@ TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) { } TEST_P(SpdyFramerTest, ControlFrameTooLarge) { + if (spdy_version_ >= 4) { + // TODO(jgraettinger): This test setup doesn't work with HPACK. + return; + } // First find the size of the header value in order to just reach the control // frame max size. SpdyFramer framer(spdy_version_); @@ -3095,6 +2984,10 @@ TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) { } TEST_P(SpdyFramerTest, DecompressCorruptHeaderBlock) { + if (spdy_version_ >= 4) { + // Deflate compression doesn't apply to HPACK. + return; + } SpdyFramer framer(spdy_version_); framer.set_enable_compression(false); // Construct a SYN_STREAM control frame without compressing the header block, @@ -3471,26 +3364,23 @@ TEST_P(SpdyFramerTest, ReadCredentialFrameFollowedByAnotherFrame) { EXPECT_EQ(2u, visitor.last_window_update_delta_); } -TEST_P(SpdyFramerTest, CreateContinuationUncompressed) { +TEST_P(SpdyFramerTest, CreateContinuation) { if (spdy_version_ < SPDY4) { return; } SpdyFramer framer(spdy_version_); - framer.set_enable_compression(false); const char kDescription[] = "CONTINUATION frame"; const unsigned char kFrameData[] = { - 0x00, 0x24, 0x0D, 0x00, // length = 40, type = 13, flags = none - 0x00, 0x00, 0x00, 0x2A, // stream id = 42 - 0x00, 0x00, 0x00, 0x03, // uncompressed header block fragment - 'b', 'a', 'r', 0x00, - 0x00, 0x00, 0x03, 'f', - 'o', 'o', 0x00, 0x00, - 0x00, 0x03, 'f', 'o', - 'o', 0x00, 0x00, 0x00, - 0x03, 'b', 'a', 'r' // end of uncompressed header block + 0x00, 0x1a, 0x0d, 0x00, // CONTINUATION + 0x00, 0x00, 0x00, 0x2a, // Stream 42 + 0x40, 0x03, 0x62, 0x61, // @.ba + 0x72, 0x03, 0x66, 0x6f, // r.fo + 0x6f, 0x40, 0x03, 0x66, // o@.f + 0x6f, 0x6f, 0x03, 0x62, // oo.b + 0x61, 0x72, // ar }; SpdyContinuationIR continuation(42); @@ -3501,40 +3391,6 @@ TEST_P(SpdyFramerTest, CreateContinuationUncompressed) { CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); } -TEST_P(SpdyFramerTest, CreateContinuationCompressed) { - if (spdy_version_ < SPDY4) { - return; - } - - SpdyFramer framer(spdy_version_); - framer.set_enable_compression(true); - - const char kDescription[] = "CONTINUATION frame"; - - const unsigned char kFrameData[] = { - 0x00, 0x35, 0x0d, 0x00, - 0x00, 0x00, 0x00, 0x2a, - 0x38, 0xea, 0xe3, 0xc6, - 0xa7, 0xc2, 0x02, 0xe5, - 0x0e, 0x50, 0xc2, 0x4b, - 0x4a, 0x04, 0xe5, 0x0b, - 0x66, 0x80, 0x00, 0x4a, - 0xcb, 0xcf, 0x07, 0x08, - 0x20, 0x10, 0x95, 0x96, - 0x9f, 0x0f, 0xa2, 0x00, - 0x02, 0x28, 0x29, 0xb1, - 0x08, 0x20, 0x80, 0x00, - 0x00, 0x00, 0x00, 0xff, - 0xff - }; - SpdyContinuationIR continuation(42); - continuation.SetHeader("bar", "foo"); - continuation.SetHeader("foo", "bar"); - scoped_ptr<SpdySerializedFrame> frame( - framer.SerializeContinuation(continuation)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); -} - TEST_P(SpdyFramerTest, ReadCompressedPushPromise) { if (spdy_version_ < 4) { return; @@ -3564,36 +3420,30 @@ TEST_P(SpdyFramerTest, ReadHeadersWithContinuation) { } const unsigned char kInput[] = { - 0x00, 0x14, 0x08, 0x00, // HEADERS with incomplete header block #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x02, - 'h', 'h', 0x00, 0x00, - - 0x00, 0x23, 0x0D, 0x00, // CONTINUATION on Stream #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x02, 'v', 'v', - 0x00, 0x00, 0x00, 0x02, - 'h', '2', 0x00, 0x00, - 0x00, 0x02, 'v', '2', - 0x00, 0x00, 0x00, 0x02, - 'h', '3', 0x00, 0x00, - 0x00, 0x02, 'v', - - 0x00, 0x15, 0x0D, 0x04, // CONTINUATION on Stream #1 - 0x00, 0x00, 0x00, 0x01, - '3', 0x00, 0x00, 0x00, - 0x02, 'h', '4', 0x00, - 0x00, 0x00, 0x02, 'v', - '4', + 0x00, 0x18, 0x08, 0x00, // HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x06, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + + 0x00, 0x1c, 0x0D, 0x00, // CONTINUATION + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x06, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x08, 0x62, 0x61, 0x7a, + 0x3d, 0x62, 0x69, 0x6e, + 0x67, 0x40, 0x06, 0x43, + + 0x00, 0x1a, 0x0D, 0x04, // CONTINUATION: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x00, 0x40, 0x04, + 0x6e, 0x61, 0x6d, 0x65, + 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, }; - SpdyHeaderBlock headers; - headers["hh"] = "vv"; - headers["h2"] = "v2"; - headers["h3"] = "v3"; - headers["h4"] = "v4"; - SpdyFramer framer(spdy_version_); TestSpdyVisitor visitor(spdy_version_); visitor.SimulateInFramer(kInput, sizeof(kInput)); @@ -3602,8 +3452,11 @@ TEST_P(SpdyFramerTest, ReadHeadersWithContinuation) { EXPECT_EQ(1, visitor.headers_frame_count_); EXPECT_EQ(2, visitor.continuation_count_); EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); - EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); EXPECT_EQ(0, visitor.zero_length_data_frame_count_); + + EXPECT_THAT(visitor.headers_, ElementsAre( + Pair("Cookie", "foo=bar; baz=bing; "), + Pair("name", "value"))); } TEST_P(SpdyFramerTest, ReadHeadersWithContinuationAndFin) { @@ -3612,35 +3465,44 @@ TEST_P(SpdyFramerTest, ReadHeadersWithContinuationAndFin) { } const unsigned char kInput[] = { - 0x00, 0x18, 0x08, 0x01, // HEADERS on Stream #1, with FIN - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x02, - 'h', 'h', 0x00, 0x00, - 0x00, 0x02, 'v', 'v', - - 0x00, 0x14, 0x0D, 0x04, // CONTINUATION on Stream #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'h', '2', 0x00, 0x00, - 0x00, 0x02, 'v', '2', + 0x00, 0x18, 0x08, 0x01, // HEADERS: FIN + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x06, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + + 0x00, 0x1c, 0x0D, 0x00, // CONTINUATION + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x06, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x08, 0x62, 0x61, 0x7a, + 0x3d, 0x62, 0x69, 0x6e, + 0x67, 0x40, 0x06, 0x43, + + 0x00, 0x1a, 0x0D, 0x04, // CONTINUATION: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x00, 0x40, 0x04, + 0x6e, 0x61, 0x6d, 0x65, + 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, }; - SpdyHeaderBlock headers; - headers["hh"] = "vv"; - headers["h2"] = "v2"; - SpdyFramer framer(spdy_version_); TestSpdyVisitor visitor(spdy_version_); visitor.SimulateInFramer(kInput, sizeof(kInput)); EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(1, visitor.headers_frame_count_); - EXPECT_EQ(1, visitor.continuation_count_); + EXPECT_EQ(2, visitor.continuation_count_); EXPECT_EQ(1, visitor.fin_flag_count_); EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); - EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); EXPECT_EQ(1, visitor.zero_length_data_frame_count_); + + EXPECT_THAT(visitor.headers_, ElementsAre( + Pair("Cookie", "foo=bar; baz=bing; "), + Pair("name", "value"))); } TEST_P(SpdyFramerTest, ReadPushPromiseWithContinuation) { @@ -3649,25 +3511,31 @@ TEST_P(SpdyFramerTest, ReadPushPromiseWithContinuation) { } const unsigned char kInput[] = { - 0x00, 0x1C, 0x0C, 0x00, // PUSH_PROMISE on Stream #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x2A, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x02, - 'h', 'h', 0x00, 0x00, - 0x00, 0x02, 'v', 'v', - - 0x00, 0x14, 0x0D, 0x04, // CONTINUATION on Stream #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'h', '2', 0x00, 0x00, - 0x00, 0x02, 'v', '2', + 0x00, 0x1c, 0x0C, 0x00, // PUSH_PROMISE + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x00, 0x00, 0x2A, // Promised stream 42 + 0x40, 0x06, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + + 0x00, 0x1c, 0x0D, 0x00, // CONTINUATION + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x06, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x08, 0x62, 0x61, 0x7a, + 0x3d, 0x62, 0x69, 0x6e, + 0x67, 0x40, 0x06, 0x43, + + 0x00, 0x1a, 0x0D, 0x04, // CONTINUATION: END_HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x00, 0x40, 0x04, + 0x6e, 0x61, 0x6d, 0x65, + 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, }; - SpdyHeaderBlock headers; - headers["hh"] = "vv"; - headers["h2"] = "v2"; - SpdyFramer framer(spdy_version_); TestSpdyVisitor visitor(spdy_version_); visitor.SimulateInFramer(kInput, sizeof(kInput)); @@ -3675,10 +3543,13 @@ TEST_P(SpdyFramerTest, ReadPushPromiseWithContinuation) { EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(1u, visitor.last_push_promise_stream_); EXPECT_EQ(42u, visitor.last_push_promise_promised_stream_); - EXPECT_EQ(1, visitor.continuation_count_); + EXPECT_EQ(2, visitor.continuation_count_); EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); - EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); EXPECT_EQ(0, visitor.zero_length_data_frame_count_); + + EXPECT_THAT(visitor.headers_, ElementsAre( + Pair("Cookie", "foo=bar; baz=bing; "), + Pair("name", "value"))); } TEST_P(SpdyFramerTest, ReadContinuationWithWrongStreamId) { @@ -3687,18 +3558,20 @@ TEST_P(SpdyFramerTest, ReadContinuationWithWrongStreamId) { } const unsigned char kInput[] = { - 0x00, 0x18, 0x08, 0x00, // HEADERS on Stream #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x02, - 'h', 'h', 0x00, 0x00, - 0x00, 0x02, 'v', 'v', - - 0x00, 0x14, 0x0D, 0x04, // CONTINUATION on Stream #2 - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x02, - 'h', '2', 0x00, 0x00, - 0x00, 0x02, 'v', '2', + 0x00, 0x18, 0x08, 0x00, // HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x06, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + + 0x00, 0x1c, 0x0D, 0x00, // CONTINUATION + 0x00, 0x00, 0x00, 0x02, // Stream 2 + 0x40, 0x06, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x08, 0x62, 0x61, 0x7a, + 0x3d, 0x62, 0x69, 0x6e, + 0x67, 0x40, 0x06, 0x43, }; SpdyFramer framer(spdy_version_); @@ -3712,7 +3585,7 @@ TEST_P(SpdyFramerTest, ReadContinuationWithWrongStreamId) { << SpdyFramer::ErrorCodeToString(framer.error_code()); EXPECT_EQ(1, visitor.headers_frame_count_); EXPECT_EQ(0, visitor.continuation_count_); - EXPECT_EQ(16u, visitor.header_buffer_length_); + EXPECT_EQ(0u, visitor.header_buffer_length_); } TEST_P(SpdyFramerTest, ReadContinuationOutOfOrder) { @@ -3721,11 +3594,12 @@ TEST_P(SpdyFramerTest, ReadContinuationOutOfOrder) { } const unsigned char kInput[] = { - 0x00, 0x14, 0x0D, 0x04, // CONTINUATION - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'h', '2', 0x00, 0x00, - 0x00, 0x02, 'v', '2', + 0x00, 0x18, 0x0D, 0x00, // CONTINUATION + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x06, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, }; SpdyFramer framer(spdy_version_); @@ -3747,14 +3621,14 @@ TEST_P(SpdyFramerTest, ExpectContinuationReceiveData) { } const unsigned char kInput[] = { - 0x00, 0x18, 0x08, 0x00, // HEADERS on Stream #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'h', 'h', 0x00, 0x00, - 0x00, 0x02, 'v', 'v', - - 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1 + 0x00, 0x18, 0x08, 0x00, // HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x06, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + + 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1 0x00, 0x00, 0x00, 0x04, 0xde, 0xad, 0xbe, 0xef, }; @@ -3770,7 +3644,7 @@ TEST_P(SpdyFramerTest, ExpectContinuationReceiveData) { << SpdyFramer::ErrorCodeToString(framer.error_code()); EXPECT_EQ(1, visitor.headers_frame_count_); EXPECT_EQ(0, visitor.continuation_count_); - EXPECT_EQ(16u, visitor.header_buffer_length_); + EXPECT_EQ(0u, visitor.header_buffer_length_); EXPECT_EQ(0, visitor.data_frame_count_); } @@ -3780,19 +3654,20 @@ TEST_P(SpdyFramerTest, ExpectContinuationReceiveControlFrame) { } const unsigned char kInput[] = { - 0x00, 0x18, 0x08, 0x00, // HEADERS on Stream #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'h', 'h', 0x00, 0x00, - 0x00, 0x02, 'v', 'v', - - 0x00, 0x18, 0x08, 0x00, // HEADERS on Stream #1 - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 'h', 'h', 0x00, 0x00, - 0x00, 0x02, 'v', 'v', + 0x00, 0x18, 0x08, 0x00, // HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x06, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, + 0x07, 0x66, 0x6f, 0x6f, + 0x3d, 0x62, 0x61, 0x72, + + 0x00, 0x1c, 0x08, 0x00, // HEADERS + 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x40, 0x06, 0x43, 0x6f, // (Note this is a valid continued encoding). + 0x6f, 0x6b, 0x69, 0x65, + 0x08, 0x62, 0x61, 0x7a, + 0x3d, 0x62, 0x69, 0x6e, + 0x67, 0x40, 0x06, 0x43, }; SpdyFramer framer(spdy_version_); @@ -3806,7 +3681,7 @@ TEST_P(SpdyFramerTest, ExpectContinuationReceiveControlFrame) { << SpdyFramer::ErrorCodeToString(framer.error_code()); EXPECT_EQ(1, visitor.headers_frame_count_); EXPECT_EQ(0, visitor.continuation_count_); - EXPECT_EQ(16u, visitor.header_buffer_length_); + EXPECT_EQ(0u, visitor.header_buffer_length_); EXPECT_EQ(0, visitor.data_frame_count_); } diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index 0fe89ef..a307b42 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -3580,7 +3580,12 @@ TEST_P(SpdyNetworkTransactionTest, InvalidSynReply) { } // Verify that we don't crash on some corrupt frames. +// TODO(jgraettinger): SPDY4 and up treats a header decompression failure as a +// connection error. I'd like to backport this behavior to SPDY3 as well. TEST_P(SpdyNetworkTransactionTest, CorruptFrameSessionError) { + if (spdy_util_.spdy_version() >= SPDY4) { + return; + } // This is the length field that's too short. scoped_ptr<SpdyFrame> syn_reply_wrong_length( spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); @@ -3627,6 +3632,46 @@ TEST_P(SpdyNetworkTransactionTest, CorruptFrameSessionError) { } } +// SPDY4 treats a header decompression failure as a connection-level error. +TEST_P(SpdyNetworkTransactionTest, CorruptFrameSessionErrorSpdy4) { + if (spdy_util_.spdy_version() < SPDY4) { + return; + } + // This is the length field that's too short. + scoped_ptr<SpdyFrame> syn_reply_wrong_length( + spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); + BufferedSpdyFramer framer(spdy_util_.spdy_version(), false); + size_t right_size = + (spdy_util_.spdy_version() < SPDY4) ? + syn_reply_wrong_length->size() - framer.GetControlFrameHeaderSize() : + syn_reply_wrong_length->size(); + size_t wrong_size = right_size - 4; + test::SetFrameLength(syn_reply_wrong_length.get(), + wrong_size, + spdy_util_.spdy_version()); + + // TODO(jgraettinger): SpdySession::OnError() should send a GOAWAY before + // breaking the connection. + scoped_ptr<SpdyFrame> req( + spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true)); + MockWrite writes[] = { + CreateMockWrite(*req), + }; + + scoped_ptr<SpdyFrame> body(spdy_util_.ConstructSpdyBodyFrame(1, true)); + MockRead reads[] = { + MockRead(ASYNC, syn_reply_wrong_length->data(), wrong_size), + }; + + DelayedSocketData data(1, reads, arraysize(reads), + writes, arraysize(writes)); + NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY, + BoundNetLog(), GetParam(), NULL); + helper.RunToCompletion(&data); + TransactionHelperResult out = helper.output(); + EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, out.rv); +} + // Test that we shutdown correctly on write errors. TEST_P(SpdyNetworkTransactionTest, WriteError) { scoped_ptr<SpdyFrame> req( @@ -3689,6 +3734,10 @@ TEST_P(SpdyNetworkTransactionTest, PartialWrite) { // In this test, we enable compression, but get a uncompressed SynReply from // the server. Verify that teardown is all clean. TEST_P(SpdyNetworkTransactionTest, DecompressFailureOnSynReply) { + if (spdy_util_.spdy_version() >= SPDY4) { + // HPACK doesn't use deflate compression. + return; + } scoped_ptr<SpdyFrame> compressed( spdy_util_.ConstructSpdyGet(NULL, 0, true, 1, LOWEST, true)); scoped_ptr<SpdyFrame> rst( diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index a2358e7..095718d 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h @@ -754,6 +754,8 @@ class NET_EXPORT_PRIVATE SpdyPushPromiseIR DISALLOW_COPY_AND_ASSIGN(SpdyPushPromiseIR); }; +// TODO(jgraettinger): This representation needs review. SpdyContinuationIR +// needs to frame a portion of a single, arbitrarily-broken encoded buffer. class NET_EXPORT_PRIVATE SpdyContinuationIR : public SpdyFrameWithNameValueBlockIR { public: diff --git a/net/tools/quic/spdy_utils.cc b/net/tools/quic/spdy_utils.cc index 8d6a236..0aa4638 100644 --- a/net/tools/quic/spdy_utils.cc +++ b/net/tools/quic/spdy_utils.cc @@ -157,7 +157,7 @@ string SpdyUtils::SerializeResponseHeaders( // static string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) { - int length = SpdyFramer::GetSerializedLength(SPDY3, &headers, true); + int length = SpdyFramer::GetSerializedLength(SPDY3, &headers); SpdyFrameBuilder builder(length); SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers); scoped_ptr<SpdyFrame> block(builder.take()); |