summaryrefslogtreecommitdiffstats
path: root/net/spdy/spdy_framer_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/spdy/spdy_framer_test.cc')
-rw-r--r--net/spdy/spdy_framer_test.cc407
1 files changed, 407 insertions, 0 deletions
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
new file mode 100644
index 0000000..21f2e68
--- /dev/null
+++ b/net/spdy/spdy_framer_test.cc
@@ -0,0 +1,407 @@
+// Copyright (c) 2009 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 <algorithm>
+#include <iostream>
+
+#include "base/scoped_ptr.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/spdy_frame_builder.h"
+#include "testing/platform_test.h"
+
+namespace flip {
+
+namespace test {
+
+void FramerSetEnableCompressionHelper(FlipFramer* framer, bool compress) {
+ framer->set_enable_compression(compress);
+}
+
+class TestFlipVisitor : public FlipFramerVisitorInterface {
+ public:
+ TestFlipVisitor()
+ : error_count_(0),
+ syn_frame_count_(0),
+ syn_reply_frame_count_(0),
+ data_bytes_(0),
+ fin_frame_count_(0),
+ fin_flag_count_(0),
+ zero_length_data_frame_count_(0) {
+ }
+
+ void OnError(FlipFramer* f) {
+ error_count_++;
+ }
+
+ void OnStreamFrameData(FlipStreamId stream_id,
+ const char* data,
+ size_t len) {
+ if (len == 0)
+ ++zero_length_data_frame_count_;
+
+ data_bytes_ += len;
+ std::cerr << "OnStreamFrameData(" << stream_id << ", \"";
+ if (len > 0) {
+ for (size_t i = 0 ; i < len; ++i) {
+ std::cerr << std::hex << (0xFF & (unsigned int)data[i]) << std::dec;
+ }
+ }
+ std::cerr << "\", " << len << ")\n";
+ }
+
+ void OnControl(const FlipControlFrame* frame) {
+ FlipHeaderBlock headers;
+ bool parsed_headers = false;
+ switch (frame->type()) {
+ case SYN_STREAM:
+ parsed_headers = framer_.ParseHeaderBlock(frame, &headers);
+ DCHECK(parsed_headers);
+ syn_frame_count_++;
+ break;
+ case SYN_REPLY:
+ parsed_headers = framer_.ParseHeaderBlock(frame, &headers);
+ DCHECK(parsed_headers);
+ syn_reply_frame_count_++;
+ break;
+ case FIN_STREAM:
+ fin_frame_count_++;
+ break;
+ default:
+ DCHECK(false); // Error!
+ }
+ if (frame->flags() & CONTROL_FLAG_FIN)
+ ++fin_flag_count_;
+ }
+
+ // 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_visitor(this);
+ size_t input_remaining = size;
+ const char* input_ptr = reinterpret_cast<const char*>(input);
+ while (input_remaining > 0 &&
+ framer_.error_code() == FlipFramer::FLIP_NO_ERROR) {
+ // To make the tests more interesting, we feed random (amd small) chunks
+ // into the framer. This simulates getting strange-sized reads from
+ // the socket.
+ const size_t kMaxReadSize = 32;
+ size_t bytes_read =
+ (rand() % std::min(input_remaining, kMaxReadSize)) + 1;
+ size_t bytes_processed = framer_.ProcessInput(input_ptr, bytes_read);
+ input_remaining -= bytes_processed;
+ input_ptr += bytes_processed;
+ if (framer_.state() == FlipFramer::FLIP_DONE)
+ framer_.Reset();
+ }
+ }
+
+ FlipFramer framer_;
+ // Counters from the visitor callbacks.
+ int error_count_;
+ int syn_frame_count_;
+ int syn_reply_frame_count_;
+ int data_bytes_;
+ int fin_frame_count_; // The count of FIN_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.
+};
+
+} // namespace test
+
+} // namespace flip
+
+using flip::FlipFrame;
+using flip::FlipFrameBuilder;
+using flip::FlipFramer;
+using flip::FlipHeaderBlock;
+using flip::FlipSynStreamControlFrame;
+using flip::kControlFlagMask;
+using flip::CONTROL_FLAG_NONE;
+using flip::SYN_STREAM;
+using flip::test::FramerSetEnableCompressionHelper;
+using flip::test::TestFlipVisitor;
+
+namespace {
+
+class FlipFramerTest : public PlatformTest {
+ public:
+ virtual void TearDown() {}
+};
+
+// Test that we can encode and decode a FlipHeaderBlock.
+TEST_F(FlipFramerTest, HeaderBlock) {
+ FlipHeaderBlock headers;
+ headers["alpha"] = "beta";
+ headers["gamma"] = "charlie";
+ FlipFramer framer;
+
+ // Encode the header block into a SynStream frame.
+ scoped_ptr<FlipSynStreamControlFrame> frame(
+ framer.CreateSynStream(1, 1, CONTROL_FLAG_NONE, true, &headers));
+ EXPECT_TRUE(frame.get() != NULL);
+
+ FlipHeaderBlock new_headers;
+ framer.ParseHeaderBlock(frame.get(), &new_headers);
+
+ EXPECT_EQ(headers.size(), new_headers.size());
+ EXPECT_EQ(headers["alpha"], new_headers["alpha"]);
+ EXPECT_EQ(headers["gamma"], new_headers["gamma"]);
+}
+
+TEST_F(FlipFramerTest, OutOfOrderHeaders) {
+ FlipFrameBuilder frame;
+
+ frame.WriteUInt16(kControlFlagMask | 1);
+ frame.WriteUInt16(SYN_STREAM);
+ frame.WriteUInt32(0); // Placeholder for the length.
+ frame.WriteUInt32(3); // stream_id
+ frame.WriteUInt16(0); // Priority.
+
+ frame.WriteUInt16(2); // Number of headers.
+ FlipHeaderBlock::iterator it;
+ frame.WriteString("gamma");
+ frame.WriteString("gamma");
+ frame.WriteString("alpha");
+ frame.WriteString("alpha");
+ // write the length
+ frame.WriteUInt32ToOffset(4, frame.length() - FlipFrame::size());
+
+ FlipHeaderBlock new_headers;
+ scoped_ptr<FlipFrame> control_frame(frame.take());
+ FlipFramer framer;
+ FramerSetEnableCompressionHelper(&framer, false);
+ EXPECT_TRUE(framer.ParseHeaderBlock(control_frame.get(), &new_headers));
+}
+
+TEST_F(FlipFramerTest, DuplicateHeader) {
+ FlipFrameBuilder frame;
+
+ frame.WriteUInt16(kControlFlagMask | 1);
+ frame.WriteUInt16(SYN_STREAM);
+ frame.WriteUInt32(0); // Placeholder for the length.
+ frame.WriteUInt32(3); // stream_id
+ frame.WriteUInt16(0); // Priority.
+
+ frame.WriteUInt16(2); // Number of headers.
+ FlipHeaderBlock::iterator it;
+ frame.WriteString("name");
+ frame.WriteString("value1");
+ frame.WriteString("name");
+ frame.WriteString("value2");
+ // write the length
+ frame.WriteUInt32ToOffset(4, frame.length() - FlipFrame::size());
+
+ FlipHeaderBlock new_headers;
+ scoped_ptr<FlipFrame> control_frame(frame.take());
+ FlipFramer framer;
+ FramerSetEnableCompressionHelper(&framer, false);
+ // This should fail because duplicate headers are verboten by the spec.
+ EXPECT_FALSE(framer.ParseHeaderBlock(control_frame.get(), &new_headers));
+}
+
+TEST_F(FlipFramerTest, MultiValueHeader) {
+ FlipFrameBuilder frame;
+
+ frame.WriteUInt16(kControlFlagMask | 1);
+ frame.WriteUInt16(SYN_STREAM);
+ frame.WriteUInt32(0); // Placeholder for the length.
+ frame.WriteUInt32(3); // stream_id
+ frame.WriteUInt16(0); // Priority.
+
+ frame.WriteUInt16(2); // Number of headers.
+ FlipHeaderBlock::iterator it;
+ frame.WriteString("name");
+ std::string value("value1\0value2");
+ frame.WriteString(value);
+ // write the length
+ frame.WriteUInt32ToOffset(4, frame.length() - FlipFrame::size());
+
+ FlipHeaderBlock new_headers;
+ scoped_ptr<FlipFrame> control_frame(frame.take());
+ FlipFramer framer;
+ FramerSetEnableCompressionHelper(&framer, 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);
+}
+
+TEST_F(FlipFramerTest, BasicCompression) {
+ FlipHeaderBlock headers;
+ headers["server"] = "FlipServer 1.0";
+ headers["date"] = "Mon 12 Jan 2009 12:12:12 PST";
+ headers["status"] = "200";
+ headers["version"] = "HTTP/1.1";
+ headers["content-type"] = "text/html";
+ headers["content-length"] = "12";
+
+ FlipFramer framer;
+ FramerSetEnableCompressionHelper(&framer, true);
+ scoped_ptr<FlipSynStreamControlFrame>
+ frame1(framer.CreateSynStream(1, 1, CONTROL_FLAG_NONE, true, &headers));
+ scoped_ptr<FlipSynStreamControlFrame>
+ frame2(framer.CreateSynStream(1, 1, CONTROL_FLAG_NONE, true, &headers));
+
+ // Expect the second frame to be more compact than the first.
+ EXPECT_LE(frame2->length(), frame1->length());
+
+ // Decompress the first frame
+ scoped_ptr<FlipFrame> frame3(framer.DecompressFrame(frame1.get()));
+
+ // Decompress the second frame
+ scoped_ptr<FlipFrame> frame4(framer.DecompressFrame(frame2.get()));
+
+ // Expect frames 3 & 4 to be the same.
+ EXPECT_EQ(0,
+ memcmp(frame3->data(), frame4->data(),
+ FlipFrame::size() + frame3->length()));
+}
+
+TEST_F(FlipFramerTest, DecompressUncompressedFrame) {
+ FlipHeaderBlock headers;
+ headers["server"] = "FlipServer 1.0";
+ headers["date"] = "Mon 12 Jan 2009 12:12:12 PST";
+ headers["status"] = "200";
+ headers["version"] = "HTTP/1.1";
+ headers["content-type"] = "text/html";
+ headers["content-length"] = "12";
+
+ FlipFramer framer;
+ FramerSetEnableCompressionHelper(&framer, true);
+ scoped_ptr<FlipSynStreamControlFrame>
+ frame1(framer.CreateSynStream(1, 1, CONTROL_FLAG_NONE, false, &headers));
+
+ // Decompress the frame
+ scoped_ptr<FlipFrame> frame2(framer.DecompressFrame(frame1.get()));
+
+ EXPECT_EQ(NULL, frame2.get());
+}
+
+TEST_F(FlipFramerTest, Basic) {
+ const unsigned char input[] = {
+ 0x80, 0x01, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x02, 'v', 'v',
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x0c,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x80, 0x01, 0x00, 0x01, // SYN Stream #3
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
+ 0x00, 0x00, 0x00, 0x08,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x04,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x80, 0x01, 0x00, 0x03, // FIN on Stream #1
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x80, 0x01, 0x00, 0x03, // FIN on Stream #3
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ TestFlipVisitor visitor;
+ visitor.SimulateInFramer(input, sizeof(input));
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(2, visitor.syn_frame_count_);
+ EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(24, visitor.data_bytes_);
+ EXPECT_EQ(2, visitor.fin_frame_count_);
+ EXPECT_EQ(0, visitor.fin_flag_count_);
+ EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
+}
+
+// Test that the FIN flag on a data frame signifies EOF.
+TEST_F(FlipFramerTest, FinOnDataFrame) {
+ const unsigned char input[] = {
+ 0x80, 0x01, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x02, 'v', 'v',
+
+ 0x80, 0x01, 0x00, 0x02, // SYN REPLY Stream #1
+ 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 'a', 'a',
+ 0x00, 0x02, 'b', 'b',
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x0c,
+ 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,
+ };
+
+ TestFlipVisitor visitor;
+ visitor.SimulateInFramer(input, sizeof(input));
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.syn_frame_count_);
+ EXPECT_EQ(1, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(16, visitor.data_bytes_);
+ EXPECT_EQ(0, visitor.fin_frame_count_);
+ EXPECT_EQ(0, visitor.fin_flag_count_);
+ EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+}
+
+// Test that the FIN flag on a SYN reply frame signifies EOF.
+TEST_F(FlipFramerTest, FinOnSynReplyFrame) {
+ const unsigned char input[] = {
+ 0x80, 0x01, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x02, 'v', 'v',
+
+ 0x80, 0x01, 0x00, 0x02, // SYN REPLY Stream #1
+ 0x01, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 'a', 'a',
+ 0x00, 0x02, 'b', 'b',
+ };
+
+ TestFlipVisitor visitor;
+ visitor.SimulateInFramer(input, sizeof(input));
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.syn_frame_count_);
+ EXPECT_EQ(1, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(0, visitor.data_bytes_);
+ EXPECT_EQ(0, visitor.fin_frame_count_);
+ EXPECT_EQ(1, visitor.fin_flag_count_);
+ EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+}
+
+} // namespace
+