Index: net/spdy/spdy_framer.cc |
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc |
index 6d57aa9563d54b38299c186abbff6d8883e15c8f..0b0c9619ae1da3c7ae7e48fe504794f2257622e2 100644 |
--- a/net/spdy/spdy_framer.cc |
+++ b/net/spdy/spdy_framer.cc |
@@ -166,22 +166,12 @@ void SpdyFramer::Reset() { |
} |
size_t SpdyFramer::GetDataFrameMinimumSize() const { |
- // Size, in bytes, of the data frame header. Future versions of SPDY |
- // will likely vary this, so we allow for the flexibility of a function call |
- // for this value as opposed to a constant. |
- return 8; |
+ return SpdyConstants::GetDataFrameMinimumSize(); |
} |
// Size, in bytes, of the control frame header. |
size_t SpdyFramer::GetControlFrameHeaderSize() const { |
- switch (protocol_version()) { |
- case SPDY2: |
- case SPDY3: |
- case SPDY4: |
- return 8; |
- } |
- LOG(DFATAL) << "Unhandled SPDY version."; |
- return 0; |
+ return SpdyConstants::GetControlFrameHeaderSize(protocol_version()); |
} |
size_t SpdyFramer::GetSynStreamMinimumSize() const { |
@@ -326,19 +316,17 @@ size_t SpdyFramer::GetFrameMinimumSize() const { |
} |
size_t SpdyFramer::GetFrameMaximumSize() const { |
- if (protocol_version() <= SPDY3) { |
- // 24-bit length field plus eight-byte frame header. |
- return ((1<<24) - 1) + 8; |
- } else { |
- // 14-bit length field. |
- return (1<<14) - 1; |
- } |
+ return SpdyConstants::GetFrameMaximumSize(protocol_version()); |
} |
size_t SpdyFramer::GetDataFrameMaximumPayload() const { |
return GetFrameMaximumSize() - GetDataFrameMinimumSize(); |
} |
+size_t SpdyFramer::GetPrefixLength(SpdyFrameType type) const { |
+ return SpdyConstants::GetPrefixLength(type, protocol_version()); |
+} |
+ |
const char* SpdyFramer::StateToString(int state) { |
switch (state) { |
case SPDY_ERROR: |
@@ -1601,7 +1589,8 @@ void SpdyFramer::DeliverHpackBlockAsSpdy3Block() { |
return; |
} |
SpdyFrameBuilder builder( |
- GetSerializedLength(protocol_version(), &block)); |
+ GetSerializedLength(protocol_version(), &block), |
+ SPDY3); |
SerializeNameValueBlockWithoutCompression(&builder, block); |
scoped_ptr<SpdyFrame> frame(builder.take()); |
@@ -2031,7 +2020,7 @@ SpdySerializedFrame* SpdyFramer::SerializeData( |
const size_t size_with_padding = num_padding_fields + |
data_ir.data().length() + data_ir.padding_payload_len() + |
GetDataFrameMinimumSize(); |
- SpdyFrameBuilder builder(size_with_padding); |
+ SpdyFrameBuilder builder(size_with_padding, protocol_version()); |
builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags); |
if (data_ir.pad_high()) { |
builder.WriteUInt8(data_ir.padding_payload_len() >> 8); |
@@ -2048,7 +2037,7 @@ SpdySerializedFrame* SpdyFramer::SerializeData( |
return builder.take(); |
} else { |
const size_t size = GetDataFrameMinimumSize() + data_ir.data().length(); |
- SpdyFrameBuilder builder(size); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags); |
builder.WriteBytes(data_ir.data().data(), data_ir.data().length()); |
DCHECK_EQ(size, builder.length()); |
@@ -2077,7 +2066,7 @@ SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField( |
frame_size += num_padding_fields; |
} |
- SpdyFrameBuilder builder(frame_size); |
+ SpdyFrameBuilder builder(frame_size, protocol_version()); |
builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags); |
if (protocol_version() > SPDY3) { |
if (data_ir.pad_high()) { |
@@ -2131,7 +2120,7 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream( |
size += GetSerializedLength(syn_stream.name_value_block()); |
} |
- SpdyFrameBuilder builder(size); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, SYN_STREAM, flags); |
builder.WriteUInt32(syn_stream.stream_id()); |
@@ -2139,10 +2128,10 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream( |
builder.WriteUInt8(priority << ((protocol_version() <= SPDY2) ? 6 : 5)); |
builder.WriteUInt8(0); // Unused byte where credential slot used to be. |
} else { |
- builder.WriteFramePrefix(*this, |
- HEADERS, |
- flags, |
- syn_stream.stream_id()); |
+ builder.BeginNewFrame(*this, |
+ HEADERS, |
+ flags, |
+ syn_stream.stream_id()); |
builder.WriteUInt32(priority); |
} |
DCHECK_EQ(GetSynStreamMinimumSize(), builder.length()); |
@@ -2191,15 +2180,15 @@ SpdySerializedFrame* SpdyFramer::SerializeSynReply( |
size += GetSerializedLength(syn_reply.name_value_block()); |
} |
- SpdyFrameBuilder builder(size); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, SYN_REPLY, flags); |
builder.WriteUInt32(syn_reply.stream_id()); |
} else { |
- builder.WriteFramePrefix(*this, |
- HEADERS, |
- flags, |
- syn_reply.stream_id()); |
+ builder.BeginNewFrame(*this, |
+ HEADERS, |
+ flags, |
+ syn_reply.stream_id()); |
} |
if (protocol_version() < SPDY3) { |
builder.WriteUInt16(0); // Unused. |
@@ -2236,14 +2225,14 @@ SpdySerializedFrame* SpdyFramer::SerializeRstStream( |
if (protocol_version() > SPDY3) { |
expected_length += rst_stream.description().size(); |
} |
- SpdyFrameBuilder builder(expected_length); |
+ SpdyFrameBuilder builder(expected_length, protocol_version()); |
// Serialize the RST_STREAM frame. |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, RST_STREAM, 0); |
builder.WriteUInt32(rst_stream.stream_id()); |
} else { |
- builder.WriteFramePrefix(*this, RST_STREAM, 0, rst_stream.stream_id()); |
+ builder.BeginNewFrame(*this, RST_STREAM, 0, rst_stream.stream_id()); |
} |
builder.WriteUInt32(rst_stream.status()); |
@@ -2277,11 +2266,11 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings( |
// Size, in bytes, of this SETTINGS frame. |
const size_t size = GetSettingsMinimumSize() + |
(values->size() * setting_size); |
- SpdyFrameBuilder builder(size); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, SETTINGS, flags); |
} else { |
- builder.WriteFramePrefix(*this, SETTINGS, flags, 0); |
+ builder.BeginNewFrame(*this, SETTINGS, flags, 0); |
} |
// If this is an ACK, payload should be empty. |
@@ -2319,15 +2308,8 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings( |
return builder.take(); |
} |
-SpdyFrame* SpdyFramer::SerializeBlocked(const SpdyBlockedIR& blocked) const { |
- DCHECK_LT(SPDY3, protocol_version()); |
- SpdyFrameBuilder builder(GetBlockedSize()); |
- builder.WriteFramePrefix(*this, BLOCKED, kNoFlags, blocked.stream_id()); |
- return builder.take(); |
-} |
- |
SpdySerializedFrame* SpdyFramer::SerializePing(const SpdyPingIR& ping) const { |
- SpdyFrameBuilder builder(GetPingSize()); |
+ SpdyFrameBuilder builder(GetPingSize(), protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, PING, kNoFlags); |
builder.WriteUInt32(static_cast<uint32>(ping.id())); |
@@ -2336,7 +2318,7 @@ SpdySerializedFrame* SpdyFramer::SerializePing(const SpdyPingIR& ping) const { |
if (ping.is_ack()) { |
flags |= PING_FLAG_ACK; |
} |
- builder.WriteFramePrefix(*this, PING, flags, 0); |
+ builder.BeginNewFrame(*this, PING, flags, 0); |
builder.WriteUInt64(ping.id()); |
} |
DCHECK_EQ(GetPingSize(), builder.length()); |
@@ -2351,13 +2333,13 @@ SpdySerializedFrame* SpdyFramer::SerializeGoAway( |
if (protocol_version() > SPDY3) { |
expected_length += goaway.description().size(); |
} |
- SpdyFrameBuilder builder(expected_length); |
+ SpdyFrameBuilder builder(expected_length, protocol_version()); |
// Serialize the GOAWAY frame. |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, GOAWAY, kNoFlags); |
} else { |
- builder.WriteFramePrefix(*this, GOAWAY, 0, 0); |
+ builder.BeginNewFrame(*this, GOAWAY, 0, 0); |
} |
// GOAWAY frames specify the last good stream id for all SPDY versions. |
@@ -2385,6 +2367,9 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( |
flags |= CONTROL_FLAG_FIN; |
} |
if (protocol_version() > SPDY3) { |
+ // TODO(mlavan): If we overflow into a CONTINUATION frame, this will |
+ // get overwritten below, so we should probably just get rid of the |
+ // end_headers field. |
if (headers.end_headers()) { |
flags |= HEADERS_FLAG_END_HEADERS; |
} |
@@ -2409,19 +2394,24 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( |
if (protocol_version() > SPDY3) { |
hpack_encoder_.EncodeHeaderSet(headers.name_value_block(), &hpack_encoding); |
size += hpack_encoding.size(); |
+ if (size > GetControlFrameBufferMaxSize()) { |
+ size += GetNumberRequiredContinuationFrames(size) * |
+ GetContinuationMinimumSize(); |
+ flags &= ~HEADERS_FLAG_END_HEADERS; |
+ } |
} else { |
size += GetSerializedLength(headers.name_value_block()); |
} |
- SpdyFrameBuilder builder(size); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, HEADERS, flags); |
builder.WriteUInt32(headers.stream_id()); |
} else { |
- builder.WriteFramePrefix(*this, |
- HEADERS, |
- flags, |
- headers.stream_id()); |
+ builder.BeginNewFrame(*this, |
+ HEADERS, |
+ flags, |
+ headers.stream_id()); |
if (headers.has_priority()) { |
builder.WriteUInt32(priority); |
} |
@@ -2432,7 +2422,10 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( |
DCHECK_EQ(GetHeadersMinimumSize(), builder.length()); |
if (protocol_version() > SPDY3) { |
- builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size()); |
+ WritePayloadWithContinuation(&builder, |
+ hpack_encoding, |
+ headers.stream_id(), |
+ HEADERS); |
} else { |
SerializeNameValueBlock(&builder, headers); |
} |
@@ -2453,25 +2446,35 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders( |
SpdySerializedFrame* SpdyFramer::SerializeWindowUpdate( |
const SpdyWindowUpdateIR& window_update) const { |
- SpdyFrameBuilder builder(GetWindowUpdateSize()); |
+ SpdyFrameBuilder builder(GetWindowUpdateSize(), protocol_version()); |
if (protocol_version() <= SPDY3) { |
builder.WriteControlFrameHeader(*this, WINDOW_UPDATE, kNoFlags); |
builder.WriteUInt32(window_update.stream_id()); |
} else { |
- builder.WriteFramePrefix(*this, |
- WINDOW_UPDATE, |
- kNoFlags, |
- window_update.stream_id()); |
+ builder.BeginNewFrame(*this, |
+ WINDOW_UPDATE, |
+ kNoFlags, |
+ window_update.stream_id()); |
} |
builder.WriteUInt32(window_update.delta()); |
DCHECK_EQ(GetWindowUpdateSize(), builder.length()); |
return builder.take(); |
} |
+SpdyFrame* SpdyFramer::SerializeBlocked(const SpdyBlockedIR& blocked) const { |
+ DCHECK_LT(SPDY3, protocol_version()); |
+ SpdyFrameBuilder builder(GetBlockedSize(), protocol_version()); |
+ builder.BeginNewFrame(*this, BLOCKED, kNoFlags, blocked.stream_id()); |
+ return builder.take(); |
+} |
+ |
SpdyFrame* SpdyFramer::SerializePushPromise( |
const SpdyPushPromiseIR& push_promise) { |
DCHECK_LT(SPDY3, protocol_version()); |
uint8 flags = 0; |
+ // TODO(mlavan): If we overflow into a CONTINUATION frame, this will |
+ // get overwritten below, so we should probably just get rid of the |
+ // end_push_promise field. |
if (push_promise.end_push_promise()) { |
flags |= PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
} |
@@ -2483,18 +2486,28 @@ SpdyFrame* SpdyFramer::SerializePushPromise( |
hpack_encoder_.EncodeHeaderSet(push_promise.name_value_block(), |
&hpack_encoding); |
size += hpack_encoding.size(); |
+ if (size > GetControlFrameBufferMaxSize()) { |
+ size += GetNumberRequiredContinuationFrames(size) * |
+ GetContinuationMinimumSize(); |
+ flags &= ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
+ } |
} else { |
size += GetSerializedLength(push_promise.name_value_block()); |
} |
- SpdyFrameBuilder builder(size); |
- builder.WriteFramePrefix(*this, PUSH_PROMISE, flags, |
- push_promise.stream_id()); |
+ SpdyFrameBuilder builder(size, protocol_version()); |
+ builder.BeginNewFrame(*this, |
+ PUSH_PROMISE, |
+ flags, |
+ push_promise.stream_id()); |
builder.WriteUInt32(push_promise.promised_stream_id()); |
DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length()); |
if (protocol_version() > SPDY3) { |
- builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size()); |
+ WritePayloadWithContinuation(&builder, |
+ hpack_encoding, |
+ push_promise.stream_id(), |
+ PUSH_PROMISE); |
} else { |
SerializeNameValueBlock(&builder, push_promise); |
} |
@@ -2529,8 +2542,8 @@ SpdyFrame* SpdyFramer::SerializeContinuation( |
&hpack_encoding); |
size += hpack_encoding.size(); |
- SpdyFrameBuilder builder(size); |
- builder.WriteFramePrefix(*this, CONTINUATION, flags, |
+ SpdyFrameBuilder builder(size, protocol_version()); |
+ builder.BeginNewFrame(*this, CONTINUATION, flags, |
continuation.stream_id()); |
DCHECK_EQ(GetContinuationMinimumSize(), builder.length()); |
@@ -2620,6 +2633,69 @@ size_t SpdyFramer::GetSerializedLength(const SpdyHeaderBlock& headers) { |
return 2 * deflateBound(compressor, uncompressed_length); |
} |
+size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) { |
+ const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize(); |
+ DCHECK_GT(protocol_version(), net::SPDY3); |
+ DCHECK_GT(size, kMaxControlFrameSize); |
+ size_t overflow = size - kMaxControlFrameSize; |
+ return overflow / (kMaxControlFrameSize - GetContinuationMinimumSize()) + 1; |
+} |
+ |
+void SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder, |
+ const string& hpack_encoding, |
+ SpdyStreamId stream_id, |
+ SpdyFrameType type) { |
+ const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize(); |
+ |
+ // 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 flags = 0; |
+ if (type == HEADERS) { |
+ fixed_field_size = GetHeadersMinimumSize(); |
+ end_flag = HEADERS_FLAG_END_HEADERS; |
+ } else if (type == PUSH_PROMISE) { |
+ fixed_field_size = GetPushPromiseMinimumSize(); |
+ end_flag = PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
+ } else { |
+ DLOG(FATAL) << "CONTINUATION frames cannot be used with frame type " |
+ << FrameTypeToString(type); |
+ } |
+ |
+ // Write as much of the payload as possible into the initial frame. |
+ size_t bytes_remaining = hpack_encoding.size() - |
+ std::min(hpack_encoding.size(), |
+ kMaxControlFrameSize - fixed_field_size); |
+ builder->WriteBytes(&hpack_encoding[0], |
+ hpack_encoding.size() - bytes_remaining); |
+ |
+ if (bytes_remaining > 0) { |
+ builder->OverwriteLength(*this, |
+ kMaxControlFrameSize - GetControlFrameHeaderSize()); |
+ } |
+ |
+ // Tack on CONTINUATION frames for the overflow. |
+ while (bytes_remaining > 0) { |
+ size_t bytes_to_write = std::min(bytes_remaining, |
+ kMaxControlFrameSize - |
+ GetContinuationMinimumSize()); |
+ // Write CONTINUATION frame prefix. |
+ if (bytes_remaining == bytes_to_write) { |
+ flags |= end_flag; |
+ } |
+ builder->BeginNewFrame(*this, |
+ CONTINUATION, |
+ flags, |
+ stream_id); |
+ // Write payload fragment. |
+ builder->WriteBytes(&hpack_encoding[hpack_encoding.size() - |
+ bytes_remaining], |
+ bytes_to_write); |
+ bytes_remaining -= bytes_to_write; |
+ } |
+} |
+ |
// The following compression setting are based on Brian Olson's analysis. See |
// https://groups.google.com/group/spdy-dev/browse_thread/thread/dfaf498542fac792 |
// for more details. |
@@ -2807,7 +2883,7 @@ void SpdyFramer::SerializeNameValueBlock( |
// First build an uncompressed version to be fed into the compressor. |
const size_t uncompressed_len = GetSerializedLength( |
protocol_version(), &(frame.name_value_block())); |
- SpdyFrameBuilder uncompressed_builder(uncompressed_len); |
+ SpdyFrameBuilder uncompressed_builder(uncompressed_len, protocol_version()); |
SerializeNameValueBlockWithoutCompression(&uncompressed_builder, |
frame.name_value_block()); |
scoped_ptr<SpdyFrame> uncompressed_payload(uncompressed_builder.take()); |