Skip to content

Commit

Permalink
Implement protocol definitions for the SPDY SETTINGS frame (previously
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mbelshe@chromium.org committed Apr 2, 2010
1 parent 4cccc22 commit 4e1d034
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 22 deletions.
3 changes: 3 additions & 0 deletions net/spdy/spdy_bitmasks.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_
39 changes: 39 additions & 0 deletions net/spdy/spdy_framer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {

Expand Down
14 changes: 14 additions & 0 deletions net/spdy/spdy_framer.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#else
#include <arpa/inet.h>
#endif
#include <list>
#include <map>
#include <string>

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
82 changes: 77 additions & 5 deletions net/spdy/spdy_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -118,15 +118,15 @@ enum SpdyControlType {
SYN_STREAM = 1,
SYN_REPLY,
RST_STREAM,
HELLO,
SETTINGS,
NOOP,
PING,
GOAWAY,
HEADERS,
NUM_CONTROL_FRAME_TYPES
};

// Flags on data packets
// Flags on data packets.
enum SpdyDataFlags {
DATA_FLAG_NONE = 0,
DATA_FLAG_FIN = 1,
Expand All @@ -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,
Expand Down Expand Up @@ -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)

// -------------------------------------------------------------------------
Expand Down Expand Up @@ -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_
84 changes: 67 additions & 17 deletions net/spdy/spdy_protocol_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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);
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -233,7 +286,4 @@ TEST(SpdyProtocolDeathTest, TestSpdyControlFrameType) {
}
}




} // namespace

0 comments on commit 4e1d034

Please sign in to comment.