SPDY changes from server.
R=willchan
Review URL: http://codereview.chromium.org/8473007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111854 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/spdy/spdy_frame_builder.cc b/net/spdy/spdy_frame_builder.cc
index cb8b486..ace581d 100644
--- a/net/spdy/spdy_frame_builder.cc
+++ b/net/spdy/spdy_frame_builder.cc
@@ -111,30 +111,6 @@
return ReadBytes(iter, data, *length);
}
-bool SpdyFrameBuilder::WriteString(const std::string& value) {
- if (value.size() > 0xffff)
- return false;
-
- if (!WriteUInt16(static_cast<int>(value.size())))
- return false;
-
- return WriteBytes(value.data(), static_cast<uint16>(value.size()));
-}
-
-bool SpdyFrameBuilder::WriteBytes(const void* data, uint16 data_len) {
- DCHECK(capacity_ != kCapacityReadOnly);
-
- char* dest = BeginWrite(data_len);
- if (!dest)
- return false;
-
- memcpy(dest, data, data_len);
-
- EndWrite(dest, data_len);
- length_ += data_len;
- return true;
-}
-
char* SpdyFrameBuilder::BeginWriteData(uint16 length) {
DCHECK_EQ(variable_buffer_offset_, 0U) <<
"There can only be one variable buffer in a SpdyFrameBuilder";
@@ -155,6 +131,7 @@
}
char* SpdyFrameBuilder::BeginWrite(size_t length) {
+ size_t offset = length_;
size_t needed_size = length_ + length;
if (needed_size > capacity_ && !Resize(std::max(capacity_ * 2, needed_size)))
return NULL;
@@ -163,12 +140,41 @@
DCHECK_LE(length, std::numeric_limits<uint32>::max());
#endif
- return buffer_ + length_;
+ return buffer_ + offset;
}
void SpdyFrameBuilder::EndWrite(char* dest, int length) {
}
+bool SpdyFrameBuilder::WriteBytes(const void* data, uint32 data_len) {
+ DCHECK(capacity_ != kCapacityReadOnly);
+
+ if (data_len > kLengthMask) {
+ return false;
+ }
+
+ char* dest = BeginWrite(data_len);
+ if (!dest)
+ return false;
+
+ memcpy(dest, data, data_len);
+
+ EndWrite(dest, data_len);
+ length_ += data_len;
+ return true;
+}
+
+bool SpdyFrameBuilder::WriteString(const std::string& value) {
+ if (value.size() > 0xffff)
+ return false;
+
+ if (!WriteUInt16(static_cast<int>(value.size())))
+ return false;
+
+ return WriteBytes(value.data(), static_cast<uint16>(value.size()));
+}
+
+// TODO(hkhalil) Remove Resize() entirely.
bool SpdyFrameBuilder::Resize(size_t new_capacity) {
if (new_capacity <= capacity_)
return true;
@@ -180,6 +186,8 @@
memcpy(p, buffer_, capacity_);
delete[] buffer_;
}
+ if (!p && new_capacity > 0)
+ return false;
buffer_ = p;
capacity_ = new_capacity;
return true;
diff --git a/net/spdy/spdy_frame_builder.h b/net/spdy/spdy_frame_builder.h
index b3d2de7..b53adbc 100644
--- a/net/spdy/spdy_frame_builder.h
+++ b/net/spdy/spdy_frame_builder.h
@@ -28,6 +28,8 @@
// track of the type of data written to it.
class NET_EXPORT_PRIVATE SpdyFrameBuilder {
public:
+ ~SpdyFrameBuilder();
+
SpdyFrameBuilder();
// Initiailizes a SpdyFrameBuilder with a buffer of given size.
@@ -40,8 +42,6 @@
// this way.
SpdyFrameBuilder(const char* data, int data_len);
- ~SpdyFrameBuilder();
-
// Returns the size of the SpdyFrameBuilder's data.
int length() const { return length_; }
@@ -77,7 +77,7 @@
return WriteBytes(&value, sizeof(value));
}
bool WriteString(const std::string& value);
- bool WriteBytes(const void* data, uint16 data_len);
+ bool WriteBytes(const void* data, uint32 data_len);
// Write an integer to a particular offset in the data buffer.
bool WriteUInt32ToOffset(int offset, uint32 value) {
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
index 29a1565..b0928dd 100644
--- a/net/spdy/spdy_framer.cc
+++ b/net/spdy/spdy_framer.cc
@@ -339,7 +339,10 @@
continue;
}
default:
- break;
+ // This ensures that we don't infinite-loop if state_ gets an
+ // invalid value somehow, such as due to a SpdyFramer getting deleted
+ // from a callback it calls.
+ goto bottom;
}
}
bottom:
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index abfa5234..1bbe355 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -61,7 +61,9 @@
// Called if an error is detected in the SpdyFrame protocol.
virtual void OnError(SpdyFramer* framer) = 0;
- // Called when a control frame is received.
+ // Called when a control frame is received. Note that SYN_STREAM, SYN_REPLY,
+ // and HEADER control frames do not include the header block data.
+ // (See OnControlFrameHeaderData().)
virtual void OnControl(const SpdyControlFrame* frame) = 0;
// Called when a chunk of header data is available. This is called
@@ -327,8 +329,6 @@
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ExpandBuffer_HeapSmash);
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, HugeHeaderBlock);
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, UnclosedStreamDataCompressors);
- FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest,
- UncompressLargerThanFrameBufferInitialSize);
friend class net::HttpNetworkLayer; // This is temporary for the server.
friend class net::HttpNetworkTransactionTest;
friend class net::HttpProxyClientSocketPoolTest;
@@ -343,17 +343,6 @@
friend void test::FramerSetEnableCompressionHelper(SpdyFramer* framer,
bool compress);
- // The initial size of the control frame buffer; this is used internally
- // as we parse through control frames. (It is exposed here for unit test
- // purposes.)
- // This is only used when compression is enabled; otherwise,
- // kUncompressedControlFrameBufferInitialSize is used.
- static size_t kControlFrameBufferInitialSize;
-
- // The maximum size of the control frame buffer that we support.
- // TODO(mbelshe): We should make this stream-based so there are no limits.
- static size_t kControlFrameBufferMaxSize;
-
private:
typedef std::map<SpdyStreamId, z_stream*> CompressorMap;
@@ -363,8 +352,6 @@
void ProcessControlFrameHeader();
size_t ProcessControlFramePayload(const char* data, size_t len);
size_t ProcessControlFrameBeforeHeaderBlock(const char* data, size_t len);
- // TODO(hkhalil): Rename to ProcessControlFrameHeaderBlock once said flag is
- // removed, replacing the old method.
size_t NewProcessControlFrameHeaderBlock(const char* data, size_t len);
size_t ProcessControlFrameHeaderBlock(const char* data, size_t len);
size_t ProcessDataFramePayload(const char* data, size_t len);
@@ -435,6 +422,13 @@
int num_stream_compressors() const { return stream_compressors_.size(); }
int num_stream_decompressors() const { return stream_decompressors_.size(); }
+ // The initial size of the control frame buffer; this is used internally
+ // as we parse through control frames. (It is exposed here for unit test
+ // purposes.)
+ // This is only used when compression is enabled; otherwise,
+ // kUncompressedControlFrameBufferInitialSize is used.
+ static size_t kControlFrameBufferInitialSize;
+
// The initial size of the control frame buffer when compression is disabled.
// This exists because we don't do stream (de)compressed control frame data to
// our visitor; we instead buffer the entirety of the control frame and then
@@ -448,6 +442,10 @@
// implemented.
static const size_t kUncompressedControlFrameBufferInitialSize = 18 + 16;
+ // The maximum size of the control frame buffer that we support.
+ // TODO(mbelshe): We should make this stream-based so there are no limits.
+ static size_t kControlFrameBufferMaxSize;
+
// The size of the buffer into which compressed frames are inflated.
static const size_t kDecompressionBufferSize = 8 * 1024;
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
index b17c66b..3547d95 100644
--- a/net/spdy/spdy_framer_test.cc
+++ b/net/spdy/spdy_framer_test.cc
@@ -20,6 +20,13 @@
static const char kHexChars[] = "0123456789ABCDEF";
static const int kColumns = 4;
+ const int kSizeLimit = 1024;
+ if (length > kSizeLimit || mark_length > kSizeLimit) {
+ LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes.";
+ length = std::min(length, kSizeLimit);
+ mark_length = std::min(mark_length, kSizeLimit);
+ }
+
std::string hex;
for (const unsigned char* row = data; length > 0;
row += kColumns, length -= kColumns) {
@@ -76,27 +83,44 @@
<< HexDumpWithMarks(actual, actual_len, marks.get(), max_len);
}
-void FramerSetEnableCompressionHelper(SpdyFramer* framer, bool compress) {
- framer->set_enable_compression(compress);
-}
-
class TestSpdyVisitor : public SpdyFramerVisitorInterface {
public:
+ static const size_t kDefaultHeaderBufferSize = 16 * 1024;
+
TestSpdyVisitor()
- : error_count_(0),
+ : use_compression_(false),
+ error_count_(0),
syn_frame_count_(0),
syn_reply_frame_count_(0),
headers_frame_count_(0),
+ goaway_count_(0),
data_bytes_(0),
fin_frame_count_(0),
fin_flag_count_(0),
- zero_length_data_frame_count_(0) {
+ zero_length_data_frame_count_(0),
+ header_blocks_count_(0),
+ control_frame_header_data_count_(0),
+ zero_length_control_frame_header_data_count_(0),
+ data_frame_count_(0),
+ header_buffer_(new char[kDefaultHeaderBufferSize]),
+ header_buffer_length_(0),
+ header_buffer_size_(kDefaultHeaderBufferSize),
+ header_stream_id_(-1),
+ header_control_type_(NUM_CONTROL_FRAME_TYPES),
+ header_buffer_valid_(false) {
}
void OnError(SpdyFramer* f) {
+ LOG(INFO) << "SpdyFramer Error: "
+ << SpdyFramer::ErrorCodeToString(f->error_code());
error_count_++;
}
+ void OnDataFrameHeader(const SpdyDataFrame* frame) {
+ data_frame_count_++;
+ header_stream_id_ = frame->stream_id();
+ }
+
void OnStreamFrameData(SpdyStreamId stream_id,
const char* data,
size_t len) {
@@ -114,29 +138,27 @@
}
void OnControl(const SpdyControlFrame* frame) {
- SpdyHeaderBlock headers;
- bool parsed_headers = false;
switch (frame->type()) {
case SYN_STREAM:
- parsed_headers = framer_.ParseHeaderBlock(frame, &headers);
- DCHECK(parsed_headers);
syn_frame_count_++;
+ InitHeaderStreaming(frame);
break;
case SYN_REPLY:
- parsed_headers = framer_.ParseHeaderBlock(frame, &headers);
- DCHECK(parsed_headers);
syn_reply_frame_count_++;
+ InitHeaderStreaming(frame);
break;
case RST_STREAM:
fin_frame_count_++;
break;
case HEADERS:
- parsed_headers = framer_.ParseHeaderBlock(frame, &headers);
- DCHECK(parsed_headers);
headers_frame_count_++;
+ InitHeaderStreaming(frame);
+ break;
+ case GOAWAY:
+ goaway_count_++;
break;
default:
- DCHECK(false); // Error!
+ DLOG(FATAL); // Error!
}
if (frame->flags() & CONTROL_FLAG_FIN)
++fin_flag_count_;
@@ -145,17 +167,30 @@
bool OnControlFrameHeaderData(SpdyStreamId stream_id,
const char* header_data,
size_t len) {
- DCHECK(false);
- return false;
- }
-
- void OnDataFrameHeader(const SpdyDataFrame* frame) {
- DCHECK(false);
+ ++control_frame_header_data_count_;
+ CHECK_EQ(header_stream_id_, stream_id);
+ if (len == 0) {
+ ++zero_length_control_frame_header_data_count_;
+ // Indicates end-of-header-block.
+ CHECK(header_buffer_valid_);
+ bool parsed_headers = SpdyFramer::ParseHeaderBlockInBuffer(
+ header_buffer_.get(), header_buffer_length_, &headers_);
+ DCHECK(parsed_headers);
+ return true;
+ }
+ const size_t available = header_buffer_size_ - header_buffer_length_;
+ if (len > available) {
+ header_buffer_valid_ = false;
+ return false;
+ }
+ memcpy(header_buffer_.get() + header_buffer_length_, header_data, len);
+ header_buffer_length_ += len;
+ return true;
}
// Convenience function which runs a framer simulation with particular input.
void SimulateInFramer(const unsigned char* input, size_t size) {
- framer_.set_enable_compression(false);
+ framer_.set_enable_compression(use_compression_);
framer_.set_visitor(this);
size_t input_remaining = size;
const char* input_ptr = reinterpret_cast<const char*>(input);
@@ -175,16 +210,56 @@
}
}
+ void InitHeaderStreaming(const SpdyControlFrame* frame) {
+ memset(header_buffer_.get(), 0, header_buffer_size_);
+ header_buffer_length_ = 0;
+ header_stream_id_ = SpdyFramer::GetControlFrameStreamId(frame);
+ header_control_type_ = frame->type();
+ header_buffer_valid_ = true;
+ DCHECK_NE(header_stream_id_, SpdyFramer::kInvalidStream);
+ }
+
+ // Override the default buffer size (16K). Call before using the framer!
+ void set_header_buffer_size(size_t header_buffer_size) {
+ header_buffer_size_ = header_buffer_size;
+ header_buffer_.reset(new char[header_buffer_size]);
+ }
+
+ static size_t control_frame_buffer_max_size() {
+ return SpdyFramer::kControlFrameBufferMaxSize;
+ }
+
+ static size_t header_data_chunk_max_size() {
+ return SpdyFramer::kHeaderDataChunkMaxSize;
+ }
+
SpdyFramer framer_;
+ bool use_compression_;
+
// Counters from the visitor callbacks.
int error_count_;
int syn_frame_count_;
int syn_reply_frame_count_;
int headers_frame_count_;
+ int goaway_count_;
int data_bytes_;
int fin_frame_count_; // The count of RST_STREAM type frames received.
int fin_flag_count_; // The count of frames with the FIN flag set.
int zero_length_data_frame_count_; // The count of zero-length data frames.
+ int header_blocks_count_;
+ int control_frame_header_data_count_; // The count of chunks received.
+ // The count of zero-length control frame header data chunks received.
+ int zero_length_control_frame_header_data_count_;
+ int data_frame_count_;
+
+ // Header block streaming state:
+ scoped_array<char> header_buffer_;
+ size_t header_buffer_length_;
+ size_t header_buffer_size_;
+ SpdyStreamId header_stream_id_;
+ SpdyControlType header_control_type_;
+ bool header_buffer_valid_;
+ SpdyHeaderBlock headers_;
};
} // namespace test
@@ -200,12 +275,12 @@
using spdy::SpdyHeaderBlock;
using spdy::SpdySynStreamControlFrame;
using spdy::kControlFlagMask;
+using spdy::kLengthMask;
using spdy::CONTROL_FLAG_NONE;
using spdy::DATA_FLAG_COMPRESSED;
using spdy::DATA_FLAG_FIN;
using spdy::SYN_STREAM;
using spdy::test::CompareCharArraysWithHexError;
-using spdy::test::FramerSetEnableCompressionHelper;
using spdy::test::TestSpdyVisitor;
namespace spdy {
@@ -227,6 +302,22 @@
}
};
+
+TEST(SpdyFrameBuilderTest, WriteLimits) {
+ SpdyFrameBuilder builder(kLengthMask + 4);
+ // length field should fail.
+ EXPECT_FALSE(builder.WriteBytes(reinterpret_cast<const void*>(0x1),
+ kLengthMask + 1));
+ EXPECT_EQ(0, builder.length());
+
+ // Writing a block of the maximum allowed size should succeed.
+ const std::string kLargeData(kLengthMask, 'A');
+ builder.WriteUInt32(kLengthMask);
+ EXPECT_EQ(4, builder.length());
+ EXPECT_TRUE(builder.WriteBytes(kLargeData.data(), kLengthMask));
+ EXPECT_EQ(4 + kLengthMask, static_cast<unsigned>(builder.length()));
+}
+
// Test that we can encode and decode a SpdyHeaderBlock.
TEST_F(SpdyFramerTest, HeaderBlock) {
SpdyHeaderBlock headers;
@@ -247,14 +338,58 @@
EXPECT_EQ(headers["gamma"], new_headers["gamma"]);
}
+// Test that we can encode and decode a SpdyHeaderBlock in serialized form.
+TEST_F(SpdyFramerTest, HeaderBlockInBuffer) {
+ SpdyHeaderBlock headers;
+ headers["alpha"] = "beta";
+ headers["gamma"] = "charlie";
+ SpdyFramer framer;
+
+ // Encode the header block into a SynStream frame.
+ scoped_ptr<SpdySynStreamControlFrame> frame(
+ framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false, &headers));
+ EXPECT_TRUE(frame.get() != NULL);
+ std::string serialized_headers(frame->header_block(),
+ frame->header_block_len());
+ SpdyHeaderBlock new_headers;
+ EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
+ serialized_headers.size(),
+ &new_headers));
+
+ EXPECT_EQ(headers.size(), new_headers.size());
+ EXPECT_EQ(headers["alpha"], new_headers["alpha"]);
+ EXPECT_EQ(headers["gamma"], new_headers["gamma"]);
+}
+
+// Test that if there's not a full frame, we fail to parse it.
+TEST_F(SpdyFramerTest, UndersizedHeaderBlockInBuffer) {
+ SpdyHeaderBlock headers;
+ headers["alpha"] = "beta";
+ headers["gamma"] = "charlie";
+ SpdyFramer framer;
+
+ // Encode the header block into a SynStream frame.
+ scoped_ptr<SpdySynStreamControlFrame> frame(
+ framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false, &headers));
+ EXPECT_TRUE(frame.get() != NULL);
+
+ std::string serialized_headers(frame->header_block(),
+ frame->header_block_len());
+ SpdyHeaderBlock new_headers;
+ EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
+ serialized_headers.size() - 2,
+ &new_headers));
+}
+
TEST_F(SpdyFramerTest, OutOfOrderHeaders) {
- SpdyFrameBuilder frame;
+ // Frame builder with plentiful buffer size.
+ SpdyFrameBuilder frame(1024);
frame.WriteUInt16(kControlFlagMask | 1);
frame.WriteUInt16(SYN_STREAM);
frame.WriteUInt32(0); // Placeholder for the length.
frame.WriteUInt32(3); // stream_id
- frame.WriteUInt32(0); // associated stream id
+ frame.WriteUInt32(0); // Associated stream id
frame.WriteUInt16(0); // Priority.
frame.WriteUInt16(2); // Number of headers.
@@ -268,8 +403,11 @@
SpdyHeaderBlock new_headers;
scoped_ptr<SpdyFrame> control_frame(frame.take());
+ SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
+ std::string serialized_headers(syn_frame.header_block(),
+ syn_frame.header_block_len());
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, false);
+ framer.set_enable_compression(false);
EXPECT_TRUE(framer.ParseHeaderBlock(control_frame.get(), &new_headers));
}
@@ -311,13 +449,14 @@
scoped_ptr<SpdyFrame> syn_frame1(frame1.take());
scoped_ptr<SpdyFrame> syn_frame2(frame2.take());
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, false);
+ framer.set_enable_compression(false);
EXPECT_FALSE(framer.ParseHeaderBlock(syn_frame1.get(), &new_headers));
EXPECT_FALSE(framer.ParseHeaderBlock(syn_frame2.get(), &new_headers));
}
TEST_F(SpdyFramerTest, DuplicateHeader) {
- SpdyFrameBuilder frame;
+ // Frame builder with plentiful buffer size.
+ SpdyFrameBuilder frame(1024);
frame.WriteUInt16(kControlFlagMask | 1);
frame.WriteUInt16(SYN_STREAM);
@@ -337,14 +476,18 @@
SpdyHeaderBlock new_headers;
scoped_ptr<SpdyFrame> control_frame(frame.take());
+ SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
+ std::string serialized_headers(syn_frame.header_block(),
+ syn_frame.header_block_len());
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, false);
+ framer.set_enable_compression(false);
// This should fail because duplicate headers are verboten by the spec.
EXPECT_FALSE(framer.ParseHeaderBlock(control_frame.get(), &new_headers));
}
TEST_F(SpdyFramerTest, MultiValueHeader) {
- SpdyFrameBuilder frame;
+ // Frame builder with plentiful buffer size.
+ SpdyFrameBuilder frame(1024);
frame.WriteUInt16(kControlFlagMask | 1);
frame.WriteUInt16(SYN_STREAM);
@@ -363,8 +506,11 @@
SpdyHeaderBlock new_headers;
scoped_ptr<SpdyFrame> control_frame(frame.take());
+ SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
+ std::string serialized_headers(syn_frame.header_block(),
+ syn_frame.header_block_len());
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, false);
+ framer.set_enable_compression(false);
EXPECT_TRUE(framer.ParseHeaderBlock(control_frame.get(), &new_headers));
EXPECT_TRUE(new_headers.find("name") != new_headers.end());
EXPECT_EQ(value, new_headers.find("name")->second);
@@ -408,7 +554,7 @@
headers["content-length"] = "12";
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, true);
+ framer.set_enable_compression(true);
scoped_ptr<SpdySynStreamControlFrame>
frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true,
&headers));
@@ -429,6 +575,16 @@
EXPECT_EQ(0,
memcmp(frame3->data(), frame4->data(),
SpdyFrame::size() + frame3->length()));
+
+ // Expect frames 3 to be the same as a uncompressed frame created
+ // from scratch.
+ scoped_ptr<SpdySynStreamControlFrame>
+ uncompressed_frame(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE,
+ false, &headers));
+ EXPECT_EQ(frame3->length(), uncompressed_frame->length());
+ EXPECT_EQ(0,
+ memcmp(frame3->data(), uncompressed_frame->data(),
+ SpdyFrame::size() + uncompressed_frame->length()));
}
TEST_F(SpdyFramerTest, DecompressUncompressedFrame) {
@@ -441,7 +597,7 @@
headers["content-length"] = "12";
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, true);
+ framer.set_enable_compression(true);
scoped_ptr<SpdySynStreamControlFrame>
frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false,
&headers));
@@ -517,6 +673,7 @@
EXPECT_EQ(2, visitor.fin_frame_count_);
EXPECT_EQ(0, visitor.fin_flag_count_);
EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
+ // EXPECT_EQ(4, visitor.data_frame_count_);
}
// Test that the FIN flag on a data frame signifies EOF.
@@ -539,13 +696,13 @@
0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
0x00, 0x00, 0x00, 0x0c,
- 0xde, 0xad, 0xbe, 0xef,
- 0xde, 0xad, 0xbe, 0xef,
- 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x01, // DATA on Stream #1, with EOF
0x01, 0x00, 0x00, 0x04,
- 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
};
TestSpdyVisitor visitor;
@@ -559,6 +716,7 @@
EXPECT_EQ(0, visitor.fin_frame_count_);
EXPECT_EQ(0, visitor.fin_flag_count_);
EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+ // EXPECT_EQ(2, visitor.data_frame_count_);
}
// Test that the FIN flag on a SYN reply frame signifies EOF.
@@ -573,8 +731,9 @@
0x00, 0x02, 'v', 'v',
0x80, 0x02, 0x00, 0x02, // SYN REPLY Stream #1
- 0x01, 0x00, 0x00, 0x10,
+ 0x01, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x02, 'a', 'a',
0x00, 0x02, 'b', 'b',
@@ -591,6 +750,7 @@
EXPECT_EQ(0, visitor.fin_frame_count_);
EXPECT_EQ(1, visitor.fin_flag_count_);
EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+ // EXPECT_EQ(0, visitor.data_frame_count_);
}
// Basic compression & decompression
@@ -598,8 +758,8 @@
SpdyFramer send_framer;
SpdyFramer recv_framer;
- FramerSetEnableCompressionHelper(&send_framer, true);
- FramerSetEnableCompressionHelper(&recv_framer, true);
+ send_framer.set_enable_compression(true);
+ recv_framer.set_enable_compression(true);
// Mix up some SYNs and DATA frames since they use different compressors.
const char kHeader1[] = "header1";
@@ -694,7 +854,7 @@
TEST_F(SpdyFramerTest, UnclosedStreamDataCompressors) {
SpdyFramer send_framer;
- FramerSetEnableCompressionHelper(&send_framer, false);
+ send_framer.set_enable_compression(true);
const char kHeader1[] = "header1";
const char kHeader2[] = "header2";
@@ -711,14 +871,14 @@
const char bytes[] = "this is a test test test test test!";
scoped_ptr<SpdyFrame> send_frame(
- send_framer.CreateDataFrame(1,
- bytes,
- arraysize(bytes),
- DATA_FLAG_FIN));
+ send_framer.CreateDataFrame(
+ 1, bytes, arraysize(bytes),
+ DATA_FLAG_FIN));
EXPECT_TRUE(send_frame.get() != NULL);
// Run the inputs through the framer.
TestSpdyVisitor visitor;
+ visitor.use_compression_ = true;
const unsigned char* data;
data = reinterpret_cast<const unsigned char*>(syn_frame->data());
visitor.SimulateInFramer(data, syn_frame->length() + SpdyFrame::size());
@@ -733,6 +893,7 @@
EXPECT_EQ(0, visitor.fin_frame_count_);
EXPECT_EQ(0, visitor.fin_flag_count_);
EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+ // EXPECT_EQ(1, visitor.data_frame_count_);
// We closed the streams, so all compressors should be down.
EXPECT_EQ(0, visitor.framer_.num_stream_compressors());
@@ -741,6 +902,22 @@
EXPECT_EQ(0, send_framer.num_stream_decompressors());
}
+TEST_F(SpdyFramerTest, WindowUpdateFrame) {
+ scoped_ptr<SpdyWindowUpdateControlFrame> window_update_frame(
+ SpdyFramer::CreateWindowUpdate(1, 0x12345678));
+
+ const unsigned char expected_data_frame[] = {
+ 0x80, 0x02, 0x00, 0x09,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x12, 0x34, 0x56, 0x78
+ };
+
+ EXPECT_EQ(16u, window_update_frame->size());
+ EXPECT_EQ(0,
+ memcmp(window_update_frame->data(), expected_data_frame, 16));
+}
+
TEST_F(SpdyFramerTest, CreateDataFrame) {
SpdyFramer framer;
@@ -805,11 +982,31 @@
0x7fffffff, "hello", 5, DATA_FLAG_FIN));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
}
+
+ {
+ const char kDescription[] = "Large data frame";
+ const int kDataSize = 4 * 1024 * 1024; // 4 MB
+ const std::string kData(kDataSize, 'A');
+ const unsigned char kFrameHeader[] = {
+ 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x40, 0x00, 0x00,
+ };
+
+ const int kFrameSize = arraysize(kFrameHeader) + kDataSize;
+ scoped_array<unsigned char> expected_frame_data(
+ new unsigned char[kFrameSize]);
+ memcpy(expected_frame_data.get(), kFrameHeader, arraysize(kFrameHeader));
+ memset(expected_frame_data.get() + arraysize(kFrameHeader), 'A', kDataSize);
+
+ scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+ 1, kData.data(), kData.size(), DATA_FLAG_FIN));
+ CompareFrame(kDescription, *frame, expected_frame_data.get(), kFrameSize);
+ }
}
TEST_F(SpdyFramerTest, CreateSynStreamUncompressed) {
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, false);
+ framer.set_enable_compression(false);
{
const char kDescription[] = "SYN_STREAM frame, lowest pri, no FIN";
@@ -834,6 +1031,8 @@
1, 0, SPDY_PRIORITY_LOWEST, CONTROL_FLAG_NONE,
false, &headers));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(
+ reinterpret_cast<const SpdyControlFrame*>(frame.get())));
}
{
@@ -893,7 +1092,7 @@
TEST_F(SpdyFramerTest, CreateSynStreamCompressed) {
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, true);
+ framer.set_enable_compression(true);
{
const char kDescription[] =
@@ -926,7 +1125,7 @@
TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) {
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, false);
+ framer.set_enable_compression(false);
{
const char kDescription[] = "SYN_REPLY frame, no FIN";
@@ -1002,7 +1201,7 @@
TEST_F(SpdyFramerTest, CreateSynReplyCompressed) {
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, true);
+ framer.set_enable_compression(true);
{
const char kDescription[] = "SYN_REPLY frame, no FIN";
@@ -1043,6 +1242,8 @@
};
scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(1, PROTOCOL_ERROR));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(
+ reinterpret_cast<const SpdyControlFrame*>(frame.get())));
}
{
@@ -1114,6 +1315,9 @@
};
scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(SpdyFramer::kInvalidStream,
+ SpdyFramer::GetControlFrameStreamId(
+ reinterpret_cast<const SpdyControlFrame*>(frame.get())));
}
{
@@ -1142,6 +1346,27 @@
};
scoped_ptr<SpdyFrame> frame(framer.CreateNopFrame());
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(SpdyFramer::kInvalidStream,
+ SpdyFramer::GetControlFrameStreamId(
+ reinterpret_cast<const SpdyControlFrame*>(frame.get())));
+ }
+}
+
+TEST_F(SpdyFramerTest, CreatePingFrame) {
+ SpdyFramer framer;
+
+ {
+ const char kDescription[] = "PING frame";
+ const unsigned char kFrameData[] = {
+ 0x80, 0x02, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x12, 0x34, 0x56, 0x78,
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreatePingFrame(0x12345678u));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(SpdyFramer::kInvalidStream,
+ SpdyFramer::GetControlFrameStreamId(
+ reinterpret_cast<const SpdyControlFrame*>(frame.get())));
}
}
@@ -1157,6 +1382,9 @@
};
scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(SpdyFramer::kInvalidStream,
+ SpdyFramer::GetControlFrameStreamId(
+ reinterpret_cast<const SpdyControlFrame*>(frame.get())));
}
{
@@ -1173,7 +1401,7 @@
TEST_F(SpdyFramerTest, CreateHeadersUncompressed) {
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, false);
+ framer.set_enable_compression(false);
{
const char kDescription[] = "HEADERS frame, no FIN";
@@ -1249,7 +1477,7 @@
TEST_F(SpdyFramerTest, CreateHeadersCompressed) {
SpdyFramer framer;
- FramerSetEnableCompressionHelper(&framer, true);
+ framer.set_enable_compression(true);
{
const char kDescription[] = "HEADERS frame, no FIN";
@@ -1290,6 +1518,8 @@
};
scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 1));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(
+ reinterpret_cast<const SpdyControlFrame*>(frame.get())));
}
{
@@ -1317,6 +1547,24 @@
}
}
+TEST_F(SpdyFramerTest, DuplicateFrame) {
+ SpdyFramer framer;
+
+ {
+ const char kDescription[] = "PING frame";
+ const unsigned char kFrameData[] = {
+ 0x80, 0x02, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x12, 0x34, 0x56, 0x78,
+ };
+ scoped_ptr<SpdyFrame> frame1(framer.CreatePingFrame(0x12345678u));
+ CompareFrame(kDescription, *frame1, kFrameData, arraysize(kFrameData));
+
+ scoped_ptr<SpdyFrame> frame2(framer.DuplicateFrame(*frame1));
+ CompareFrame(kDescription, *frame2, kFrameData, arraysize(kFrameData));
+ }
+}
+
// This test case reproduces conditions that caused ExpandControlFrameBuffer to
// fail to expand the buffer control frame buffer when it should have, allowing
// the framer to overrun the buffer, and smash other heap contents. This test
@@ -1350,6 +1598,157 @@
}
}
+TEST_F(SpdyFramerTest, ReadGarbage) {
+ SpdyFramer framer;
+ unsigned char garbage_frame[256];
+ memset(garbage_frame, ~0, sizeof(garbage_frame));
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(garbage_frame, sizeof(garbage_frame));
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST_F(SpdyFramerTest, ReadGarbageWithValidVersion) {
+ SpdyFramer framer;
+ char garbage_frame[256];
+ memset(garbage_frame, ~0, sizeof(garbage_frame));
+ SpdyControlFrame control_frame(&garbage_frame[0], false);
+ control_frame.set_version(kSpdyProtocolVersion);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame.data()),
+ sizeof(garbage_frame));
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST(SpdyFramer, StateToStringTest) {
+ EXPECT_STREQ("ERROR",
+ SpdyFramer::StateToString(SpdyFramer::SPDY_ERROR));
+ EXPECT_STREQ("DONE",
+ SpdyFramer::StateToString(SpdyFramer::SPDY_DONE));
+ EXPECT_STREQ("AUTO_RESET",
+ SpdyFramer::StateToString(SpdyFramer::SPDY_AUTO_RESET));
+ EXPECT_STREQ("RESET",
+ SpdyFramer::StateToString(SpdyFramer::SPDY_RESET));
+ EXPECT_STREQ("READING_COMMON_HEADER",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_READING_COMMON_HEADER));
+ EXPECT_STREQ("INTERPRET_CONTROL_FRAME_COMMON_HEADER",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER));
+ EXPECT_STREQ("CONTROL_FRAME_PAYLOAD",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_CONTROL_FRAME_PAYLOAD));
+ EXPECT_STREQ("IGNORE_REMAINING_PAYLOAD",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_IGNORE_REMAINING_PAYLOAD));
+ EXPECT_STREQ("FORWARD_STREAM_FRAME",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_FORWARD_STREAM_FRAME));
+ EXPECT_STREQ("SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK));
+ EXPECT_STREQ("SPDY_CONTROL_FRAME_HEADER_BLOCK",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK));
+ EXPECT_STREQ("UNKNOWN_STATE",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK + 1));
+}
+
+TEST(SpdyFramer, ErrorCodeToStringTest) {
+ EXPECT_STREQ("NO_ERROR",
+ SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_NO_ERROR));
+ EXPECT_STREQ("INVALID_CONTROL_FRAME",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_INVALID_CONTROL_FRAME));
+ EXPECT_STREQ("CONTROL_PAYLOAD_TOO_LARGE",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE));
+ EXPECT_STREQ("ZLIB_INIT_FAILURE",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_ZLIB_INIT_FAILURE));
+ EXPECT_STREQ("UNSUPPORTED_VERSION",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_UNSUPPORTED_VERSION));
+ EXPECT_STREQ("DECOMPRESS_FAILURE",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_DECOMPRESS_FAILURE));
+ EXPECT_STREQ("COMPRESS_FAILURE",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_COMPRESS_FAILURE));
+ EXPECT_STREQ("UNKNOWN_ERROR",
+ SpdyFramer::ErrorCodeToString(SpdyFramer::LAST_ERROR));
+}
+
+TEST(SpdyFramer, StatusCodeToStringTest) {
+ EXPECT_STREQ("INVALID",
+ SpdyFramer::StatusCodeToString(INVALID));
+ EXPECT_STREQ("PROTOCOL_ERROR",
+ SpdyFramer::StatusCodeToString(PROTOCOL_ERROR));
+ EXPECT_STREQ("INVALID_STREAM",
+ SpdyFramer::StatusCodeToString(INVALID_STREAM));
+ EXPECT_STREQ("REFUSED_STREAM",
+ SpdyFramer::StatusCodeToString(REFUSED_STREAM));
+ EXPECT_STREQ("UNSUPPORTED_VERSION",
+ SpdyFramer::StatusCodeToString(UNSUPPORTED_VERSION));
+ EXPECT_STREQ("CANCEL",
+ SpdyFramer::StatusCodeToString(CANCEL));
+ EXPECT_STREQ("INTERNAL_ERROR",
+ SpdyFramer::StatusCodeToString(INTERNAL_ERROR));
+ EXPECT_STREQ("FLOW_CONTROL_ERROR",
+ SpdyFramer::StatusCodeToString(FLOW_CONTROL_ERROR));
+ EXPECT_STREQ("UNKNOWN_STATUS",
+ SpdyFramer::StatusCodeToString(NUM_STATUS_CODES));
+}
+
+TEST(SpdyFramer, ControlTypeToStringTest) {
+ EXPECT_STREQ("SYN_STREAM",
+ SpdyFramer::ControlTypeToString(SYN_STREAM));
+ EXPECT_STREQ("SYN_REPLY",
+ SpdyFramer::ControlTypeToString(SYN_REPLY));
+ EXPECT_STREQ("RST_STREAM",
+ SpdyFramer::ControlTypeToString(RST_STREAM));
+ EXPECT_STREQ("SETTINGS",
+ SpdyFramer::ControlTypeToString(SETTINGS));
+ EXPECT_STREQ("NOOP",
+ SpdyFramer::ControlTypeToString(NOOP));
+ EXPECT_STREQ("PING",
+ SpdyFramer::ControlTypeToString(PING));
+ EXPECT_STREQ("GOAWAY",
+ SpdyFramer::ControlTypeToString(GOAWAY));
+ EXPECT_STREQ("HEADERS",
+ SpdyFramer::ControlTypeToString(HEADERS));
+ EXPECT_STREQ("WINDOW_UPDATE",
+ SpdyFramer::ControlTypeToString(WINDOW_UPDATE));
+ EXPECT_STREQ("UNKNOWN_CONTROL_TYPE",
+ SpdyFramer::ControlTypeToString(NUM_CONTROL_FRAME_TYPES));
+}
+
+TEST(SpdyFramer, GetMinimumControlFrameSizeTest) {
+ EXPECT_EQ(SpdySynStreamControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(SYN_STREAM));
+ EXPECT_EQ(SpdySynReplyControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(SYN_REPLY));
+ EXPECT_EQ(SpdyRstStreamControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(RST_STREAM));
+ EXPECT_EQ(SpdySettingsControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(SETTINGS));
+ EXPECT_EQ(SpdyNoOpControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(NOOP));
+ EXPECT_EQ(SpdyPingControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(PING));
+ EXPECT_EQ(SpdyGoAwayControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(GOAWAY));
+ EXPECT_EQ(SpdyHeadersControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(HEADERS));
+ EXPECT_EQ(SpdyWindowUpdateControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(WINDOW_UPDATE));
+ EXPECT_EQ(static_cast<size_t>(0x7FFFFFFF),
+ SpdyFramer::GetMinimumControlFrameSize(NUM_CONTROL_FRAME_TYPES));
+}
+
std::string RandomString(int length) {
std::string rv;
for (int index = 0; index < length; index++)
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h
index ac6cd6b..eed9798 100644
--- a/net/spdy/spdy_protocol.h
+++ b/net/spdy/spdy_protocol.h
@@ -123,6 +123,7 @@
// +----------------------------------+
// | Delta-Window-Size (32 bits) |
// +----------------------------------+
+//
namespace spdy {
// This implementation of Spdy is version 2; It's like version 1, with some
@@ -166,6 +167,7 @@
enum SpdyDataFlags {
DATA_FLAG_NONE = 0,
DATA_FLAG_FIN = 1,
+ // TODO(hkhalil): Remove.
DATA_FLAG_COMPRESSED = 2
};
diff --git a/net/spdy/spdy_protocol_test.cc b/net/spdy/spdy_protocol_test.cc
index dbc00dbd..7c575af 100644
--- a/net/spdy/spdy_protocol_test.cc
+++ b/net/spdy/spdy_protocol_test.cc
@@ -14,6 +14,7 @@
using spdy::GOAWAY;
using spdy::HEADERS;
using spdy::NOOP;
+using spdy::NUM_CONTROL_FRAME_TYPES;
using spdy::PING;
using spdy::RST_STREAM;
using spdy::SETTINGS;
@@ -27,7 +28,10 @@
using spdy::SpdyFrame;
using spdy::SpdyFramer;
using spdy::SpdyHeaderBlock;
+using spdy::SpdyHeadersControlFrame;
using spdy::SpdyGoAwayControlFrame;
+using spdy::SpdyNoOpControlFrame;
+using spdy::SpdyPingControlFrame;
using spdy::SpdyRstStreamControlFrame;
using spdy::SpdySettings;
using spdy::SpdySettingsControlFrame;
@@ -50,8 +54,11 @@
EXPECT_EQ(18u, SpdySynStreamControlFrame::size());
EXPECT_EQ(14u, SpdySynReplyControlFrame::size());
EXPECT_EQ(16u, SpdyRstStreamControlFrame::size());
- EXPECT_EQ(12u, SpdyGoAwayControlFrame::size());
EXPECT_EQ(12u, SpdySettingsControlFrame::size());
+ EXPECT_EQ(8u, SpdyNoOpControlFrame::size());
+ EXPECT_EQ(12u, SpdyPingControlFrame::size());
+ EXPECT_EQ(12u, SpdyGoAwayControlFrame::size());
+ EXPECT_EQ(14u, SpdyHeadersControlFrame::size());
EXPECT_EQ(16u, SpdyWindowUpdateControlFrame::size());
EXPECT_EQ(4u, sizeof(FlagsAndLength));
EXPECT_EQ(1, SYN_STREAM);
@@ -125,6 +132,24 @@
EXPECT_EQ(spdy::INVALID_STREAM, rst_frame->status());
EXPECT_EQ(0, rst_frame->flags());
+ scoped_ptr<SpdyNoOpControlFrame> noop_frame(
+ framer.CreateNopFrame());
+ EXPECT_EQ(kSpdyProtocolVersion, noop_frame->version());
+ EXPECT_TRUE(noop_frame->is_control_frame());
+ EXPECT_EQ(NOOP, noop_frame->type());
+ EXPECT_EQ(0, noop_frame->flags());
+
+ const uint32 kUniqueId = 1234567u;
+ const uint32 kUniqueId2 = 31415926u;
+ scoped_ptr<SpdyPingControlFrame> ping_frame(
+ framer.CreatePingFrame(kUniqueId));
+ EXPECT_EQ(kSpdyProtocolVersion, ping_frame->version());
+ EXPECT_TRUE(ping_frame->is_control_frame());
+ EXPECT_EQ(PING, ping_frame->type());
+ EXPECT_EQ(kUniqueId, ping_frame->unique_id());
+ ping_frame->set_unique_id(kUniqueId2);
+ EXPECT_EQ(kUniqueId2, ping_frame->unique_id());
+
scoped_ptr<SpdyGoAwayControlFrame> goaway_frame(
framer.CreateGoAway(123));
EXPECT_EQ(kSpdyProtocolVersion, goaway_frame->version());
@@ -132,6 +157,15 @@
EXPECT_EQ(GOAWAY, goaway_frame->type());
EXPECT_EQ(123u, goaway_frame->last_accepted_stream_id());
+ scoped_ptr<SpdyHeadersControlFrame> headers_frame(
+ framer.CreateHeaders(123, CONTROL_FLAG_NONE, false, &headers));
+ EXPECT_EQ(kSpdyProtocolVersion, headers_frame->version());
+ EXPECT_TRUE(headers_frame->is_control_frame());
+ EXPECT_EQ(HEADERS, headers_frame->type());
+ EXPECT_EQ(123u, headers_frame->stream_id());
+ EXPECT_EQ(2, headers_frame->header_block_len());
+ EXPECT_EQ(0, headers_frame->flags());
+
scoped_ptr<SpdyWindowUpdateControlFrame> window_update_frame(
framer.CreateWindowUpdate(123, 456));
EXPECT_EQ(kSpdyProtocolVersion, window_update_frame->version());
@@ -230,6 +264,20 @@
}
}
+TEST(SpdyProtocolTest, HasHeaderBlock) {
+ SpdyControlFrame frame(SpdyControlFrame::size());
+ for (SpdyControlType type = SYN_STREAM;
+ type < NUM_CONTROL_FRAME_TYPES;
+ type = static_cast<SpdyControlType>(type + 1)) {
+ frame.set_type(type);
+ if (type == SYN_STREAM || type == SYN_REPLY || type == HEADERS) {
+ EXPECT_TRUE(frame.has_header_block());
+ } else {
+ EXPECT_FALSE(frame.has_header_block());
+ }
+ }
+}
+
// 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.
@@ -313,4 +361,22 @@
}
}
+TEST(SpdyProtocolDeathTest, TestRstStreamStatusBounds) {
+ SpdyFramer framer;
+ scoped_ptr<SpdyRstStreamControlFrame> rst_frame;
+
+ rst_frame.reset(framer.CreateRstStream(123, spdy::PROTOCOL_ERROR));
+ EXPECT_EQ(spdy::PROTOCOL_ERROR, rst_frame->status());
+
+ rst_frame->set_status(spdy::INVALID);
+ EXPECT_EQ(spdy::INVALID, rst_frame->status());
+
+ rst_frame->set_status(
+ static_cast<spdy::SpdyStatusCodes>(spdy::INVALID - 1));
+ EXPECT_EQ(spdy::INVALID, rst_frame->status());
+
+ rst_frame->set_status(spdy::NUM_STATUS_CODES);
+ EXPECT_EQ(spdy::INVALID, rst_frame->status());
+}
+
} // namespace