summaryrefslogtreecommitdiffstats
path: root/net/spdy
diff options
context:
space:
mode:
authormbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-02 22:24:45 +0000
committermbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-02 22:24:45 +0000
commit4e1d0347528d3be39ff4854a1b9268accb001282 (patch)
tree3f0a617f0071096e45be3a1d2890e7691c542d37 /net/spdy
parent4cccc224e222ceeb765ce93d5bb40223b210c472 (diff)
downloadchromium_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.h3
-rw-r--r--net/spdy/spdy_framer.cc39
-rw-r--r--net/spdy/spdy_framer.h14
-rw-r--r--net/spdy/spdy_protocol.h82
-rw-r--r--net/spdy/spdy_protocol_test.cc84
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