| Index: net/spdy/spdy_framer.cc
|
| diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
|
| index 85578515861a97680cd3142d72a437b8fa1ac618..5afa3f22a13899cb73d822f6b78e304933355cfd 100644
|
| --- a/net/spdy/spdy_framer.cc
|
| +++ b/net/spdy/spdy_framer.cc
|
| @@ -167,22 +167,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 {
|
| @@ -327,19 +317,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:
|
| @@ -1608,7 +1596,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());
|
| @@ -2052,7 +2041,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);
|
| @@ -2069,7 +2058,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());
|
| @@ -2098,7 +2087,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()) {
|
| @@ -2152,7 +2141,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());
|
| @@ -2160,10 +2149,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());
|
| @@ -2212,15 +2201,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.
|
| @@ -2257,14 +2246,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());
|
| @@ -2298,11 +2287,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.
|
| @@ -2340,15 +2329,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()));
|
| @@ -2357,7 +2339,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());
|
| @@ -2372,13 +2354,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.
|
| @@ -2406,6 +2388,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;
|
| }
|
| @@ -2430,19 +2415,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);
|
| }
|
| @@ -2453,7 +2443,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);
|
| }
|
| @@ -2474,25 +2467,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;
|
| }
|
| @@ -2504,18 +2507,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);
|
| }
|
| @@ -2550,8 +2563,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());
|
|
|
| @@ -2641,6 +2654,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.
|
| @@ -2831,7 +2907,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());
|
|
|