summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-19 05:55:42 +0000
committerjgraettinger@chromium.org <jgraettinger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-19 05:55:42 +0000
commite3352df2a6cd878f3db12f405bcacbce4c0dee6e (patch)
tree51eb2ecbbfae4d37e08f9989068467c0dcf77023
parentc4432939ffbacd0b7aedded5ecb604a366c2310e (diff)
downloadchromium_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.cc2
-rw-r--r--net/spdy/hpack_encoding_context.cc7
-rw-r--r--net/spdy/hpack_huffman_table.cc7
-rw-r--r--net/spdy/spdy_framer.cc215
-rw-r--r--net/spdy/spdy_framer.h32
-rw-r--r--net/spdy/spdy_framer_test.cc729
-rw-r--r--net/spdy/spdy_network_transaction_unittest.cc49
-rw-r--r--net/spdy/spdy_protocol.h2
-rw-r--r--net/tools/quic/spdy_utils.cc2
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());