Commit 498174e9 authored by bnc's avatar bnc Committed by Commit bot

Implement padding PUSH_PROMISE and HEADERS frames.

This lands server change 77795511 by raullenchai.

BUG=345769

Review URL: https://codereview.chromium.org/661743003

Cr-Commit-Position: refs/heads/master@{#300713}
parent f2afbee8
...@@ -70,6 +70,9 @@ const uint8 kNoFlags = 0; ...@@ -70,6 +70,9 @@ const uint8 kNoFlags = 0;
const size_t kPriorityDependencyPayloadSize = 4; const size_t kPriorityDependencyPayloadSize = 4;
const size_t kPriorityWeightPayloadSize = 1; const size_t kPriorityWeightPayloadSize = 1;
// Wire size of pad length field.
const size_t kPadLengthFieldSize = 1;
} // namespace } // namespace
const SpdyStreamId SpdyFramer::kInvalidStream = static_cast<SpdyStreamId>(-1); const SpdyStreamId SpdyFramer::kInvalidStream = static_cast<SpdyStreamId>(-1);
...@@ -379,8 +382,8 @@ const char* SpdyFramer::StateToString(int state) { ...@@ -379,8 +382,8 @@ const char* SpdyFramer::StateToString(int state) {
return "READING_COMMON_HEADER"; return "READING_COMMON_HEADER";
case SPDY_CONTROL_FRAME_PAYLOAD: case SPDY_CONTROL_FRAME_PAYLOAD:
return "CONTROL_FRAME_PAYLOAD"; return "CONTROL_FRAME_PAYLOAD";
case SPDY_READ_PADDING_LENGTH: case SPDY_READ_DATA_FRAME_PADDING_LENGTH:
return "SPDY_READ_PADDING_LENGTH"; return "SPDY_READ_DATA_FRAME_PADDING_LENGTH";
case SPDY_CONSUME_PADDING: case SPDY_CONSUME_PADDING:
return "SPDY_CONSUME_PADDING"; return "SPDY_CONSUME_PADDING";
case SPDY_IGNORE_REMAINING_PAYLOAD: case SPDY_IGNORE_REMAINING_PAYLOAD:
...@@ -601,8 +604,8 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { ...@@ -601,8 +604,8 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
break; break;
} }
case SPDY_READ_PADDING_LENGTH: { case SPDY_READ_DATA_FRAME_PADDING_LENGTH: {
size_t bytes_read = ProcessFramePaddingLength(data, len); size_t bytes_read = ProcessDataFramePaddingLength(data, len);
len -= bytes_read; len -= bytes_read;
data += bytes_read; data += bytes_read;
break; break;
...@@ -809,7 +812,7 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { ...@@ -809,7 +812,7 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
remaining_data_length_, remaining_data_length_,
current_frame_flags_ & DATA_FLAG_FIN); current_frame_flags_ & DATA_FLAG_FIN);
if (remaining_data_length_ > 0) { if (remaining_data_length_ > 0) {
CHANGE_STATE(SPDY_READ_PADDING_LENGTH); CHANGE_STATE(SPDY_READ_DATA_FRAME_PADDING_LENGTH);
} else { } else {
// Empty data frame. // Empty data frame.
if (current_frame_flags_ & DATA_FLAG_FIN) { if (current_frame_flags_ & DATA_FLAG_FIN) {
...@@ -1083,15 +1086,23 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) { ...@@ -1083,15 +1086,23 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
break; break;
case HEADERS: case HEADERS:
frame_size_without_variable_data = GetHeadersMinimumSize(); frame_size_without_variable_data = GetHeadersMinimumSize();
if (protocol_version() > SPDY3 && if (protocol_version() > SPDY3) {
current_frame_flags_ & HEADERS_FLAG_PRIORITY) { if (current_frame_flags_ & HEADERS_FLAG_PADDED) {
frame_size_without_variable_data += kPadLengthFieldSize;
}
if (current_frame_flags_ & HEADERS_FLAG_PRIORITY) {
frame_size_without_variable_data += frame_size_without_variable_data +=
kPriorityDependencyPayloadSize + kPriorityDependencyPayloadSize +
kPriorityWeightPayloadSize; kPriorityWeightPayloadSize;
} }
}
break; break;
case PUSH_PROMISE: case PUSH_PROMISE:
frame_size_without_variable_data = GetPushPromiseMinimumSize(); frame_size_without_variable_data = GetPushPromiseMinimumSize();
if (protocol_version() > SPDY3 &&
current_frame_flags_ & PUSH_PROMISE_FLAG_PADDED) {
frame_size_without_variable_data += kPadLengthFieldSize;
}
break; break;
case CONTINUATION: case CONTINUATION:
frame_size_without_variable_data = GetContinuationMinimumSize(); frame_size_without_variable_data = GetContinuationMinimumSize();
...@@ -1457,6 +1468,14 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, ...@@ -1457,6 +1468,14 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
expect_continuation_ = current_frame_stream_id_; expect_continuation_ = current_frame_stream_id_;
end_stream_when_done_ = current_frame_flags_ & CONTROL_FLAG_FIN; end_stream_when_done_ = current_frame_flags_ & CONTROL_FLAG_FIN;
} }
if (protocol_version() > SPDY3 &&
current_frame_flags_ & HEADERS_FLAG_PADDED) {
uint8 pad_payload_len = 0;
DCHECK_EQ(remaining_padding_payload_length_, 0u);
successful_read = reader.ReadUInt8(&pad_payload_len);
DCHECK(successful_read);
remaining_padding_payload_length_ = pad_payload_len;
}
const bool has_priority = const bool has_priority =
(current_frame_flags_ & HEADERS_FLAG_PRIORITY) != 0; (current_frame_flags_ & HEADERS_FLAG_PRIORITY) != 0;
uint32 priority = 0; uint32 priority = 0;
...@@ -1502,7 +1521,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, ...@@ -1502,7 +1521,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
expect_continuation_ == 0); expect_continuation_ == 0);
} }
} }
CHANGE_STATE(SPDY_READ_PADDING_LENGTH); CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
break; break;
case PUSH_PROMISE: case PUSH_PROMISE:
{ {
...@@ -1511,6 +1530,17 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, ...@@ -1511,6 +1530,17 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
set_error(SPDY_INVALID_CONTROL_FRAME); set_error(SPDY_INVALID_CONTROL_FRAME);
break; break;
} }
bool successful_read = true;
if (protocol_version() > SPDY3 &&
current_frame_flags_ & PUSH_PROMISE_FLAG_PADDED) {
DCHECK_EQ(remaining_padding_payload_length_, 0u);
uint8 pad_payload_len = 0;
successful_read = reader.ReadUInt8(&pad_payload_len);
DCHECK(successful_read);
remaining_padding_payload_length_ = pad_payload_len;
}
}
{
SpdyStreamId promised_stream_id = kInvalidStream; SpdyStreamId promised_stream_id = kInvalidStream;
bool successful_read = reader.ReadUInt31(&promised_stream_id); bool successful_read = reader.ReadUInt31(&promised_stream_id);
DCHECK(successful_read); DCHECK(successful_read);
...@@ -1533,7 +1563,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, ...@@ -1533,7 +1563,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
(current_frame_flags_ & (current_frame_flags_ &
PUSH_PROMISE_FLAG_END_PUSH_PROMISE) != 0); PUSH_PROMISE_FLAG_END_PUSH_PROMISE) != 0);
} }
CHANGE_STATE(SPDY_READ_PADDING_LENGTH); CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
break; break;
case CONTINUATION: case CONTINUATION:
{ {
...@@ -2120,11 +2150,10 @@ size_t SpdyFramer::ProcessAltSvcFramePayload(const char* data, size_t len) { ...@@ -2120,11 +2150,10 @@ size_t SpdyFramer::ProcessAltSvcFramePayload(const char* data, size_t len) {
return processed_bytes; return processed_bytes;
} }
// TODO(raullenchai): ProcessFramePaddingLength should be able to deal with size_t SpdyFramer::ProcessDataFramePaddingLength(const char* data, size_t len) {
// HEADERS_FLAG_PADDED and PUSH_PROMISE_FLAG_PADDED as well (see b/15777051). DCHECK_EQ(SPDY_READ_DATA_FRAME_PADDING_LENGTH, state_);
size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) { DCHECK_EQ(0u, remaining_padding_payload_length_);
DCHECK_EQ(SPDY_READ_PADDING_LENGTH, state_); DCHECK_EQ(DATA, current_frame_type_);
DCHECK_EQ(remaining_padding_payload_length_, 0u);
size_t original_len = len; size_t original_len = len;
if (current_frame_flags_ & DATA_FLAG_PADDED) { if (current_frame_flags_ & DATA_FLAG_PADDED) {
...@@ -2149,16 +2178,7 @@ size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) { ...@@ -2149,16 +2178,7 @@ size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) {
set_error(SPDY_INVALID_DATA_FRAME_FLAGS); set_error(SPDY_INVALID_DATA_FRAME_FLAGS);
return 0; return 0;
} }
if (current_frame_type_ == DATA) {
CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME);
} else {
DCHECK(current_frame_type_ == HEADERS ||
current_frame_type_ == PUSH_PROMISE ||
current_frame_type_ == SYN_STREAM ||
current_frame_type_ == SYN_REPLY)
<< current_frame_type_;
CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
}
return original_len - len; return original_len - len;
} }
...@@ -2606,11 +2626,20 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( ...@@ -2606,11 +2626,20 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders(
if (headers.has_priority()) { if (headers.has_priority()) {
flags |= HEADERS_FLAG_PRIORITY; flags |= HEADERS_FLAG_PRIORITY;
} }
if (headers.padded()) {
flags |= HEADERS_FLAG_PADDED;
}
} }
// The size of this frame, including variable-length name-value block. // The size of this frame, including padding (if there is any)
// and variable-length name-value block.
size_t size = GetHeadersMinimumSize(); size_t size = GetHeadersMinimumSize();
if (protocol_version() > SPDY3 && headers.padded()) {
size += kPadLengthFieldSize;
size += headers.padding_payload_len();
}
uint32 priority = headers.priority(); uint32 priority = headers.priority();
if (headers.has_priority()) { if (headers.has_priority()) {
if (priority > GetLowestPriority()) { if (priority > GetLowestPriority()) {
...@@ -2655,6 +2684,11 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( ...@@ -2655,6 +2684,11 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders(
DCHECK_EQ(GetHeadersMinimumSize(), builder.length()); DCHECK_EQ(GetHeadersMinimumSize(), builder.length());
if (protocol_version() > SPDY3) { if (protocol_version() > SPDY3) {
int padding_payload_len = 0;
if (headers.padded()) {
builder.WriteUInt8(headers.padding_payload_len());
padding_payload_len = headers.padding_payload_len();
}
if (headers.has_priority()) { if (headers.has_priority()) {
// TODO(jgraettinger): Plumb priorities and stream dependencies. // TODO(jgraettinger): Plumb priorities and stream dependencies.
builder.WriteUInt32(0); // Non-exclusive bit and root stream ID. builder.WriteUInt32(0); // Non-exclusive bit and root stream ID.
...@@ -2663,7 +2697,8 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( ...@@ -2663,7 +2697,8 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders(
WritePayloadWithContinuation(&builder, WritePayloadWithContinuation(&builder,
hpack_encoding, hpack_encoding,
headers.stream_id(), headers.stream_id(),
HEADERS); HEADERS,
padding_payload_len);
} else { } else {
SerializeNameValueBlock(&builder, headers); SerializeNameValueBlock(&builder, headers);
} }
...@@ -2717,6 +2752,12 @@ SpdyFrame* SpdyFramer::SerializePushPromise( ...@@ -2717,6 +2752,12 @@ SpdyFrame* SpdyFramer::SerializePushPromise(
// The size of this frame, including variable-length name-value block. // The size of this frame, including variable-length name-value block.
size_t size = GetPushPromiseMinimumSize(); size_t size = GetPushPromiseMinimumSize();
if (push_promise.padded()) {
flags |= PUSH_PROMISE_FLAG_PADDED;
size += kPadLengthFieldSize;
size += push_promise.padding_payload_len();
}
string hpack_encoding; string hpack_encoding;
if (enable_compression_) { if (enable_compression_) {
GetHpackEncoder()->EncodeHeaderSet( GetHpackEncoder()->EncodeHeaderSet(
...@@ -2737,13 +2778,24 @@ SpdyFrame* SpdyFramer::SerializePushPromise( ...@@ -2737,13 +2778,24 @@ SpdyFrame* SpdyFramer::SerializePushPromise(
PUSH_PROMISE, PUSH_PROMISE,
flags, flags,
push_promise.stream_id()); push_promise.stream_id());
int padding_payload_len = 0;
if (push_promise.padded()) {
builder.WriteUInt8(push_promise.padding_payload_len());
builder.WriteUInt32(push_promise.promised_stream_id());
DCHECK_EQ(GetPushPromiseMinimumSize() + kPadLengthFieldSize,
builder.length());
padding_payload_len = push_promise.padding_payload_len();
} else {
builder.WriteUInt32(push_promise.promised_stream_id()); builder.WriteUInt32(push_promise.promised_stream_id());
DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length()); DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length());
}
WritePayloadWithContinuation(&builder, WritePayloadWithContinuation(&builder,
hpack_encoding, hpack_encoding,
push_promise.stream_id(), push_promise.stream_id(),
PUSH_PROMISE); PUSH_PROMISE,
padding_payload_len);
if (debug_visitor_) { if (debug_visitor_) {
// SPDY4 uses HPACK for header compression. However, continue to // SPDY4 uses HPACK for header compression. However, continue to
...@@ -2923,32 +2975,34 @@ size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) { ...@@ -2923,32 +2975,34 @@ size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) {
void SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder, void SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder,
const string& hpack_encoding, const string& hpack_encoding,
SpdyStreamId stream_id, SpdyStreamId stream_id,
SpdyFrameType type) { SpdyFrameType type,
int padding_payload_len) {
const size_t kMaxControlFrameSize = GetHeaderFragmentMaxSize(); const size_t kMaxControlFrameSize = GetHeaderFragmentMaxSize();
// In addition to the prefix, fixed_field_size includes the size of
// any fields that come before the variable-length name/value block.
size_t fixed_field_size = 0;
uint8 end_flag = 0; uint8 end_flag = 0;
uint8 flags = 0; uint8 flags = 0;
if (type == HEADERS) { if (type == HEADERS) {
fixed_field_size = GetHeadersMinimumSize();
end_flag = HEADERS_FLAG_END_HEADERS; end_flag = HEADERS_FLAG_END_HEADERS;
} else if (type == PUSH_PROMISE) { } else if (type == PUSH_PROMISE) {
fixed_field_size = GetPushPromiseMinimumSize();
end_flag = PUSH_PROMISE_FLAG_END_PUSH_PROMISE; end_flag = PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
} else { } else {
DLOG(FATAL) << "CONTINUATION frames cannot be used with frame type " DLOG(FATAL) << "CONTINUATION frames cannot be used with frame type "
<< FrameTypeToString(type); << FrameTypeToString(type);
} }
// Write as much of the payload as possible into the initial frame. // Write all the padding payload and as much of the data payload as possible
size_t bytes_remaining = hpack_encoding.size() - // into the initial frame.
size_t bytes_remaining = 0;
bytes_remaining = hpack_encoding.size() -
std::min(hpack_encoding.size(), std::min(hpack_encoding.size(),
kMaxControlFrameSize - fixed_field_size); kMaxControlFrameSize - builder->length() -
padding_payload_len);
builder->WriteBytes(&hpack_encoding[0], builder->WriteBytes(&hpack_encoding[0],
hpack_encoding.size() - bytes_remaining); hpack_encoding.size() - bytes_remaining);
if (padding_payload_len > 0) {
string padding = string(padding_payload_len, 0);
builder->WriteBytes(padding.data(), padding.length());
}
if (bytes_remaining > 0) { if (bytes_remaining > 0) {
builder->OverwriteLength(*this, builder->OverwriteLength(*this,
kMaxControlFrameSize - GetControlFrameHeaderSize()); kMaxControlFrameSize - GetControlFrameHeaderSize());
......
...@@ -336,7 +336,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { ...@@ -336,7 +336,7 @@ class NET_EXPORT_PRIVATE SpdyFramer {
SPDY_AUTO_RESET, SPDY_AUTO_RESET,
SPDY_READING_COMMON_HEADER, SPDY_READING_COMMON_HEADER,
SPDY_CONTROL_FRAME_PAYLOAD, SPDY_CONTROL_FRAME_PAYLOAD,
SPDY_READ_PADDING_LENGTH, SPDY_READ_DATA_FRAME_PADDING_LENGTH,
SPDY_CONSUME_PADDING, SPDY_CONSUME_PADDING,
SPDY_IGNORE_REMAINING_PAYLOAD, SPDY_IGNORE_REMAINING_PAYLOAD,
SPDY_FORWARD_STREAM_FRAME, SPDY_FORWARD_STREAM_FRAME,
...@@ -601,6 +601,8 @@ class NET_EXPORT_PRIVATE SpdyFramer { ...@@ -601,6 +601,8 @@ class NET_EXPORT_PRIVATE SpdyFramer {
UnclosedStreamDataCompressorsOneByteAtATime); UnclosedStreamDataCompressorsOneByteAtATime);
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest,
UncompressLargerThanFrameBufferInitialSize); UncompressLargerThanFrameBufferInitialSize);
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest,
CreatePushPromiseThenContinuationUncompressed);
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ReadLargeSettingsFrame); FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ReadLargeSettingsFrame);
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest,
ReadLargeSettingsFrameInSmallChunks); ReadLargeSettingsFrameInSmallChunks);
...@@ -634,7 +636,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { ...@@ -634,7 +636,7 @@ class NET_EXPORT_PRIVATE SpdyFramer {
size_t ProcessControlFrameHeaderBlock(const char* data, size_t ProcessControlFrameHeaderBlock(const char* data,
size_t len, size_t len,
bool is_hpack_header_block); bool is_hpack_header_block);
size_t ProcessFramePaddingLength(const char* data, size_t len); size_t ProcessDataFramePaddingLength(const char* data, size_t len);
size_t ProcessFramePadding(const char* data, size_t len); size_t ProcessFramePadding(const char* data, size_t len);
size_t ProcessDataFramePayload(const char* data, size_t len); size_t ProcessDataFramePayload(const char* data, size_t len);
size_t ProcessGoAwayFramePayload(const char* data, size_t len); size_t ProcessGoAwayFramePayload(const char* data, size_t len);
...@@ -672,7 +674,8 @@ class NET_EXPORT_PRIVATE SpdyFramer { ...@@ -672,7 +674,8 @@ class NET_EXPORT_PRIVATE SpdyFramer {
void WritePayloadWithContinuation(SpdyFrameBuilder* builder, void WritePayloadWithContinuation(SpdyFrameBuilder* builder,
const std::string& hpack_encoding, const std::string& hpack_encoding,
SpdyStreamId stream_id, SpdyStreamId stream_id,
SpdyFrameType type); SpdyFrameType type,
int padding_payload_len);
private: private:
// Deliver the given control frame's uncompressed headers block to the // Deliver the given control frame's uncompressed headers block to the
......
...@@ -2805,6 +2805,35 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { ...@@ -2805,6 +2805,35 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
} }
} }
{
const char kDescription[] =
"HEADERS frame with a 0-length header name, FIN, max stream ID, padded";
const unsigned char kV4FrameData[] = {
0x00, 0x00, 0x15, 0x01, // Headers
0x0d, 0x7f, 0xff, 0xff, // FIN | END_HEADERS | PADDED, Stream
// 0x7fffffff
0xff, 0x05, 0x00, 0x00, // Pad length field
0x03, 0x66, 0x6f, 0x6f, // .foo
0x00, 0x03, 0x66, 0x6f, // @.fo
0x6f, 0x03, 0x62, 0x61, // o.ba
0x72, // r
// Padding payload
0x00, 0x00, 0x00, 0x00, 0x00,
};
SpdyHeadersIR headers_ir(0x7fffffff);
headers_ir.set_fin(true);
headers_ir.SetHeader("", "foo");
headers_ir.SetHeader("foo", "bar");
headers_ir.set_padding_len(6);
scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
if (IsSpdy2() || IsSpdy3()) {
// Padding is not supported.
} else {
CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
}
}
} }
// TODO(phajdan.jr): Clean up after we no longer need // TODO(phajdan.jr): Clean up after we no longer need
...@@ -2982,27 +3011,98 @@ TEST_P(SpdyFramerTest, CreatePushPromiseUncompressed) { ...@@ -2982,27 +3011,98 @@ TEST_P(SpdyFramerTest, CreatePushPromiseUncompressed) {
return; return;
} }
{
// Test framing PUSH_PROMISE without padding.
SpdyFramer framer(spdy_version_); SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false); framer.set_enable_compression(false);
const char kDescription[] = "PUSH_PROMISE frame"; const char kDescription[] = "PUSH_PROMISE frame without padding";
const unsigned char kFrameData[] = { const unsigned char kFrameData[] = {
0x00, 0x00, 0x16, 0x05, 0x04, // PUSH_PROMISE: END_HEADERS 0x00, 0x00, 0x16, 0x05, // PUSH_PROMISE
0x00, 0x00, 0x00, 0x2a, // Stream 42 0x04, 0x00, 0x00, 0x00, // END_HEADERS
0x00, 0x00, 0x00, 0x39, // Promised stream 57 0x2a, 0x00, 0x00, 0x00, // Stream 42
0x00, 0x03, 0x62, 0x61, // @.ba 0x39, 0x00, 0x03, 0x62, // Promised stream 57, @.b
0x72, 0x03, 0x66, 0x6f, // r.fo 0x61, 0x72, 0x03, 0x66, // ar.f
0x6f, 0x00, 0x03, 0x66, // o@.f 0x6f, 0x6f, 0x00, 0x03, // oo@.
0x6f, 0x6f, 0x03, 0x62, // oo.b 0x66, 0x6f, 0x6f, 0x03, // foo.
0x61, 0x72, // ar 0x62, 0x61, 0x72, // bar
};
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 framing PUSH_PROMISE with one byte of padding.
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
const char kDescription[] = "PUSH_PROMISE frame with one byte of padding";
const unsigned char kFrameData[] = {
0x00, 0x00, 0x17, 0x05, // PUSH_PROMISE
0x0c, 0x00, 0x00, 0x00, // END_HEADERS | PADDED
0x2a, 0x00, 0x00, 0x00, // Stream 42, Pad length field
0x00, 0x39, 0x00, 0x03, // Promised stream 57
0x62, 0x61, 0x72, 0x03, // bar.
0x66, 0x6f, 0x6f, 0x00, // foo@
0x03, 0x66, 0x6f, 0x6f, // .foo
0x03, 0x62, 0x61, 0x72, // .bar
};
SpdyPushPromiseIR push_promise(42, 57);
push_promise.set_padding_len(1);
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 framing PUSH_PROMISE with 177 bytes of padding.
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
const char kDescription[] = "PUSH_PROMISE frame with 177 bytes of padding";
const unsigned char kFrameData[] = {
0x00, 0x00, 0xc7, 0x05, // PUSH_PROMISE
0x0c, 0x00, 0x00, 0x00, // END_HEADERS | PADDED
0x2a, 0xb0, 0x00, 0x00, // Stream 42, Pad length field
0x00, 0x39, 0x00, 0x03, // Promised stream 57
0x62, 0x61, 0x72, 0x03, // bar.
0x66, 0x6f, 0x6f, 0x00, // foo@
0x03, 0x66, 0x6f, 0x6f, // .foo
0x03, 0x62, 0x61, 0x72, // .bar
// Padding of 176 0x00(s).
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}; };
SpdyPushPromiseIR push_promise(42, 57); SpdyPushPromiseIR push_promise(42, 57);
push_promise.set_padding_len(177);
push_promise.SetHeader("bar", "foo"); push_promise.SetHeader("bar", "foo");
push_promise.SetHeader("foo", "bar"); push_promise.SetHeader("foo", "bar");
scoped_ptr<SpdySerializedFrame> frame( scoped_ptr<SpdySerializedFrame> frame(
framer.SerializePushPromise(push_promise)); framer.SerializePushPromise(push_promise));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
}
} }
TEST_P(SpdyFramerTest, CreateContinuationUncompressed) { TEST_P(SpdyFramerTest, CreateContinuationUncompressed) {
...@@ -3032,6 +3132,107 @@ TEST_P(SpdyFramerTest, CreateContinuationUncompressed) { ...@@ -3032,6 +3132,107 @@ TEST_P(SpdyFramerTest, CreateContinuationUncompressed) {
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
} }
TEST_P(SpdyFramerTest, CreatePushPromiseThenContinuationUncompressed) {
if (spdy_version_ <= SPDY3) {
return;
}
{
// Test framing in a case such that a PUSH_PROMISE frame, with one byte of
// padding, cannot hold all the data payload, which is overflowed to the
// consecutive CONTINUATION frame.
SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false);
const char kDescription[] =
"PUSH_PROMISE and CONTINUATION frames with one byte of padding";
const unsigned char kPartialPushPromiseFrameData[] = {
0x00, 0x03, 0xf6, 0x05, // PUSH_PROMISE
0x08, 0x00, 0x00, 0x00, // PADDED
0x2a, 0x00, 0x00, 0x00, // Stream 42
0x00, 0x39, 0x00, 0x03, // Promised stream 57
0x78, 0x78, 0x78, 0x7f, // xxx.
0x80, 0x07, 0x78, 0x78, // ..xx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, // xx
};
const unsigned char kContinuationFrameData[] = {
0x00, 0x00, 0x16, 0x09, // CONTINUATION
0x04, 0x00, 0x00, 0x00, // END_HEADERS
0x2a, 0x78, 0x78, 0x78, // Stream 42, xxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78, 0x78, 0x78, // xxxx
0x78, 0x78,
};
SpdyPushPromiseIR push_promise(42, 57);
push_promise.set_padding_len(1);
string big_value(framer.GetHeaderFragmentMaxSize(), 'x');
push_promise.SetHeader("xxx", big_value);
scoped_ptr<SpdySerializedFrame> frame(
framer.SerializePushPromise(push_promise));
// The entire frame should look like below:
// Name Length in Byte
// ------------------------------------------- Begin of PUSH_PROMISE frame
// PUSH_PROMISE header 9
// Pad length field 1
// Promised stream 4
// Length field of key 2
// Content of key 3
// Length field of value 3
// Part of big_value 16361
// ------------------------------------------- Begin of CONTINUATION frame
// CONTINUATION header 9
// Remaining of big_value 22
// ------------------------------------------- End
// Length of everything listed above except big_value.
int len_non_data_payload = 31;
EXPECT_EQ(framer.GetHeaderFragmentMaxSize() + len_non_data_payload,
frame->size());
// Partially compare the PUSH_PROMISE frame against the template.
const unsigned char* frame_data =
reinterpret_cast<const unsigned char*>(frame->data());
CompareCharArraysWithHexError(kDescription,
frame_data,
arraysize(kPartialPushPromiseFrameData),
kPartialPushPromiseFrameData,
arraysize(kPartialPushPromiseFrameData));
// Compare the CONTINUATION frame against the template.
frame_data += framer.GetHeaderFragmentMaxSize();
CompareCharArraysWithHexError(kDescription,
frame_data,
arraysize(kContinuationFrameData),
kContinuationFrameData,
arraysize(kContinuationFrameData));
}
}
TEST_P(SpdyFramerTest, CreateAltSvc) { TEST_P(SpdyFramerTest, CreateAltSvc) {
if (spdy_version_ <= SPDY3) { if (spdy_version_ <= SPDY3) {
return; return;
...@@ -3263,6 +3464,7 @@ TEST_P(SpdyFramerTest, TooLargeHeadersFrameUsesContinuation) { ...@@ -3263,6 +3464,7 @@ TEST_P(SpdyFramerTest, TooLargeHeadersFrameUsesContinuation) {
SpdyFramer framer(spdy_version_); SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false); framer.set_enable_compression(false);
SpdyHeadersIR headers(1); SpdyHeadersIR headers(1);
headers.set_padding_len(256);
// Exact payload length will change with HPACK, but this should be long // Exact payload length will change with HPACK, but this should be long
// enough to cause an overflow. // enough to cause an overflow.
...@@ -3291,6 +3493,7 @@ TEST_P(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation) { ...@@ -3291,6 +3493,7 @@ TEST_P(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation) {
SpdyFramer framer(spdy_version_); SpdyFramer framer(spdy_version_);
framer.set_enable_compression(false); framer.set_enable_compression(false);
SpdyPushPromiseIR push_promise(1, 2); SpdyPushPromiseIR push_promise(1, 2);
push_promise.set_padding_len(256);
// Exact payload length will change with HPACK, but this should be long // Exact payload length will change with HPACK, but this should be long
// enough to cause an overflow. // enough to cause an overflow.
...@@ -3729,7 +3932,7 @@ TEST_P(SpdyFramerTest, ProcessDataFrameWithPadding) { ...@@ -3729,7 +3932,7 @@ TEST_P(SpdyFramerTest, ProcessDataFrameWithPadding) {
CHECK_EQ(framer.GetDataFrameMinimumSize(), CHECK_EQ(framer.GetDataFrameMinimumSize(),
framer.ProcessInput(frame->data(), framer.ProcessInput(frame->data(),
framer.GetDataFrameMinimumSize())); framer.GetDataFrameMinimumSize()));
CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_PADDING_LENGTH); CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_DATA_FRAME_PADDING_LENGTH);
CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR); CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
bytes_consumed += framer.GetDataFrameMinimumSize(); bytes_consumed += framer.GetDataFrameMinimumSize();
...@@ -3966,31 +4169,31 @@ TEST_P(SpdyFramerTest, ReadPushPromiseWithContinuation) { ...@@ -3966,31 +4169,31 @@ TEST_P(SpdyFramerTest, ReadPushPromiseWithContinuation) {
} }
const unsigned char kInput[] = { const unsigned char kInput[] = {
0x00, 0x00, 0x17, 0x05, 0x08, // PUSH_PROMISE: PADDED 0x00, 0x00, 0x17, 0x05, // PUSH_PROMISE
0x00, 0x00, 0x00, 0x01, // Stream 1 0x08, 0x00, 0x00, 0x00, // PADDED
0x00, 0x00, 0x00, 0x2A, // Promised stream 42 0x01, 0x02, 0x00, 0x00, // Stream 1, Pad length field
0x02, // Padding of 2. 0x00, 0x2A, 0x00, 0x06, // Promised stream 42
0x00, 0x06, 0x63, 0x6f, 0x63, 0x6f, 0x6f, 0x6b,
0x6f, 0x6b, 0x69, 0x65, 0x69, 0x65, 0x07, 0x66,
0x07, 0x66, 0x6f, 0x6f, 0x6f, 0x6f, 0x3d, 0x62,
0x3d, 0x62, 0x61, 0x72, 0x61, 0x72, 0x00, 0x00,
0x00, 0x00,
0x00, 0x00, 0x14, 0x09, // CONTINUATION
0x00, 0x00, 0x14, 0x09, 0x00, // CONTINUATION 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, // Stream 1 0x01, 0x00, 0x06, 0x63, // Stream 1
0x00, 0x06, 0x63, 0x6f,
0x6f, 0x6b, 0x69, 0x65,
0x08, 0x62, 0x61, 0x7a,
0x3d, 0x62, 0x69, 0x6e,
0x67, 0x00, 0x06, 0x63,
0x00, 0x00, 0x12, 0x09, 0x04, // CONTINUATION: END_HEADERS
0x00, 0x00, 0x00, 0x01, // Stream 1
0x6f, 0x6f, 0x6b, 0x69, 0x6f, 0x6f, 0x6b, 0x69,
0x65, 0x00, 0x00, 0x04, 0x65, 0x08, 0x62, 0x61,
0x6e, 0x61, 0x6d, 0x65, 0x7a, 0x3d, 0x62, 0x69,
0x05, 0x76, 0x61, 0x6c, 0x6e, 0x67, 0x00, 0x06,
0x75, 0x65, 0x63,
0x00, 0x00, 0x12, 0x09, // CONTINUATION
0x04, 0x00, 0x00, 0x00, // END_HEADERS
0x01, 0x6f, 0x6f, 0x6b, // Stream 1
0x69, 0x65, 0x00, 0x00,
0x04, 0x6e, 0x61, 0x6d,
0x65, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65,
}; };
SpdyFramer framer(spdy_version_); SpdyFramer framer(spdy_version_);
......
...@@ -847,7 +847,9 @@ class NET_EXPORT_PRIVATE SpdyHeadersIR : public SpdyFrameWithNameValueBlockIR { ...@@ -847,7 +847,9 @@ class NET_EXPORT_PRIVATE SpdyHeadersIR : public SpdyFrameWithNameValueBlockIR {
explicit SpdyHeadersIR(SpdyStreamId stream_id) explicit SpdyHeadersIR(SpdyStreamId stream_id)
: SpdyFrameWithNameValueBlockIR(stream_id), : SpdyFrameWithNameValueBlockIR(stream_id),
has_priority_(false), has_priority_(false),
priority_(0) {} priority_(0),
padded_(false),
padding_payload_len_(0) {}
void Visit(SpdyFrameVisitor* visitor) const override; void Visit(SpdyFrameVisitor* visitor) const override;
...@@ -856,10 +858,24 @@ class NET_EXPORT_PRIVATE SpdyHeadersIR : public SpdyFrameWithNameValueBlockIR { ...@@ -856,10 +858,24 @@ class NET_EXPORT_PRIVATE SpdyHeadersIR : public SpdyFrameWithNameValueBlockIR {
uint32 priority() const { return priority_; } uint32 priority() const { return priority_; }
void set_priority(SpdyPriority priority) { priority_ = priority; } void set_priority(SpdyPriority priority) { priority_ = priority; }
bool padded() const { return padded_; }
int padding_payload_len() const { return padding_payload_len_; }
void set_padding_len(int padding_len) {
DCHECK_GT(padding_len, 0);
DCHECK_LE(padding_len, kPaddingSizePerFrame);
padded_ = true;
// The pad field takes one octet on the wire.
padding_payload_len_ = padding_len - 1;
}
private: private:
bool has_priority_; bool has_priority_;
// 31-bit priority. // 31-bit priority.
uint32 priority_; uint32 priority_;
bool padded_;
int padding_payload_len_;
DISALLOW_COPY_AND_ASSIGN(SpdyHeadersIR); DISALLOW_COPY_AND_ASSIGN(SpdyHeadersIR);
}; };
...@@ -901,14 +917,30 @@ class NET_EXPORT_PRIVATE SpdyPushPromiseIR ...@@ -901,14 +917,30 @@ class NET_EXPORT_PRIVATE SpdyPushPromiseIR
public: public:
SpdyPushPromiseIR(SpdyStreamId stream_id, SpdyStreamId promised_stream_id) SpdyPushPromiseIR(SpdyStreamId stream_id, SpdyStreamId promised_stream_id)
: SpdyFrameWithNameValueBlockIR(stream_id), : SpdyFrameWithNameValueBlockIR(stream_id),
promised_stream_id_(promised_stream_id) {} promised_stream_id_(promised_stream_id),
padded_(false),
padding_payload_len_(0) {}
SpdyStreamId promised_stream_id() const { return promised_stream_id_; } SpdyStreamId promised_stream_id() const { return promised_stream_id_; }
void set_promised_stream_id(SpdyStreamId id) { promised_stream_id_ = id; } void set_promised_stream_id(SpdyStreamId id) { promised_stream_id_ = id; }
void Visit(SpdyFrameVisitor* visitor) const override; void Visit(SpdyFrameVisitor* visitor) const override;
bool padded() const { return padded_; }
int padding_payload_len() const { return padding_payload_len_; }
void set_padding_len(int padding_len) {
DCHECK_GT(padding_len, 0);
DCHECK_LE(padding_len, kPaddingSizePerFrame);
padded_ = true;
// The pad field takes one octet on the wire.
padding_payload_len_ = padding_len - 1;
}
private: private:
SpdyStreamId promised_stream_id_; SpdyStreamId promised_stream_id_;
bool padded_;
int padding_payload_len_;
DISALLOW_COPY_AND_ASSIGN(SpdyPushPromiseIR); DISALLOW_COPY_AND_ASSIGN(SpdyPushPromiseIR);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment