diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-12 23:07:39 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-12 23:07:39 +0000 |
commit | 339489a3c74ee37dd9818f127e285dcf81d2d1fb (patch) | |
tree | 12d5c58973814b8e62d4b10ede1ebe08a1d29c6a | |
parent | f5a2775922b5b54f6b226b3280724df811003e14 (diff) | |
download | chromium_src-339489a3c74ee37dd9818f127e285dcf81d2d1fb.zip chromium_src-339489a3c74ee37dd9818f127e285dcf81d2d1fb.tar.gz chromium_src-339489a3c74ee37dd9818f127e285dcf81d2d1fb.tar.bz2 |
Refactor in preparation for SPDY 4, part 1.
Zero behavioral change to the on-the-wire format nor protocol semantics.
This lands server change 40997047.
Also create spdy_protocol.cc and move some functions to it to
avoid clang warnings.
Review URL: https://chromiumcodereview.appspot.com/12220116
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@182053 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/net.gyp | 1 | ||||
-rw-r--r-- | net/spdy/spdy_frame_builder.cc | 4 | ||||
-rw-r--r-- | net/spdy/spdy_frame_builder.h | 4 | ||||
-rw-r--r-- | net/spdy/spdy_framer.cc | 472 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 27 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.cc | 24 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.h | 304 |
7 files changed, 684 insertions, 152 deletions
diff --git a/net/net.gyp b/net/net.gyp index 51cdaf6..9fad87b 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -826,6 +826,7 @@ 'spdy/spdy_http_utils.h', 'spdy/spdy_io_buffer.cc', 'spdy/spdy_io_buffer.h', + 'spdy/spdy_protocol.cc', 'spdy/spdy_protocol.h', 'spdy/spdy_proxy_client_socket.cc', 'spdy/spdy_proxy_client_socket.h', diff --git a/net/spdy/spdy_frame_builder.cc b/net/spdy/spdy_frame_builder.cc index 314dd12..ead527c 100644 --- a/net/spdy/spdy_frame_builder.cc +++ b/net/spdy/spdy_frame_builder.cc @@ -12,7 +12,7 @@ namespace net { namespace { // Creates a FlagsAndLength. -FlagsAndLength CreateFlagsAndLength(SpdyControlFlags flags, size_t length) { +FlagsAndLength CreateFlagsAndLength(uint8 flags, size_t length) { DCHECK_EQ(0u, length & ~static_cast<size_t>(kLengthMask)); FlagsAndLength flags_length; flags_length.length_ = htonl(static_cast<uint32>(length)); @@ -30,7 +30,7 @@ SpdyFrameBuilder::SpdyFrameBuilder(size_t size) } SpdyFrameBuilder::SpdyFrameBuilder(SpdyControlType type, - SpdyControlFlags flags, + uint8 flags, int spdy_version, size_t size) : buffer_(new char[size]), diff --git a/net/spdy/spdy_frame_builder.h b/net/spdy/spdy_frame_builder.h index 8f3b0cd..0a2410c 100644 --- a/net/spdy/spdy_frame_builder.h +++ b/net/spdy/spdy_frame_builder.h @@ -31,7 +31,9 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder { // Initializes a SpdyFrameBuilder with a buffer of given size, // populate with a SPDY control frame header based on // |type|, |flags|, and |spdy_version|. - SpdyFrameBuilder(SpdyControlType type, SpdyControlFlags flags, + // + // TODO(akalin): Add a typedef for this uint8. + SpdyFrameBuilder(SpdyControlType type, uint8 flags, int spdy_version, size_t size); // Initiailizes a SpdyFrameBuilder with a buffer of given size, diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 21615a0..96ccf9a 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -46,6 +46,9 @@ struct DictionaryIds { // initialized lazily to avoid static initializers. base::LazyInstance<DictionaryIds>::Leaky g_dictionary_ids; +// Used to indicate no flags in a SPDY flags field. +const uint8 kNoFlags = 0; + } // namespace const int SpdyFramer::kMinSpdyVersion = 2; @@ -677,15 +680,13 @@ void SpdyFramer::WriteHeaderBlock(SpdyFrameBuilder* frame, } SpdyHeaderBlock::const_iterator it; for (it = headers->begin(); it != headers->end(); ++it) { - bool wrote_header; if (spdy_version < 3) { - wrote_header = frame->WriteString(it->first); - wrote_header &= frame->WriteString(it->second); + frame->WriteString(it->first); + frame->WriteString(it->second); } else { - wrote_header = frame->WriteStringPiece32(it->first); - wrote_header &= frame->WriteStringPiece32(it->second); + frame->WriteStringPiece32(it->first); + frame->WriteStringPiece32(it->second); } - DCHECK(wrote_header); } } @@ -1291,29 +1292,20 @@ SpdySynStreamControlFrame* SpdyFramer::CreateSynStream( SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) { - DCHECK_EQ(0u, stream_id & ~kStreamIdMask); - DCHECK_EQ(0u, associated_stream_id & ~kStreamIdMask); - - // Find our length. - size_t frame_size = SpdySynStreamControlFrame::size() + - GetSerializedLength(spdy_version_, headers); + DCHECK_EQ(0, flags & ~CONTROL_FLAG_FIN & ~CONTROL_FLAG_UNIDIRECTIONAL); - SpdyFrameBuilder frame(SYN_STREAM, flags, spdy_version_, frame_size); - frame.WriteUInt32(stream_id); - frame.WriteUInt32(associated_stream_id); - // Cap as appropriate. - if (priority > GetLowestPriority()) { - DLOG(DFATAL) << "Priority out-of-bounds."; - priority = GetLowestPriority(); - } - // Priority is 2 bits for <spdy3, 3 bits otherwise. - frame.WriteUInt8(priority << ((spdy_version_ < 3) ? 6 : 5)); - frame.WriteUInt8((spdy_version_ < 3) ? 0 : credential_slot); - WriteHeaderBlock(&frame, spdy_version_, headers); - DCHECK_EQ(frame.length(), frame_size); + SpdySynStreamIR syn_stream(stream_id); + syn_stream.set_associated_to_stream_id(associated_stream_id); + syn_stream.set_priority(priority); + syn_stream.set_slot(credential_slot); + syn_stream.set_fin((flags & CONTROL_FLAG_FIN) != 0); + syn_stream.set_unidirectional((flags & CONTROL_FLAG_UNIDIRECTIONAL) != 0); + // TODO(hkhalil): Avoid copy here. + *(syn_stream.GetMutableNameValueBlock()) = *headers; scoped_ptr<SpdySynStreamControlFrame> syn_frame( - reinterpret_cast<SpdySynStreamControlFrame*>(frame.take())); + reinterpret_cast<SpdySynStreamControlFrame*>( + SerializeSynStream(syn_stream))); if (compressed) { return reinterpret_cast<SpdySynStreamControlFrame*>( CompressControlFrame(*syn_frame.get(), headers)); @@ -1321,32 +1313,57 @@ SpdySynStreamControlFrame* SpdyFramer::CreateSynStream( return syn_frame.release(); } +SpdySerializedFrame* SpdyFramer::SerializeSynStream( + const SpdySynStreamIR& syn_stream) { + uint8 flags = 0; + if (syn_stream.fin()) { + flags |= CONTROL_FLAG_FIN; + } + if (syn_stream.unidirectional()) { + flags |= CONTROL_FLAG_UNIDIRECTIONAL; + } + + // The size, in bytes, of this frame not including the variable-length + // name-value block. Calculated as: + // 8 (control frame header) + 2 * 4 (stream IDs) + 1 (priority) + 1 (slot) + const size_t kSynStreamSizeBeforeNameValueBlock = 18; + + // The size of this frame, including variable-length name-value block. + const size_t size = kSynStreamSizeBeforeNameValueBlock + + GetSerializedLength(protocol_version(), + &(syn_stream.name_value_block())); + + SpdyFrameBuilder builder(SYN_STREAM, flags, protocol_version(), size); + builder.WriteUInt32(syn_stream.stream_id()); + builder.WriteUInt32(syn_stream.associated_to_stream_id()); + uint8 priority = syn_stream.priority(); + if (priority > GetLowestPriority()) { + DLOG(DFATAL) << "Priority out-of-bounds."; + priority = GetLowestPriority(); + } + builder.WriteUInt8(priority << ((spdy_version_ < 3) ? 6 : 5)); + builder.WriteUInt8(syn_stream.slot()); + DCHECK_EQ(kSynStreamSizeBeforeNameValueBlock, builder.length()); + SerializeNameValueBlock(&builder, syn_stream); + + return builder.take(); +} + SpdySynReplyControlFrame* SpdyFramer::CreateSynReply( SpdyStreamId stream_id, SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) { - DCHECK_GT(stream_id, 0u); - DCHECK_EQ(0u, stream_id & ~kStreamIdMask); + DCHECK_EQ(0, flags & ~CONTROL_FLAG_FIN); - // Find our length. - size_t frame_size = SpdySynReplyControlFrame::size() + - GetSerializedLength(spdy_version_, headers); - // In SPDY 2, there were 2 unused bytes before payload. - if (spdy_version_ < 3) { - frame_size += 2; - } - - SpdyFrameBuilder frame(SYN_REPLY, flags, spdy_version_, frame_size); - frame.WriteUInt32(stream_id); - if (spdy_version_ < 3) { - frame.WriteUInt16(0); // Unused - } - WriteHeaderBlock(&frame, spdy_version_, headers); - DCHECK_EQ(frame.length(), frame_size); + SpdySynReplyIR syn_reply(stream_id); + syn_reply.set_fin(flags & CONTROL_FLAG_FIN); + // TODO(hkhalil): Avoid copy here. + *(syn_reply.GetMutableNameValueBlock()) = *headers; scoped_ptr<SpdySynReplyControlFrame> reply_frame( - reinterpret_cast<SpdySynReplyControlFrame*>(frame.take())); + reinterpret_cast<SpdySynReplyControlFrame*>(SerializeSynReply( + syn_reply))); if (compressed) { return reinterpret_cast<SpdySynReplyControlFrame*>( CompressControlFrame(*reply_frame.get(), headers)); @@ -1354,159 +1371,298 @@ SpdySynReplyControlFrame* SpdyFramer::CreateSynReply( return reply_frame.release(); } +SpdySerializedFrame* SpdyFramer::SerializeSynReply( + const SpdySynReplyIR& syn_reply) { + uint8 flags = 0; + if (syn_reply.fin()) { + flags |= CONTROL_FLAG_FIN; + } + + // The size, in bytes, of this frame not including the variable-length + // name-value block. Calculated as: + // 8 (control frame header) + 4 (stream ID) + size_t syn_reply_size_before_name_value_block = 12; + // In SPDY 2, there were 2 unused bytes before payload. + if (protocol_version() < 3) { + syn_reply_size_before_name_value_block += 2; + } + + // The size of this frame, including variable-length name-value block. + size_t size = syn_reply_size_before_name_value_block + + GetSerializedLength(protocol_version(), + &(syn_reply.name_value_block())); + + SpdyFrameBuilder builder(SYN_REPLY, flags, protocol_version(), size); + builder.WriteUInt32(syn_reply.stream_id()); + if (protocol_version() < 3) { + builder.WriteUInt16(0); // Unused. + } + DCHECK_EQ(syn_reply_size_before_name_value_block, builder.length()); + SerializeNameValueBlock(&builder, syn_reply); + + return builder.take(); +} + SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream( SpdyStreamId stream_id, SpdyRstStreamStatus status) const { - DCHECK_GT(stream_id, 0u); - DCHECK_EQ(0u, stream_id & ~kStreamIdMask); - DCHECK_NE(status, RST_STREAM_INVALID); - DCHECK_LT(status, RST_STREAM_NUM_STATUS_CODES); - - size_t frame_size = SpdyRstStreamControlFrame::size(); - SpdyFrameBuilder frame(RST_STREAM, CONTROL_FLAG_NONE, spdy_version_, - frame_size); - frame.WriteUInt32(stream_id); - frame.WriteUInt32(status); - DCHECK_EQ(frame.length(), frame_size); - return reinterpret_cast<SpdyRstStreamControlFrame*>(frame.take()); + SpdyRstStreamIR rst_stream(stream_id, status); + return reinterpret_cast<SpdyRstStreamControlFrame*>( + SerializeRstStream(rst_stream)); +} + +SpdySerializedFrame* SpdyFramer::SerializeRstStream( + const SpdyRstStreamIR& rst_stream) const { + // Size of our RST_STREAM frame. Calculated as: + // 8 (control frame header) + 4 (stream id) + 4 (status code) + const size_t kRstStreamFrameSize = 16; + + SpdyFrameBuilder builder(RST_STREAM, + kNoFlags, + protocol_version(), + kRstStreamFrameSize); + builder.WriteUInt32(rst_stream.stream_id()); + builder.WriteUInt32(rst_stream.status()); + DCHECK_EQ(kRstStreamFrameSize, builder.length()); + return builder.take(); } SpdySettingsControlFrame* SpdyFramer::CreateSettings( const SettingsMap& values) const { - size_t frame_size = SpdySettingsControlFrame::size() + 8 * values.size(); - SpdyFrameBuilder frame(SETTINGS, CONTROL_FLAG_NONE, spdy_version_, - frame_size); - frame.WriteUInt32(values.size()); + SpdySettingsIR settings; for (SettingsMap::const_iterator it = values.begin(); it != values.end(); - it++) { - SettingsFlagsAndId flags_and_id(it->second.first, it->first); - uint32 id_and_flags_wire = flags_and_id.GetWireFormat(spdy_version_); - frame.WriteBytes(&id_and_flags_wire, 4); - frame.WriteUInt32(it->second.second); - } - DCHECK_EQ(frame.length(), frame_size); - return reinterpret_cast<SpdySettingsControlFrame*>(frame.take()); + ++it) { + settings.AddSetting(it->first, + (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0, + (it->second.first & SETTINGS_FLAG_PERSISTED) != 0, + it->second.second); + } + return reinterpret_cast<SpdySettingsControlFrame*>( + SerializeSettings(settings)); +} + +SpdySerializedFrame* SpdyFramer::SerializeSettings( + const SpdySettingsIR& settings) const { + uint8 flags = 0; + if (settings.clear_settings()) { + flags |= SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS; + } + const SpdySettingsIR::ValueMap* values = &(settings.values()); + + // Size, in bytes, of this SETTINGS frame not including the IDs and values + // from the variable-length value block. Calculated as: + // 8 (control frame header) + 4 (number of ID/value pairs) + const size_t kSettingsSizeWithoutValues = 12; + // Size, in bytes, of this SETTINGS frame. + const size_t size = kSettingsSizeWithoutValues + (values->size() * 8); + + SpdyFrameBuilder builder(SETTINGS, flags, protocol_version(), size); + builder.WriteUInt32(values->size()); + DCHECK_EQ(kSettingsSizeWithoutValues, builder.length()); + for (SpdySettingsIR::ValueMap::const_iterator it = values->begin(); + it != values->end(); + ++it) { + uint8 setting_flags = 0; + if (it->second.persist_value) { + setting_flags |= SETTINGS_FLAG_PLEASE_PERSIST; + } + if (it->second.persisted) { + setting_flags |= SETTINGS_FLAG_PERSISTED; + } + SettingsFlagsAndId flags_and_id(setting_flags, it->first); + uint32 id_and_flags_wire = flags_and_id.GetWireFormat(protocol_version()); + builder.WriteBytes(&id_and_flags_wire, 4); + builder.WriteUInt32(it->second.value); + } + DCHECK_EQ(size, builder.length()); + return builder.take(); } SpdyPingControlFrame* SpdyFramer::CreatePingFrame(uint32 unique_id) const { - size_t frame_size = SpdyPingControlFrame::size(); - SpdyFrameBuilder frame(PING, CONTROL_FLAG_NONE, spdy_version_, frame_size); - frame.WriteUInt32(unique_id); - DCHECK_EQ(frame.length(), frame_size); - return reinterpret_cast<SpdyPingControlFrame*>(frame.take()); + SpdyPingIR ping(unique_id); + return reinterpret_cast<SpdyPingControlFrame*>(SerializePing(ping)); +} + +SpdySerializedFrame* SpdyFramer::SerializePing(const SpdyPingIR& ping) const { + // Size, in bytes, of this PING frame. Calculated as: + // 8 (control frame header) + 4 (id) + const size_t kPingSize = 12; + SpdyFrameBuilder builder(PING, 0, protocol_version(), kPingSize); + builder.WriteUInt32(ping.id()); + DCHECK_EQ(kPingSize, builder.length()); + return builder.take(); } SpdyGoAwayControlFrame* SpdyFramer::CreateGoAway( SpdyStreamId last_accepted_stream_id, SpdyGoAwayStatus status) const { - DCHECK_EQ(0u, last_accepted_stream_id & ~kStreamIdMask); - - // SPDY 2 GOAWAY frames are 4 bytes smaller than in SPDY 3. We account for - // this difference via a separate offset variable, since - // SpdyGoAwayControlFrame::size() returns the SPDY 3 size. - const size_t goaway_offset = (protocol_version() < 3) ? 4 : 0; - size_t frame_size = SpdyGoAwayControlFrame::size() - goaway_offset; - SpdyFrameBuilder frame(GOAWAY, CONTROL_FLAG_NONE, spdy_version_, frame_size); - frame.WriteUInt32(last_accepted_stream_id); + SpdyGoAwayIR goaway(last_accepted_stream_id, status); + return reinterpret_cast<SpdyGoAwayControlFrame*>(SerializeGoAway(goaway)); +} + +SpdySerializedFrame* SpdyFramer::SerializeGoAway( + const SpdyGoAwayIR& goaway) const { + // Size, in bytes, of this GOAWAY frame. Calculated as: + // 8 (control frame header) + 4 (last good stream id) + size_t size = 12; + // SPDY 3+ GOAWAY frames also contain a status. + if (protocol_version() >= 3) { + size += 4; + } + SpdyFrameBuilder builder(GOAWAY, 0, protocol_version(), size); + builder.WriteUInt32(goaway.last_good_stream_id()); if (protocol_version() >= 3) { - frame.WriteUInt32(status); + builder.WriteUInt32(goaway.status()); } - DCHECK_EQ(frame.length(), frame_size); - return reinterpret_cast<SpdyGoAwayControlFrame*>(frame.take()); + DCHECK_EQ(size, builder.length()); + return builder.take(); } SpdyHeadersControlFrame* SpdyFramer::CreateHeaders( SpdyStreamId stream_id, SpdyControlFlags flags, bool compressed, - const SpdyHeaderBlock* headers) { + const SpdyHeaderBlock* header_block) { // Basically the same as CreateSynReply(). - DCHECK_GT(stream_id, 0u); - DCHECK_EQ(0u, stream_id & ~kStreamIdMask); - - // Find our length. - size_t frame_size = SpdyHeadersControlFrame::size() + - GetSerializedLength(spdy_version_, headers); - // In SPDY 2, there were 2 unused bytes before payload. - if (spdy_version_ < 3) { - frame_size += 2; - } + DCHECK_EQ(0, flags & (!CONTROL_FLAG_FIN)); - SpdyFrameBuilder frame(HEADERS, flags, spdy_version_, frame_size); - frame.WriteUInt32(stream_id); - if (spdy_version_ < 3) { - frame.WriteUInt16(0); // Unused - } - WriteHeaderBlock(&frame, spdy_version_, headers); - DCHECK_EQ(frame.length(), frame_size); + SpdyHeadersIR headers(stream_id); + headers.set_fin(flags & CONTROL_FLAG_FIN); + // TODO(hkhalil): Avoid copy here. + *(headers.GetMutableNameValueBlock()) = *header_block; scoped_ptr<SpdyHeadersControlFrame> headers_frame( - reinterpret_cast<SpdyHeadersControlFrame*>(frame.take())); + reinterpret_cast<SpdyHeadersControlFrame*>(SerializeHeaders(headers))); if (compressed) { return reinterpret_cast<SpdyHeadersControlFrame*>( - CompressControlFrame(*headers_frame.get(), headers)); + CompressControlFrame(*headers_frame.get(), + headers.GetMutableNameValueBlock())); } return headers_frame.release(); } +SpdySerializedFrame* SpdyFramer::SerializeHeaders( + const SpdyHeadersIR& headers) { + uint8 flags = 0; + if (headers.fin()) { + flags |= CONTROL_FLAG_FIN; + } + + // The size, in bytes, of this frame not including the variable-length + // name-value block. Calculated as: + // 8 (control frame header) + 4 (stream ID) + size_t headers_size_before_name_value_block = 12; + // In SPDY 2, there were 2 unused bytes before payload. + if (protocol_version() < 3) { + headers_size_before_name_value_block += 2; + } + + // The size of this frame, including variable-length name-value block. + size_t size = headers_size_before_name_value_block + + GetSerializedLength(protocol_version(), + &(headers.name_value_block())); + + SpdyFrameBuilder builder(HEADERS, flags, protocol_version(), size); + builder.WriteUInt32(headers.stream_id()); + if (protocol_version() < 3) { + builder.WriteUInt16(0); // Unused. + } + DCHECK_EQ(headers_size_before_name_value_block, builder.length()); + + SerializeNameValueBlock(&builder, headers); + DCHECK_EQ(size, builder.length()); + + return builder.take(); +} + SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate( SpdyStreamId stream_id, uint32 delta_window_size) const { - DCHECK_GT(stream_id, 0u); - DCHECK_EQ(0u, stream_id & ~kStreamIdMask); - DCHECK_GT(delta_window_size, 0u); - DCHECK_LE(delta_window_size, - static_cast<uint32>(kSpdyStreamMaximumWindowSize)); - - size_t frame_size = SpdyWindowUpdateControlFrame::size(); - SpdyFrameBuilder frame(WINDOW_UPDATE, CONTROL_FLAG_NONE, spdy_version_, - frame_size); - frame.WriteUInt32(stream_id); - frame.WriteUInt32(delta_window_size); - DCHECK_EQ(frame.length(), frame_size); - return reinterpret_cast<SpdyWindowUpdateControlFrame*>(frame.take()); + SpdyWindowUpdateIR window_update(stream_id, delta_window_size); + return reinterpret_cast<SpdyWindowUpdateControlFrame*>( + SerializeWindowUpdate(window_update)); +} + +SpdySerializedFrame* SpdyFramer::SerializeWindowUpdate( + const SpdyWindowUpdateIR& window_update) const { + // Size, in bytes, of this WINDOW_UPDATE frame. Calculated as: + // 8 (control frame header) + 4 (stream id) + 4 (delta) + const size_t kWindowUpdateSize = 16; + + SpdyFrameBuilder builder(WINDOW_UPDATE, + kNoFlags, + protocol_version(), + kWindowUpdateSize); + builder.WriteUInt32(window_update.stream_id()); + builder.WriteUInt32(window_update.delta()); + DCHECK_EQ(kWindowUpdateSize, builder.length()); + return builder.take(); } +// TODO(hkhalil): Gut with SpdyCredential removal. SpdyCredentialControlFrame* SpdyFramer::CreateCredentialFrame( const SpdyCredential& credential) const { - // Calculate the size of the frame by adding the size of the - // variable length data to the size of the fixed length data. - size_t frame_size = SpdyCredentialControlFrame::size() + - credential.proof.length(); - DCHECK_EQ(SpdyCredentialControlFrame::size(), 14u); + SpdyCredentialIR credential_ir(credential.slot); + credential_ir.set_proof(credential.proof); for (std::vector<std::string>::const_iterator cert = credential.certs.begin(); cert != credential.certs.end(); ++cert) { - frame_size += sizeof(uint32); // size of the cert_length field - frame_size += cert->length(); // size of the cert_data field + credential_ir.AddCertificate(*cert); } + return reinterpret_cast<SpdyCredentialControlFrame*>( + SerializeCredential(credential_ir)); +} - SpdyFrameBuilder frame(CREDENTIAL, CONTROL_FLAG_NONE, spdy_version_, - frame_size); - frame.WriteUInt16(credential.slot); - frame.WriteUInt32(credential.proof.size()); - frame.WriteBytes(credential.proof.c_str(), credential.proof.size()); - for (std::vector<std::string>::const_iterator cert = credential.certs.begin(); - cert != credential.certs.end(); - ++cert) { - frame.WriteUInt32(cert->length()); - frame.WriteBytes(cert->c_str(), cert->length()); +SpdySerializedFrame* SpdyFramer::SerializeCredential( + const SpdyCredentialIR& credential) const { + size_t size = 8; // Room for frame header. + size += 2; // Room for slot. + size += 4 + credential.proof().length(); // Room for proof. + for (SpdyCredentialIR::CertificateList::const_iterator it = + credential.certificates()->begin(); + it != credential.certificates()->end(); + ++it) { + size += 4 + it->length(); // Room for certificate. + } + + SpdyFrameBuilder builder(CREDENTIAL, 0, protocol_version(), size); + builder.WriteUInt16(credential.slot()); + builder.WriteStringPiece32(credential.proof()); + for (SpdyCredentialIR::CertificateList::const_iterator it = + credential.certificates()->begin(); + it != credential.certificates()->end(); + ++it) { + builder.WriteStringPiece32(*it); } - DCHECK_EQ(frame.length(), frame_size); - return reinterpret_cast<SpdyCredentialControlFrame*>(frame.take()); + DCHECK_EQ(size, builder.length()); + return builder.take(); } SpdyDataFrame* SpdyFramer::CreateDataFrame( - SpdyStreamId stream_id, - const char* data, + SpdyStreamId stream_id, const char* data, uint32 len, SpdyDataFlags flags) const { - DCHECK_EQ(0u, stream_id & ~kStreamIdMask); - size_t frame_size = SpdyDataFrame::size() + len; - SpdyFrameBuilder frame(stream_id, flags, frame_size); - frame.WriteBytes(data, len); - DCHECK_EQ(frame.length(), frame_size); - return reinterpret_cast<SpdyDataFrame*>(frame.take()); + DCHECK_EQ(0, flags & (!DATA_FLAG_FIN)); + + SpdyDataIR data_ir(stream_id, base::StringPiece(data, len)); + data_ir.set_fin(flags & DATA_FLAG_FIN); + return reinterpret_cast<SpdyDataFrame*>(SerializeData(data_ir)); +} + +SpdySerializedFrame* SpdyFramer::SerializeData(const SpdyDataIR& data) const { + // Size, in bytes, of this DATA frame. Calculated as: + // 4 (stream id) + 1 (flags) + 3 (length) + payload length + const size_t size = 8 + data.data().length(); + + SpdyDataFlags flags = DATA_FLAG_NONE; + if (data.fin()) { + flags = DATA_FLAG_FIN; + } + + SpdyFrameBuilder builder(data.stream_id(), flags, size); + builder.WriteBytes(data.data().data(), data.data().length()); + DCHECK_EQ(size, builder.length()); + return builder.take(); } // The following compression setting are based on Brian Olson's analysis. See @@ -1912,8 +2068,30 @@ SpdyStreamId SpdyFramer::GetControlFrameStreamId( return stream_id; } -void SpdyFramer::set_enable_compression(bool value) { - enable_compression_ = value; +void SpdyFramer::SerializeNameValueBlock( + SpdyFrameBuilder* builder, + const SpdyFrameWithNameValueBlockIR& frame) const { + const SpdyNameValueBlock* name_value_block = &(frame.name_value_block()); + + // Serialize number of headers. + if (protocol_version() < 3) { + builder->WriteUInt16(name_value_block->size()); + } else { + builder->WriteUInt32(name_value_block->size()); + } + + // Serialize each header. + for (SpdyHeaderBlock::const_iterator it = name_value_block->begin(); + it != name_value_block->end(); + ++it) { + if (protocol_version() < 3) { + builder->WriteString(it->first); + builder->WriteString(it->second); + } else { + builder->WriteStringPiece32(it->first); + builder->WriteStringPiece32(it->second); + } + } } } // namespace net diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index a97eddb..436ee6c 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -44,6 +44,10 @@ class TestSpdyVisitor; } // namespace test +// A datastructure for holding a set of headers from either a +// SYN_STREAM or SYN_REPLY frame. +typedef std::map<std::string, std::string> SpdyHeaderBlock; + // A datastructure for holding the ID and flag fields for SETTINGS. // Conveniently handles converstion to/from wire format. class NET_EXPORT_PRIVATE SettingsFlagsAndId { @@ -72,6 +76,7 @@ typedef std::pair<SpdySettingsFlags, uint32> SettingsFlagsAndValue; typedef std::map<SpdySettingsIds, SettingsFlagsAndValue> SettingsMap; // A datastrcture for holding the contents of a CREDENTIAL frame. +// TODO(hkhalil): Remove, use SpdyCredentialIR instead. struct NET_EXPORT_PRIVATE SpdyCredential { SpdyCredential(); ~SpdyCredential(); @@ -299,6 +304,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { const SpdyHeaderBlock* headers); // Retrieve serialized length of SpdyHeaderBlock. + // TODO(hkhalil): Change to const reference instead of const pointer. static size_t GetSerializedLength(const int spdy_version, const SpdyHeaderBlock* headers); @@ -360,6 +366,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers); + SpdySerializedFrame* SerializeSynStream(const SpdySynStreamIR& syn_stream); // Create a SpdySynReplyControlFrame. // |stream_id| is the stream for this frame. @@ -371,17 +378,22 @@ class NET_EXPORT_PRIVATE SpdyFramer { SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers); + SpdySerializedFrame* SerializeSynReply(const SpdySynReplyIR& syn_reply); SpdyRstStreamControlFrame* CreateRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) const; + SpdySerializedFrame* SerializeRstStream( + const SpdyRstStreamIR& rst_stream) const; // Creates an instance of SpdySettingsControlFrame. The SETTINGS frame is // used to communicate name/value pairs relevant to the communication channel. SpdySettingsControlFrame* CreateSettings(const SettingsMap& values) const; + SpdySerializedFrame* SerializeSettings(const SpdySettingsIR& settings) const; // Creates an instance of SpdyPingControlFrame. The unique_id is used to // identify the ping request/response. SpdyPingControlFrame* CreatePingFrame(uint32 unique_id) const; + SpdySerializedFrame* SerializePing(const SpdyPingIR& ping) const; // Creates an instance of SpdyGoAwayControlFrame. The GOAWAY frame is used // prior to the shutting down of the TCP connection, and includes the @@ -389,6 +401,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { // to completion. SpdyGoAwayControlFrame* CreateGoAway(SpdyStreamId last_accepted_stream_id, SpdyGoAwayStatus status) const; + SpdySerializedFrame* SerializeGoAway(const SpdyGoAwayIR& goaway) const; // Creates an instance of SpdyHeadersControlFrame. The HEADERS frame is used // for sending additional headers outside of a SYN_STREAM/SYN_REPLY. The @@ -397,18 +410,23 @@ class NET_EXPORT_PRIVATE SpdyFramer { SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers); + SpdySerializedFrame* SerializeHeaders(const SpdyHeadersIR& headers); // Creates an instance of SpdyWindowUpdateControlFrame. The WINDOW_UPDATE // frame is used to implement per stream flow control in SPDY. SpdyWindowUpdateControlFrame* CreateWindowUpdate( SpdyStreamId stream_id, uint32 delta_window_size) const; + SpdySerializedFrame* SerializeWindowUpdate( + const SpdyWindowUpdateIR& window_update) const; // Creates an instance of SpdyCredentialControlFrame. The CREDENTIAL // frame is used to send a client certificate to the server when // request more than one origin are sent over the same SPDY session. SpdyCredentialControlFrame* CreateCredentialFrame( const SpdyCredential& credential) const; + SpdySerializedFrame* SerializeCredential( + const SpdyCredentialIR& credential) const; // Given a SpdySettingsControlFrame, extract the settings. // Returns true on successful parse, false otherwise. @@ -430,6 +448,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { // To mark this frame as the last data frame, enable DATA_FLAG_FIN. SpdyDataFrame* CreateDataFrame(SpdyStreamId stream_id, const char* data, uint32 len, SpdyDataFlags flags) const; + SpdySerializedFrame* SerializeData(const SpdyDataIR& data) const; // NOTES about frame compression. // We want spdy to compress headers across the entire session. As long as @@ -467,7 +486,9 @@ class NET_EXPORT_PRIVATE SpdyFramer { const SpdyControlFrame* control_frame); // For ease of testing and experimentation we can tweak compression on/off. - void set_enable_compression(bool value); + void set_enable_compression(bool value) { + enable_compression_ = value; + } // Used only in log messages. void set_display_protocol(const std::string& protocol) { @@ -560,6 +581,10 @@ class NET_EXPORT_PRIVATE SpdyFramer { void WriteHeaderBlockToZ(const SpdyHeaderBlock* headers, z_stream* out) const; + void SerializeNameValueBlock( + SpdyFrameBuilder* builder, + const SpdyFrameWithNameValueBlockIR& frame) const; + // Set the error code and moves the framer into the error state. void set_error(SpdyError error); diff --git a/net/spdy/spdy_protocol.cc b/net/spdy/spdy_protocol.cc new file mode 100644 index 0000000..9a43c0d --- /dev/null +++ b/net/spdy/spdy_protocol.cc @@ -0,0 +1,24 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/spdy/spdy_protocol.h" + +namespace net { + +SpdyFrameWithNameValueBlockIR::SpdyFrameWithNameValueBlockIR( + SpdyStreamId stream_id) : SpdyFrameWithFinIR(stream_id) {} + +SpdyFrameWithNameValueBlockIR::~SpdyFrameWithNameValueBlockIR() {} + +SpdySettingsIR::SpdySettingsIR() : clear_settings_(false) {} + +SpdySettingsIR::~SpdySettingsIR() {} + +SpdyCredentialIR::SpdyCredentialIR(int16 slot) { + set_slot(slot); +} + +SpdyCredentialIR::~SpdyCredentialIR() {} + +} // namespace diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index 0647ea1..e9faa24 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h @@ -8,9 +8,12 @@ #define NET_SPDY_SPDY_PROTOCOL_H_ #include <limits> +#include <map> +#include <vector> #include "base/basictypes.h" #include "base/logging.h" +#include "base/string_piece.h" #include "base/sys_byteorder.h" #include "net/spdy/spdy_bitmasks.h" @@ -355,6 +358,7 @@ const int kV3DictionarySize = arraysize(kV3Dictionary); // Note: all protocol data structures are on-the-wire format. That means that // data is stored in network-normalized order. Readers must use the // accessors provided or call ntohX() functions. +// TODO(hkhalil): remove above note. // Types of Spdy Control Frames. enum SpdyControlType { @@ -445,6 +449,12 @@ typedef uint32 SpdyStreamId; // number between 0 and 3. typedef uint8 SpdyPriority; +typedef uint8 SpdyCredentialSlot; + +typedef std::map<std::string, std::string> SpdyNameValueBlock; + +typedef uint32 SpdyPingId; + // ------------------------------------------------------------------------- // These structures mirror the protocol structure definitions. @@ -532,6 +542,298 @@ struct SpdyWindowUpdateControlFrameBlock : SpdyFrameBlock { #pragma pack(pop) +class SpdyFrame; +typedef SpdyFrame SpdySerializedFrame; + +class SpdyFramer; +class SpdyFrameBuilder; + +// Intermediate representation for SPDY frames. +// TODO(hkhalil): Rename this class to SpdyFrame when the existing SpdyFrame is +// gone. +class SpdyFrameIR { + public: + virtual ~SpdyFrameIR() {} + + protected: + SpdyFrameIR() {} + + private: + DISALLOW_COPY_AND_ASSIGN(SpdyFrameIR); +}; + +// Abstract class intended to be inherited by IRs that have a stream associated +// to them. +class SpdyFrameWithStreamIdIR : public SpdyFrameIR { + public: + virtual ~SpdyFrameWithStreamIdIR() {} + SpdyStreamId stream_id() const { return stream_id_; } + void set_stream_id(SpdyStreamId stream_id) { + // TODO(hkhalil): DCHECK_LT(0u, stream_id); + DCHECK_EQ(0u, stream_id & ~kStreamIdMask); + stream_id_ = stream_id; + } + + protected: + explicit SpdyFrameWithStreamIdIR(SpdyStreamId stream_id) { + set_stream_id(stream_id); + } + + private: + SpdyStreamId stream_id_; + + DISALLOW_COPY_AND_ASSIGN(SpdyFrameWithStreamIdIR); +}; + +// Abstract class intended to be inherited by IRs that have the option of a FIN +// flag. Implies SpdyFrameWithStreamIdIR. +class SpdyFrameWithFinIR : public SpdyFrameWithStreamIdIR { + public: + virtual ~SpdyFrameWithFinIR() {} + bool fin() const { return fin_; } + void set_fin(bool fin) { fin_ = fin; } + + protected: + explicit SpdyFrameWithFinIR(SpdyStreamId stream_id) + : SpdyFrameWithStreamIdIR(stream_id), + fin_(false) {} + + private: + bool fin_; + + DISALLOW_COPY_AND_ASSIGN(SpdyFrameWithFinIR); +}; + +// Abstract class intended to be inherited by IRs that contain a name-value +// block. Implies SpdyFrameWithFinIR. +class SpdyFrameWithNameValueBlockIR : public SpdyFrameWithFinIR { + public: + const SpdyNameValueBlock& name_value_block() const { + return name_value_block_; + } + SpdyNameValueBlock* GetMutableNameValueBlock() { return &name_value_block_; } + + protected: + explicit SpdyFrameWithNameValueBlockIR(SpdyStreamId stream_id); + virtual ~SpdyFrameWithNameValueBlockIR(); + + private: + SpdyNameValueBlock name_value_block_; + + DISALLOW_COPY_AND_ASSIGN(SpdyFrameWithNameValueBlockIR); +}; + +class SpdyDataIR : public SpdyFrameWithFinIR { + public: + SpdyDataIR(SpdyStreamId stream_id, const base::StringPiece& data) + : SpdyFrameWithFinIR(stream_id) { + set_data(data); + } + base::StringPiece data() const { return data_; } + void set_data(const base::StringPiece& data) { data.CopyToString(&data_); } + + private: + std::string data_; + + DISALLOW_COPY_AND_ASSIGN(SpdyDataIR); +}; + +class SpdySynStreamIR : public SpdyFrameWithNameValueBlockIR { + public: + explicit SpdySynStreamIR(SpdyStreamId stream_id) + : SpdyFrameWithNameValueBlockIR(stream_id), + associated_to_stream_id_(0), + priority_(0), + slot_(0), + unidirectional_(false) {} + SpdyStreamId associated_to_stream_id() const { + return associated_to_stream_id_; + } + void set_associated_to_stream_id(SpdyStreamId stream_id) { + associated_to_stream_id_ = stream_id; + } + SpdyPriority priority() const { return priority_; } + void set_priority(SpdyPriority priority) { priority_ = priority; } + SpdyCredentialSlot slot() const { return slot_; } + void set_slot(SpdyCredentialSlot slot) { slot_ = slot; } + bool unidirectional() const { return unidirectional_; } + void set_unidirectional(bool unidirectional) { + unidirectional_ = unidirectional; + } + + private: + SpdyStreamId associated_to_stream_id_; + SpdyPriority priority_; + SpdyCredentialSlot slot_; + bool unidirectional_; + + DISALLOW_COPY_AND_ASSIGN(SpdySynStreamIR); +}; + +class SpdySynReplyIR : public SpdyFrameWithNameValueBlockIR { + public: + explicit SpdySynReplyIR(SpdyStreamId stream_id) + : SpdyFrameWithNameValueBlockIR(stream_id) {} + + private: + DISALLOW_COPY_AND_ASSIGN(SpdySynReplyIR); +}; + +class SpdyRstStreamIR : public SpdyFrameWithStreamIdIR { + public: + SpdyRstStreamIR(SpdyStreamId stream_id, SpdyRstStreamStatus status) + : SpdyFrameWithStreamIdIR(stream_id) { + set_status(status); + } + SpdyRstStreamStatus status() const { + return status_; + } + void set_status(SpdyRstStreamStatus status) { + DCHECK_NE(status, RST_STREAM_INVALID); + DCHECK_LT(status, RST_STREAM_NUM_STATUS_CODES); + status_ = status; + } + + private: + SpdyRstStreamStatus status_; + + DISALLOW_COPY_AND_ASSIGN(SpdyRstStreamIR); +}; + +class SpdySettingsIR : public SpdyFrameIR { + public: + // Associates flags with a value. + struct Value { + Value() : persist_value(false), + persisted(false), + value(0) {} + bool persist_value; + bool persisted; + int32 value; + }; + typedef std::map<SpdySettingsIds, Value> ValueMap; + + SpdySettingsIR(); + + virtual ~SpdySettingsIR(); + + // Overwrites as appropriate. + const ValueMap& values() const { return values_; } + void AddSetting(SpdySettingsIds id, + bool persist_value, + bool persisted, + int32 value) { + // TODO(hkhalil): DCHECK_LE(SETTINGS_UPLOAD_BANDWIDTH, id); + // TODO(hkhalil): DCHECK_GE(SETTINGS_INITIAL_WINDOW_SIZE, id); + values_[id].persist_value = persist_value; + values_[id].persisted = persisted; + values_[id].value = value; + } + bool clear_settings() const { return clear_settings_; } + void set_clear_settings(bool clear_settings) { + clear_settings_ = clear_settings; + } + + private: + ValueMap values_; + bool clear_settings_; + + DISALLOW_COPY_AND_ASSIGN(SpdySettingsIR); +}; + +class SpdyPingIR : public SpdyFrameIR { + public: + explicit SpdyPingIR(SpdyPingId id) : id_(id) {} + SpdyPingId id() const { return id_; } + + private: + SpdyPingId id_; + + DISALLOW_COPY_AND_ASSIGN(SpdyPingIR); +}; + +class SpdyGoAwayIR : public SpdyFrameIR { + public: + SpdyGoAwayIR(SpdyStreamId last_good_stream_id, SpdyGoAwayStatus status) { + set_last_good_stream_id(last_good_stream_id); + set_status(status); + } + SpdyStreamId last_good_stream_id() const { return last_good_stream_id_; } + void set_last_good_stream_id(SpdyStreamId last_good_stream_id) { + DCHECK_LE(0u, last_good_stream_id); + DCHECK_EQ(0u, last_good_stream_id & ~kStreamIdMask); + last_good_stream_id_ = last_good_stream_id; + } + SpdyGoAwayStatus status() const { return status_; } + void set_status(SpdyGoAwayStatus status) { + // TODO(hkhalil): Check valid ranges of status? + status_ = status; + } + + private: + SpdyStreamId last_good_stream_id_; + SpdyGoAwayStatus status_; + + DISALLOW_COPY_AND_ASSIGN(SpdyGoAwayIR); +}; + +class SpdyHeadersIR : public SpdyFrameWithNameValueBlockIR { + public: + explicit SpdyHeadersIR(SpdyStreamId stream_id) + : SpdyFrameWithNameValueBlockIR(stream_id) {} + + private: + DISALLOW_COPY_AND_ASSIGN(SpdyHeadersIR); +}; + +class SpdyWindowUpdateIR : public SpdyFrameWithStreamIdIR { + public: + SpdyWindowUpdateIR(SpdyStreamId stream_id, int32 delta) + : SpdyFrameWithStreamIdIR(stream_id) { + set_delta(delta); + } + int32 delta() const { return delta_; } + void set_delta(int32 delta) { + DCHECK_LT(0, delta); + DCHECK_LE(delta, kSpdyStreamMaximumWindowSize); + delta_ = delta; + } + + private: + int32 delta_; + + DISALLOW_COPY_AND_ASSIGN(SpdyWindowUpdateIR); +}; + +class SpdyCredentialIR : public SpdyFrameIR { + public: + typedef std::vector<std::string> CertificateList; + + explicit SpdyCredentialIR(int16 slot); + virtual ~SpdyCredentialIR(); + + int16 slot() const { return slot_; } + void set_slot(int16 slot) { + // TODO(hkhalil): Verify valid slot range? + slot_ = slot; + } + base::StringPiece proof() const { return proof_; } + void set_proof(const base::StringPiece& proof) { + proof.CopyToString(&proof_); + } + const CertificateList* certificates() const { return &certificates_; } + void AddCertificate(const base::StringPiece& certificate) { + certificates_.push_back(certificate.as_string()); + } + + private: + int16 slot_; + std::string proof_; + CertificateList certificates_; + + DISALLOW_COPY_AND_ASSIGN(SpdyCredentialIR); +}; + // ------------------------------------------------------------------------- // Wrapper classes for various SPDY frames. @@ -731,7 +1033,7 @@ class SpdySynStreamControlFrame : public SpdyControlFrame { } void set_credential_slot(uint8 credential_slot) { - DCHECK(version() >= 3); + DCHECK_LE(3, version()); mutable_block()->credential_slot_ = credential_slot; } |