diff options
author | mbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-02 22:24:45 +0000 |
---|---|---|
committer | mbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-02 22:24:45 +0000 |
commit | 4e1d0347528d3be39ff4854a1b9268accb001282 (patch) | |
tree | 3f0a617f0071096e45be3a1d2890e7691c542d37 /net/spdy | |
parent | 4cccc224e222ceeb765ce93d5bb40223b210c472 (diff) | |
download | chromium_src-4e1d0347528d3be39ff4854a1b9268accb001282.zip chromium_src-4e1d0347528d3be39ff4854a1b9268accb001282.tar.gz chromium_src-4e1d0347528d3be39ff4854a1b9268accb001282.tar.bz2 |
Implement protocol definitions for the SPDY SETTINGS frame (previously
labeled the HELLO frame).
BUG=none
TEST=SpdyProtocolTest.
Review URL: http://codereview.chromium.org/1569018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43535 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/spdy')
-rw-r--r-- | net/spdy/spdy_bitmasks.h | 3 | ||||
-rw-r--r-- | net/spdy/spdy_framer.cc | 39 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 14 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.h | 82 | ||||
-rw-r--r-- | net/spdy/spdy_protocol_test.cc | 84 |
5 files changed, 200 insertions, 22 deletions
diff --git a/net/spdy/spdy_bitmasks.h b/net/spdy/spdy_bitmasks.h index 0a7351f..40403e1 100644 --- a/net/spdy/spdy_bitmasks.h +++ b/net/spdy/spdy_bitmasks.h @@ -19,6 +19,9 @@ const unsigned int kPriorityMask = 0xc0; // Mask the lower 24 bits. const unsigned int kLengthMask = 0xffffff; +// Mask the Id from a SETTINGS id. +const unsigned int kSettingsIdMask = 0xffffff; + } // namespace spdy #endif // NET_SPDY_SPDY_BITMASKS_H_ diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 2d9ea98..864a0d9 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -478,6 +478,26 @@ bool SpdyFramer::ParseHeaderBlock(const SpdyFrame* frame, return false; } +/* static */ +bool SpdyFramer::ParseSettings(const SpdySettingsControlFrame* frame, + SpdySettings* settings) { + DCHECK_EQ(frame->type(), SETTINGS); + DCHECK(settings); + + SpdyFrameBuilder parser(frame->header_block(), frame->header_block_len()); + void* iter = NULL; + for (size_t index = 0; index < frame->num_entries(); ++index) { + uint32 id; + uint32 value; + if (!parser.ReadUInt32(&iter, &id)) + return false; + if (!parser.ReadUInt32(&iter, &value)) + return false; + settings->insert(settings->end(), std::make_pair(id, value)); + } + return true; +} + SpdySynStreamControlFrame* SpdyFramer::CreateSynStream( SpdyStreamId stream_id, SpdyStreamId associated_stream_id, int priority, SpdyControlFlags flags, bool compressed, SpdyHeaderBlock* headers) { @@ -537,6 +557,25 @@ SpdyGoAwayControlFrame* SpdyFramer::CreateGoAway( return reinterpret_cast<SpdyGoAwayControlFrame*>(frame.take()); } +/* static */ +SpdySettingsControlFrame* SpdyFramer::CreateSettings( + const SpdySettings& values) { + SpdyFrameBuilder frame; + frame.WriteUInt16(kControlFlagMask | kSpdyProtocolVersion); + frame.WriteUInt16(SETTINGS); + size_t settings_size = SpdySettingsControlFrame::size() - SpdyFrame::size() + + 8 * values.size(); + frame.WriteUInt32(settings_size); + frame.WriteUInt32(values.size()); + SpdySettings::const_iterator it = values.begin(); + while (it != values.end()) { + frame.WriteUInt32(it->first.id_); + frame.WriteUInt32(it->second); + ++it; + } + return reinterpret_cast<SpdySettingsControlFrame*>(frame.take()); +} + SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(SpdyStreamId stream_id, SpdyControlFlags flags, bool compressed, SpdyHeaderBlock* headers) { diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index c97ba682..c050ac73 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -10,6 +10,7 @@ #else #include <arpa/inet.h> #endif +#include <list> #include <map> #include <string> @@ -40,6 +41,9 @@ void FramerSetEnableCompressionHelper(SpdyFramer* framer, bool compress); // SYN_STREAM or SYN_REPLY frame. typedef std::map<std::string, std::string> SpdyHeaderBlock; +// A datastructure for holding a set of ID/value pairs for a SETTINGS frame. +typedef std::list<std::pair<spdy::SettingsFlagsAndId, uint32> > SpdySettings; + // SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer. // Implement this interface to receive event callbacks as frames are // decoded from the framer. @@ -155,6 +159,16 @@ class SpdyFramer { static SpdyGoAwayControlFrame* CreateGoAway( SpdyStreamId last_accepted_stream_id); + // Creates an instance of SpdySettingsControlFrame. The SETTINGS frame is + // used to communicate name/value pairs relevant to the communication channel. + // TODO(mbelshe): add the name/value pairs!! + static SpdySettingsControlFrame* CreateSettings(const SpdySettings& values); + + // Given a SpdySettingsControlFrame, extract the settings. + // Returns true on successful parse, false otherwise. + static bool ParseSettings(const SpdySettingsControlFrame* frame, + SpdySettings* settings); + // Create a SpdySynReplyControlFrame. // |stream_id| is the stream for this frame. // |flags| is the flags to use with the data. diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index 5abb202..c8d5830 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h @@ -70,13 +70,13 @@ // | Status code (32 bits) | // +----------------------------------+ // -// Control Frame: HELLO +// Control Frame: SETTINGS // +----------------------------------+ // |1|000000000000001|0000000000000100| // +----------------------------------+ // | flags (8) | Length (24 bits) | // +----------------------------------+ -// | Unused |# of entries (16)| +// | # of entries (32) | // +----------------------------------+ // // Control Frame: NOOP @@ -118,7 +118,7 @@ enum SpdyControlType { SYN_STREAM = 1, SYN_REPLY, RST_STREAM, - HELLO, + SETTINGS, NOOP, PING, GOAWAY, @@ -126,7 +126,7 @@ enum SpdyControlType { NUM_CONTROL_FRAME_TYPES }; -// Flags on data packets +// Flags on data packets. enum SpdyDataFlags { DATA_FLAG_NONE = 0, DATA_FLAG_FIN = 1, @@ -140,6 +140,26 @@ enum SpdyControlFlags { CONTROL_FLAG_UNIDIRECTIONAL = 2 }; +// Flags on the SETTINGS control frame. +enum SpdySettingsControlFlags { + SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS = 0x1 +}; + +// Flags for settings within a SETTINGS frame. +enum SpdySettingsFlags { + SETTINGS_FLAG_PLEASE_PERSIST = 0x1, + SETTINGS_FLAG_PERSISTED = 0x2 +}; + +// List of known settings. +enum SpdySettingsIds { + SETTINGS_UPLOAD_BANDWIDTH = 0x1, + SETTINGS_DOWNLOAD_BANDWIDTH = 0x2, + SETTINGS_ROUND_TRIP_TIME = 0x3, + SETTINGS_MAX_CONCURRENT_STREAMS = 0x4, + SETTINGS_CURRENT_CWND = 0x5 +}; + // Status codes, as used in control frames (primarily RST_STREAM). enum SpdyStatusCodes { INVALID = 0, @@ -209,11 +229,27 @@ struct SpdyRstStreamControlFrameBlock : SpdyFrameBlock { uint32 status_; }; -// A GOAWAY Control Frame structure +// A GOAWAY Control Frame structure. struct SpdyGoAwayControlFrameBlock : SpdyFrameBlock { SpdyStreamId last_accepted_stream_id_; }; +// A structure for the 8 bit flags and 24 bit ID fields. +union SettingsFlagsAndId { + uint8 flags_[4]; // 8 bits + uint32 id_; // 24 bits + + SettingsFlagsAndId(uint32 val) : id_(val) {}; + uint8 flags() const { return flags_[0]; } + uint32 id() const { return (ntohl(id_) & kSettingsIdMask); }; +}; + +// A SETTINGS Control Frame structure. +struct SpdySettingsControlFrameBlock : SpdyFrameBlock { + uint32 num_entries_; + // Variable data here. +}; + #pragma pack(pop) // ------------------------------------------------------------------------- @@ -510,6 +546,42 @@ class SpdyGoAwayControlFrame : public SpdyControlFrame { DISALLOW_COPY_AND_ASSIGN(SpdyGoAwayControlFrame); }; +class SpdySettingsControlFrame : public SpdyControlFrame { + public: + SpdySettingsControlFrame() : SpdyControlFrame(size()) {} + SpdySettingsControlFrame(char* data, bool owns_buffer) + : SpdyControlFrame(data, owns_buffer) {} + + SpdyStreamId num_entries() const { + return ntohl(block()->num_entries_); + } + + void set_num_entries(int val) { + mutable_block()->num_entries_ = htonl(val); + } + + int header_block_len() const { + return length() - (size() - SpdyFrame::size()); + } + + const char* header_block() const { + return reinterpret_cast<const char*>(block()) + size(); + } + + // Returns the size of the SpdySettingsControlFrameBlock structure. + // Note: this is not the size of the SpdySettingsControlFrameBlock class. + static size_t size() { return sizeof(SpdySettingsControlFrameBlock); } + + private: + const struct SpdySettingsControlFrameBlock* block() const { + return static_cast<SpdySettingsControlFrameBlock*>(frame_); + } + struct SpdySettingsControlFrameBlock* mutable_block() { + return static_cast<SpdySettingsControlFrameBlock*>(frame_); + } + DISALLOW_COPY_AND_ASSIGN(SpdySettingsControlFrame); +}; + } // namespace spdy #endif // NET_SPDY_SPDY_PROTOCOL_H_ diff --git a/net/spdy/spdy_protocol_test.cc b/net/spdy/spdy_protocol_test.cc index 128ff47..4a14575 100644 --- a/net/spdy/spdy_protocol_test.cc +++ b/net/spdy/spdy_protocol_test.cc @@ -9,26 +9,30 @@ #include "net/spdy/spdy_framer.h" #include "testing/platform_test.h" -using spdy::SpdyDataFrame; -using spdy::SpdyFrame; +using spdy::CONTROL_FLAG_FIN; +using spdy::CONTROL_FLAG_NONE; +using spdy::GOAWAY; +using spdy::RST_STREAM; +using spdy::SETTINGS; +using spdy::SYN_REPLY; +using spdy::SYN_STREAM; +using spdy::FlagsAndLength; using spdy::SpdyControlFrame; using spdy::SpdyControlType; -using spdy::SpdyGoAwayControlFrame; -using spdy::SpdySynStreamControlFrame; -using spdy::SpdySynReplyControlFrame; -using spdy::SpdyRstStreamControlFrame; +using spdy::SpdyDataFrame; +using spdy::SpdyFrame; using spdy::SpdyFramer; using spdy::SpdyHeaderBlock; -using spdy::FlagsAndLength; +using spdy::SpdyGoAwayControlFrame; +using spdy::SpdyRstStreamControlFrame; +using spdy::SpdySettings; +using spdy::SpdySettingsControlFrame; +using spdy::SpdySynReplyControlFrame; +using spdy::SpdySynStreamControlFrame; +using spdy::SettingsFlagsAndId; using spdy::kLengthMask; -using spdy::kStreamIdMask; using spdy::kSpdyProtocolVersion; -using spdy::GOAWAY; -using spdy::SYN_STREAM; -using spdy::SYN_REPLY; -using spdy::RST_STREAM; -using spdy::CONTROL_FLAG_FIN; -using spdy::CONTROL_FLAG_NONE; +using spdy::kStreamIdMask; namespace { @@ -41,10 +45,12 @@ TEST(SpdyProtocolTest, ProtocolConstants) { EXPECT_EQ(14u, SpdySynReplyControlFrame::size()); EXPECT_EQ(16u, SpdyRstStreamControlFrame::size()); EXPECT_EQ(12u, SpdyGoAwayControlFrame::size()); + EXPECT_EQ(12u, SpdySettingsControlFrame::size()); EXPECT_EQ(4u, sizeof(FlagsAndLength)); EXPECT_EQ(1, SYN_STREAM); EXPECT_EQ(2, SYN_REPLY); EXPECT_EQ(3, RST_STREAM); + EXPECT_EQ(4, SETTINGS); EXPECT_EQ(7, GOAWAY); } @@ -158,6 +164,53 @@ TEST(SpdyProtocolTest, TestDataFrame) { EXPECT_EQ(length, frame.length()); } +// Test various types of SETTINGS frames. +TEST(SpdyProtocolTest, TestSpdySettingsFrame) { + SpdyFramer framer; + + // Create a settings frame with no settings. + SpdySettings settings; + scoped_ptr<SpdySettingsControlFrame> settings_frame( + framer.CreateSettings(settings)); + EXPECT_EQ(kSpdyProtocolVersion, settings_frame->version()); + EXPECT_TRUE(settings_frame->is_control_frame()); + EXPECT_EQ(SETTINGS, settings_frame->type()); + EXPECT_EQ(0u, settings_frame->num_entries()); + + // We'll add several different ID/Flag combinations and then verify + // that they encode and decode properly. + SettingsFlagsAndId ids[] = { + 0x00000000, + 0xffffffff, + 0xff000001, + 0x01000002, + }; + + for (size_t index = 0; index < arraysize(ids); ++index) { + settings.insert(settings.end(), std::make_pair(ids[index], index)); + settings_frame.reset(framer.CreateSettings(settings)); + EXPECT_EQ(kSpdyProtocolVersion, settings_frame->version()); + EXPECT_TRUE(settings_frame->is_control_frame()); + EXPECT_EQ(SETTINGS, settings_frame->type()); + EXPECT_EQ(index + 1, settings_frame->num_entries()); + + SpdySettings parsed_settings; + EXPECT_TRUE(framer.ParseSettings(settings_frame.get(), &parsed_settings)); + EXPECT_EQ(parsed_settings.size(), settings.size()); + SpdySettings::const_iterator it = parsed_settings.begin(); + int pos = 0; + while (it != parsed_settings.end()) { + SettingsFlagsAndId parsed = it->first; + uint32 value = it->second; + EXPECT_EQ(parsed.flags(), ids[pos].flags()); + EXPECT_EQ(parsed.id(), ids[pos].id()); + EXPECT_EQ(value, static_cast<uint32>(pos)); + ++it; + ++pos; + } + } +} + // Make sure that overflows both die in debug mode, and do not cause problems // in opt mode. Note: The EXPECT_DEBUG_DEATH call does not work on Win32 yet, // so we comment it out. @@ -233,7 +286,4 @@ TEST(SpdyProtocolDeathTest, TestSpdyControlFrameType) { } } - - - } // namespace |