// 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 #include "base/scoped_ptr.h" #include "flip_framer.h" // cross-google3 directory naming. #include "flip_protocol.h" #include "flip_frame_builder.h" #ifdef _WIN32 #include "testing/platform_test.h" #else #include "testing/base/public/gunit.h" #define PlatformTest ::testing::Test #endif namespace flip { class FlipFramerTest : public PlatformTest { public: virtual void TearDown() {} }; class TestFlipVisitor : public FlipFramerVisitorInterface { public: explicit TestFlipVisitor(FlipFramer* framer) : framer_(framer), error_count_(0), syn_frame_count_(0), syn_reply_frame_count_(0), data_frame_count_(0), fin_frame_count_(0) { } void OnError(FlipFramer* f) { error_count_++; } void OnStreamFrameData(FlipStreamId stream_id, const char* data, uint32 len) { data_frame_count_++; #ifdef TEST_LOGGING std::cerr << "OnStreamFrameData(" << stream_id << ", \""; if (len > 0) { for (uint32 i = 0 ; i < len; ++i) { std::cerr << std::hex << (0xFF & (unsigned int)data[i]) << std::dec; } } std::cerr << "\", " << len << ")\n"; #endif // TEST_LOGGING } void OnControl(const FlipControlFrame* frame) { FlipHeaderBlock headers; bool parsed_headers = false; switch (frame->type()) { case SYN_STREAM: parsed_headers = framer_->ParseHeaderBlock( reinterpret_cast(frame), &headers); DCHECK(parsed_headers); syn_frame_count_++; VLOG(2) << "OnSyn(" << frame->stream_id() << ")\n"; break; case SYN_REPLY: parsed_headers = framer_->ParseHeaderBlock( reinterpret_cast(frame), &headers); DCHECK(parsed_headers); syn_reply_frame_count_++; VLOG(2) << "OnSynReply(" << frame->stream_id() << ")\n"; break; case FIN_STREAM: fin_frame_count_++; VLOG(2) << "OnFin(" << frame->stream_id() << ")\n"; break; default: DCHECK(false); // Error! } } void OnLameDuck() { } FlipFramer* framer_; // Counters from the visitor callbacks. int error_count_; int syn_frame_count_; int syn_reply_frame_count_; int data_frame_count_; int fin_frame_count_; }; // Test our protocol constants TEST_F(FlipFramerTest, ProtocolConstants) { EXPECT_EQ(8, sizeof(FlipFrame)); EXPECT_EQ(8, sizeof(FlipDataFrame)); EXPECT_EQ(12, sizeof(FlipControlFrame)); EXPECT_EQ(16, sizeof(FlipSynStreamControlFrame)); EXPECT_EQ(16, sizeof(FlipSynReplyControlFrame)); EXPECT_EQ(16, sizeof(FlipFinStreamControlFrame)); EXPECT_EQ(1, SYN_STREAM); EXPECT_EQ(2, SYN_REPLY); EXPECT_EQ(3, FIN_STREAM); } // 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 frame( framer.CreateSynStream(1, 1, true, &headers)); EXPECT_TRUE(frame.get() != NULL); FlipHeaderBlock new_headers; FlipFrame* control_frame = reinterpret_cast(frame.get()); framer.ParseHeaderBlock(control_frame, &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, HeaderBlockBarfsOnOutOfOrderHeaders) { 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() - sizeof(FlipFrame)); FlipHeaderBlock new_headers; const FlipFrame* control_frame = frame.data(); FlipFramer framer; framer.set_enable_compression(false); EXPECT_FALSE(framer.ParseHeaderBlock(control_frame, &new_headers)); } 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; scoped_ptr frame1(framer.CreateSynStream(1, 1, true, &headers)); scoped_ptr frame2(framer.CreateSynStream(1, 1, 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 frame3( framer.DecompressFrame(reinterpret_cast(frame1.get()))); // Decompress the second frame scoped_ptr frame4( framer.DecompressFrame(reinterpret_cast(frame2.get()))); // Expect frames 3 & 4 to be the same. EXPECT_EQ(0, memcmp(frame3.get(), frame4.get(), sizeof(FlipFrame) + frame3->length())); } 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, }; FlipFramer framer; framer.set_enable_compression(false); TestFlipVisitor visitor(&framer); framer.set_visitor(&visitor); size_t input_remaining = sizeof(input); const char* input_ptr = reinterpret_cast(input); while (input_remaining > 0 && framer.error_code() == FlipFramer::FLIP_NO_ERROR) { size_t bytes_processed = framer.ProcessInput(input_ptr, sizeof(input)); input_remaining -= bytes_processed; input_ptr += bytes_processed; if (framer.state() == FlipFramer::FLIP_DONE) framer.Reset(); } EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(2, visitor.syn_frame_count_); EXPECT_EQ(0, visitor.syn_reply_frame_count_); EXPECT_EQ(4, visitor.data_frame_count_); EXPECT_EQ(2, visitor.fin_frame_count_); } } // namespace flip