diff options
author | mbelshe@google.com <mbelshe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-20 15:32:32 +0000 |
---|---|---|
committer | mbelshe@google.com <mbelshe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-20 15:32:32 +0000 |
commit | 289b93d79e3e7a55553ae0c41fd48461e43e9d8e (patch) | |
tree | 6899338a89e927f6eff837d49ad39f91e84b4846 /net | |
parent | 9194b3fc91953bb1472d8671d2322ac5616ee0b4 (diff) | |
download | chromium_src-289b93d79e3e7a55553ae0c41fd48461e43e9d8e.zip chromium_src-289b93d79e3e7a55553ae0c41fd48461e43e9d8e.tar.gz chromium_src-289b93d79e3e7a55553ae0c41fd48461e43e9d8e.tar.bz2 |
Fixup the flip_framer eof-handling semantics now that we have
the FIN bit in place. The FlipFrameVisitor will always inject
a zero-length data packet to the Visitor as a signal that the
data stream is complete. Even if the FIN packet was set on a
SYN_REPLY (e.g. there are no data packets), the FlipFramer will
simulate a zero-length read to the caller. Likewise, zero-length
reads are never sent to the visitor unless the FIN packet has
been received. This means that the FlipFramer must swallow
zero-length data packets. Also merged in changes from server.
BUG=none
TEST=flip_framer_test.cc
Review URL: http://codereview.chromium.org/294015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29513 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/flip/flip_frame_builder.cc | 2 | ||||
-rw-r--r-- | net/flip/flip_frame_builder.h | 15 | ||||
-rw-r--r-- | net/flip/flip_framer.cc | 97 | ||||
-rw-r--r-- | net/flip/flip_framer.h | 56 | ||||
-rw-r--r-- | net/flip/flip_framer_test.cc | 154 | ||||
-rw-r--r-- | net/flip/flip_protocol.h | 20 | ||||
-rw-r--r-- | net/flip/flip_session.cc | 3 |
7 files changed, 257 insertions, 90 deletions
diff --git a/net/flip/flip_frame_builder.cc b/net/flip/flip_frame_builder.cc index 50d9607..61851b5 100644 --- a/net/flip/flip_frame_builder.cc +++ b/net/flip/flip_frame_builder.cc @@ -9,7 +9,7 @@ namespace flip { - // We mark a read only FlipFrameBuilder with a special capacity_. +// We mark a read only FlipFrameBuilder with a special capacity_. static const size_t kCapacityReadOnly = std::numeric_limits<size_t>::max(); FlipFrameBuilder::FlipFrameBuilder() diff --git a/net/flip/flip_frame_builder.h b/net/flip/flip_frame_builder.h index 0640630..fa59c45 100644 --- a/net/flip/flip_frame_builder.h +++ b/net/flip/flip_frame_builder.h @@ -19,9 +19,7 @@ namespace flip { // This class provides facilities for basic binary value packing and unpacking -// into Flip frames. Note: this is similar to Chrome's pickle class, but is -// simplified to work in both the client and server, and without excess -// padding. +// into Flip frames. // // The FlipFrameBuilder supports appending primitive values (int, string, etc) // to a frame instance. The FlipFrameBuilder grows its internal memory buffer @@ -86,11 +84,16 @@ class FlipFrameBuilder { // Write an integer to a particular offset in the data buffer. bool WriteUInt32ToOffset(int offset, uint32 value) { - if (offset + sizeof(value) > length_) - return false; value = htonl(value); + return WriteBytesToOffset(offset, &value, sizeof(value)); + } + + // Write to a particular offset in the data buffer. + bool WriteBytesToOffset(int offset, const void* data, uint32 data_len) { + if (offset + data_len > length_) + return false; char *ptr = buffer_ + offset; - memcpy(ptr, &value, sizeof(value)); + memcpy(ptr, data, data_len); return true; } diff --git a/net/flip/flip_framer.cc b/net/flip/flip_framer.cc index b5b611f..c0136d0 100644 --- a/net/flip/flip_framer.cc +++ b/net/flip/flip_framer.cc @@ -262,19 +262,29 @@ size_t FlipFramer::ProcessInput(const char* data, size_t len) { } size_t decompressed_size = decompressed_max_size - decompressor_->avail_out; - visitor_->OnStreamFrameData(current_data_frame->stream_id(), - decompressed.get(), - decompressed_size); + // Only inform the visitor if there is data. + if (decompressed_size) + visitor_->OnStreamFrameData(current_data_frame->stream_id(), + decompressed.get(), + decompressed_size); amount_to_forward -= decompressor_->avail_in; } else { - // The data frame was not compressed - visitor_->OnStreamFrameData(current_data_frame->stream_id(), - data, amount_to_forward); + // The data frame was not compressed. + // Only inform the visitor if there is data. + if (amount_to_forward) + visitor_->OnStreamFrameData(current_data_frame->stream_id(), + data, amount_to_forward); } } data += amount_to_forward; len -= amount_to_forward; remaining_payload_ -= amount_to_forward; + // If the FIN flag is set, and there is no more data in this data + // frame, inform the visitor of EOF via a 0-length data frame. + if (!remaining_payload_ && + current_data_frame->flags() & DATA_FLAG_FIN) + visitor_->OnStreamFrameData(current_data_frame->stream_id(), NULL, + 0); } else { CHANGE_STATE(FLIP_AUTO_RESET); } @@ -305,11 +315,12 @@ size_t FlipFramer::ProcessCommonHeader(const char* data, size_t len) { current_frame_len_ += bytes_to_append; data += bytes_to_append; len -= bytes_to_append; - // Check for an empty data packet. + // Check for an empty data frame. if (current_frame_len_ == sizeof(FlipFrame) && !current_frame->is_control_frame() && current_frame->length() == 0) { - visitor_->OnStreamFrameData(current_frame->stream_id(), NULL, 0); + if (current_frame->flags() & CONTROL_FLAG_FIN) + visitor_->OnStreamFrameData(current_frame->stream_id(), NULL, 0); CHANGE_STATE(FLIP_RESET); } break; @@ -343,6 +354,12 @@ size_t FlipFramer::ProcessControlFramePayload(const char* data, size_t len) { FlipControlFrame* control_frame = reinterpret_cast<FlipControlFrame*>(current_frame_buffer_); visitor_->OnControl(control_frame); + + // If this is a FIN, tell the caller. + if (control_frame->type() == SYN_REPLY && + control_frame->flags() & CONTROL_FLAG_FIN) + visitor_->OnStreamFrameData(control_frame->stream_id(), NULL, 0); + CHANGE_STATE(FLIP_IGNORE_REMAINING_PAYLOAD); } while (false); return original_len - len; @@ -403,15 +420,13 @@ bool FlipFramer::ParseHeaderBlock(const FlipFrame* frame, } FlipSynStreamControlFrame* FlipFramer::CreateSynStream( - int stream_id, - int priority, - bool compress, - FlipHeaderBlock* headers) { + FlipStreamId stream_id, int priority, FlipControlFlags flags, + bool compressed, FlipHeaderBlock* headers) { FlipFrameBuilder frame; frame.WriteUInt16(kControlFlagMask | kFlipProtocolVersion); frame.WriteUInt16(SYN_STREAM); - frame.WriteUInt32(0); // Placeholder for the length. + frame.WriteUInt32(0); // Placeholder for the length and flags frame.WriteUInt32(stream_id); frame.WriteUInt16(ntohs(priority) << 6); // Priority. @@ -421,9 +436,16 @@ FlipSynStreamControlFrame* FlipFramer::CreateSynStream( frame.WriteString(it->first); frame.WriteString(it->second); } - // write the length - frame.WriteUInt32ToOffset(4, frame.length() - sizeof(FlipFrame)); - if (compress) { + + // Write the length and flags. + size_t length = frame.length() - sizeof(FlipFrame); + DCHECK(length < static_cast<size_t>(kLengthMask)); + FlagsAndLength flags_length; + flags_length.length_ = htonl(static_cast<uint32>(length)); + flags_length.flags_[0] = flags; + frame.WriteBytesToOffset(4, &flags_length, sizeof(flags_length)); + + if (compressed) { FlipSynStreamControlFrame* new_frame = reinterpret_cast<FlipSynStreamControlFrame*>( CompressFrame(frame.data())); @@ -434,7 +456,7 @@ FlipSynStreamControlFrame* FlipFramer::CreateSynStream( } /* static */ -FlipFinStreamControlFrame* FlipFramer::CreateFinStream(int stream_id, +FlipFinStreamControlFrame* FlipFramer::CreateFinStream(FlipStreamId stream_id, int status) { FlipFrameBuilder frame; frame.WriteUInt16(kControlFlagMask | kFlipProtocolVersion); @@ -445,16 +467,16 @@ FlipFinStreamControlFrame* FlipFramer::CreateFinStream(int stream_id, return reinterpret_cast<FlipFinStreamControlFrame*>(frame.take()); } -FlipSynReplyControlFrame* FlipFramer::CreateSynReply(int stream_id, - bool compressed, FlipHeaderBlock* headers) { +FlipSynReplyControlFrame* FlipFramer::CreateSynReply(FlipStreamId stream_id, + FlipControlFlags flags, bool compressed, FlipHeaderBlock* headers) { FlipFrameBuilder frame; frame.WriteUInt16(kControlFlagMask | kFlipProtocolVersion); frame.WriteUInt16(SYN_REPLY); - frame.WriteUInt32(0); // Placeholder for the length. + frame.WriteUInt32(0); // Placeholder for the length and flags. frame.WriteUInt32(stream_id); - frame.WriteUInt16(0); // Priority. + frame.WriteUInt16(0); // Unused frame.WriteUInt16(headers->size()); // Number of headers. FlipHeaderBlock::iterator it; @@ -463,28 +485,49 @@ FlipSynReplyControlFrame* FlipFramer::CreateSynReply(int stream_id, frame.WriteString(it->first); frame.WriteString(it->second); } - // write the length - frame.WriteUInt32ToOffset(4, frame.length() - sizeof(FlipFrame)); + + // Write the length + size_t length = frame.length() - sizeof(FlipFrame); + DCHECK(length < static_cast<size_t>(kLengthMask)); + FlagsAndLength flags_length; + flags_length.length_ = htonl(static_cast<uint32>(length)); + flags_length.flags_[0] = flags; + frame.WriteBytesToOffset(4, &flags_length, sizeof(flags_length)); + if (compressed) return reinterpret_cast<FlipSynReplyControlFrame*>( CompressFrame(frame.data())); return reinterpret_cast<FlipSynReplyControlFrame*>(frame.take()); } -FlipDataFrame* FlipFramer::CreateDataFrame(int stream_id, +FlipDataFrame* FlipFramer::CreateDataFrame(FlipStreamId stream_id, const char* data, - int len, bool compressed) { + uint32 len, FlipDataFlags flags) { FlipFrameBuilder frame; frame.WriteUInt32(stream_id); - frame.WriteUInt32(len); + DCHECK(len < static_cast<size_t>(kLengthMask)); + FlagsAndLength flags_length; + flags_length.length_ = htonl(len); + flags_length.flags_[0] = flags; + frame.WriteBytes(&flags_length, sizeof(flags_length)); + frame.WriteBytes(data, len); - if (compressed) + if (flags & DATA_FLAG_COMPRESSED) return reinterpret_cast<FlipDataFrame*>(CompressFrame(frame.data())); return reinterpret_cast<FlipDataFrame*>(frame.take()); } +/* static */ +FlipControlFrame* FlipFramer::CreateNopFrame() { + FlipFrameBuilder frame; + frame.WriteUInt16(kControlFlagMask | kFlipProtocolVersion); + frame.WriteUInt16(NOOP); + frame.WriteUInt32(0); + return reinterpret_cast<FlipControlFrame*>(frame.take()); +} + static const int kCompressorLevel = Z_DEFAULT_COMPRESSION; // This is just a hacked dictionary to use for shrinking HTTP-like headers. // TODO(mbelshe): Use a scientific methodology for computing the dictionary. diff --git a/net/flip/flip_framer.h b/net/flip/flip_framer.h index 5275017..8f89c30 100644 --- a/net/flip/flip_framer.h +++ b/net/flip/flip_framer.h @@ -29,6 +29,7 @@ namespace flip { class FlipFramer; class FlipFramerTest; +class TestFlipVisitor; // A datastructure for holding a set of headers from either a // SYN_STREAM or SYN_REPLY frame. @@ -48,6 +49,11 @@ class FlipFramerVisitorInterface { virtual void OnControl(const FlipControlFrame* frame) = 0; // Called when data is received. + // |stream_id| The stream receiving data. + // |data| A buffer containing the data received. + // |len| The length of the data buffer. + // When the other side has finished sending data on this stream, + // this method will be called with a zero-length buffer. virtual void OnStreamFrameData(flip::FlipStreamId stream_id, const char* data, size_t len) = 0; @@ -120,24 +126,44 @@ class FlipFramer { // Returns true if successfully parsed, false otherwise. bool ParseHeaderBlock(const FlipFrame* frame, FlipHeaderBlock* block); - // Frame creation utilities - // Create a FlipSynStreamControlFrame. The resulting frame will be - // compressed if |compressed| is true. - FlipSynStreamControlFrame* CreateSynStream(int stream_id, int priority, - bool compress, + // Create a FlipSynStreamControlFrame. + // |stream_id| is the stream for this frame. + // |priority| is the priority (0-3) for this frame. + // |flags| is the flags to use with the data. + // To mark this frame as the last frame, enable CONTROL_FLAG_FIN. + // |compressed| specifies whether the frame should be compressed. + // |headers| is the header block to include in the frame. + FlipSynStreamControlFrame* CreateSynStream(FlipStreamId stream_id, + int priority, + FlipControlFlags flags, + bool compressed, FlipHeaderBlock* headers); - static FlipFinStreamControlFrame* CreateFinStream(int stream_id, int status); - // Create a FlipSynReplyControlFrame.The resulting frame will be - // compressed if |compressed| is true. - FlipSynReplyControlFrame* CreateSynReply(int stream_id, - bool compress, + static FlipFinStreamControlFrame* CreateFinStream(FlipStreamId stream_id, + int status); + + // Create a FlipSynReplyControlFrame. + // |stream_id| is the stream for this frame. + // |flags| is the flags to use with the data. + // To mark this frame as the last frame, enable CONTROL_FLAG_FIN. + // |compressed| specifies whether the frame should be compressed. + // |headers| is the header block to include in the frame. + FlipSynReplyControlFrame* CreateSynReply(FlipStreamId stream_id, + FlipControlFlags flags, + bool compressed, FlipHeaderBlock* headers); - // Create a FlipDataFrame. The resulting frame will be - // compressed if |compressed| is true. - FlipDataFrame* CreateDataFrame(int stream_id, const char* data, - int len, bool compressed); + // Create a data frame. + // |stream_id| is the stream for this frame + // |data| is the data to be included in the frame. + // |len| is the length of the data + // |flags| is the flags to use with the data. + // To create a compressed frame, enable DATA_FLAG_COMPRESSED. + // To mark this frame as the last data frame, enable DATA_FLAG_FIN. + FlipDataFrame* CreateDataFrame(FlipStreamId stream_id, const char* data, + uint32 len, FlipDataFlags flags); + + static FlipControlFrame* CreateNopFrame(); // NOTES about frame compression. // We want flip to compress headers across the entire session. As long as @@ -172,9 +198,9 @@ class FlipFramer { static const char* ErrorCodeToString(int error_code); protected: - FRIEND_TEST(FlipFramerTest, Basic); FRIEND_TEST(FlipFramerTest, HeaderBlockBarfsOnOutOfOrderHeaders); friend class net::FlipNetworkTransactionTest; + friend class flip::TestFlipVisitor; // For ease of testing we can tweak compression on/off. void set_enable_compression(bool value); diff --git a/net/flip/flip_framer_test.cc b/net/flip/flip_framer_test.cc index 979c541..bee0432 100644 --- a/net/flip/flip_framer_test.cc +++ b/net/flip/flip_framer_test.cc @@ -19,22 +19,27 @@ class FlipFramerTest : public PlatformTest { class TestFlipVisitor : public FlipFramerVisitorInterface { public: - explicit TestFlipVisitor(FlipFramer* framer) - : framer_(framer), - error_count_(0), + explicit TestFlipVisitor() + : error_count_(0), syn_frame_count_(0), syn_reply_frame_count_(0), - data_frame_count_(0), - fin_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) { - data_frame_count_++; + if (len == 0) + zero_length_data_frame_count_++; + + data_bytes_ += len; #ifdef TEST_LOGGING std::cerr << "OnStreamFrameData(" << stream_id << ", \""; if (len > 0) { @@ -51,13 +56,13 @@ class TestFlipVisitor : public FlipFramerVisitorInterface { bool parsed_headers = false; switch (frame->type()) { case SYN_STREAM: - parsed_headers = framer_->ParseHeaderBlock( + parsed_headers = framer_.ParseHeaderBlock( reinterpret_cast<const FlipFrame*>(frame), &headers); DCHECK(parsed_headers); syn_frame_count_++; break; case SYN_REPLY: - parsed_headers = framer_->ParseHeaderBlock( + parsed_headers = framer_.ParseHeaderBlock( reinterpret_cast<const FlipFrame*>(frame), &headers); DCHECK(parsed_headers); syn_reply_frame_count_++; @@ -68,18 +73,44 @@ class TestFlipVisitor : public FlipFramerVisitorInterface { default: DCHECK(false); // Error! } + if (frame->flags() & CONTROL_FLAG_FIN) + fin_flag_count_++; } void OnLameDuck() { } - FlipFramer* framer_; + // 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_frame_count_; - int fin_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. }; // Test our protocol constants @@ -125,7 +156,7 @@ TEST_F(FlipFramerTest, ControlFrameStructs) { FlipHeaderBlock headers; scoped_array<FlipSynStreamControlFrame> syn_frame( - framer.CreateSynStream(123, 2, false, &headers)); + framer.CreateSynStream(123, 2, CONTROL_FLAG_NONE, false, &headers)); EXPECT_EQ(kFlipProtocolVersion, syn_frame.get()->version()); EXPECT_EQ(true, syn_frame.get()->is_control_frame()); EXPECT_EQ(SYN_STREAM, syn_frame.get()->type()); @@ -134,7 +165,7 @@ TEST_F(FlipFramerTest, ControlFrameStructs) { EXPECT_EQ(2, syn_frame.get()->header_block_len()); scoped_array<FlipSynReplyControlFrame> syn_reply( - framer.CreateSynReply(123, false, &headers)); + framer.CreateSynReply(123, CONTROL_FLAG_NONE, false, &headers)); EXPECT_EQ(kFlipProtocolVersion, syn_reply.get()->version()); EXPECT_EQ(true, syn_reply.get()->is_control_frame()); EXPECT_EQ(SYN_REPLY, syn_reply.get()->type()); @@ -161,7 +192,7 @@ TEST_F(FlipFramerTest, HeaderBlock) { // Encode the header block into a SynStream frame. scoped_array<FlipSynStreamControlFrame> frame( - framer.CreateSynStream(1, 1, true, &headers)); + framer.CreateSynStream(1, 1, CONTROL_FLAG_NONE, true, &headers)); EXPECT_TRUE(frame.get() != NULL); FlipHeaderBlock new_headers; @@ -209,9 +240,9 @@ TEST_F(FlipFramerTest, BasicCompression) { FlipFramer framer; scoped_array<FlipSynStreamControlFrame> - frame1(framer.CreateSynStream(1, 1, true, &headers)); + frame1(framer.CreateSynStream(1, 1, CONTROL_FLAG_NONE, true, &headers)); scoped_array<FlipSynStreamControlFrame> - frame2(framer.CreateSynStream(1, 1, true, &headers)); + frame2(framer.CreateSynStream(1, 1, CONTROL_FLAG_NONE, true, &headers)); // Expect the second frame to be more compact than the first. EXPECT_LE(frame2.get()->length(), frame1.get()->length()); @@ -273,25 +304,86 @@ TEST_F(FlipFramerTest, Basic) { 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<const char*>(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(); - } + 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(4, visitor.data_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 flip diff --git a/net/flip/flip_protocol.h b/net/flip/flip_protocol.h index c737473..8a585bc 100644 --- a/net/flip/flip_protocol.h +++ b/net/flip/flip_protocol.h @@ -89,23 +89,25 @@ const int kFlipProtocolVersion = 1; // accessors provided or call ntohX() functions. // Types of Flip Control Frames. -typedef enum { +enum FlipControlType { SYN_STREAM = 1, SYN_REPLY, FIN_STREAM, NOOP -} FlipControlType; +}; // Flags on data packets -typedef enum { +enum FlipDataFlags { + DATA_FLAG_NONE = 0, DATA_FLAG_FIN = 1, DATA_FLAG_COMPRESSED = 2 // TODO(mbelshe): remove me. -} FlipDataFlags; +}; // Flags on control packets -typedef enum { +enum FlipControlFlags { + CONTROL_FLAG_NONE = 0, CONTROL_FLAG_FIN = 1 -} FlipControlFlags; +}; // A FLIP stream id is a 31 bit entity. typedef uint32 FlipStreamId; @@ -115,10 +117,10 @@ typedef uint32 FlipStreamId; #define FLIP_PRIORITY_HIGHEST 0 // A special structure for the 8 bit flags and 24 bit length fields. -typedef union { +union FlagsAndLength { uint8 flags_[4]; // 8 bits uint32 length_; // 24 bits -} FlagsLength; +}; // All Flip Frame types derive from the FlipFrame struct. typedef struct { @@ -146,7 +148,7 @@ typedef struct { FlipStreamId stream_id_; } data_; }; - FlagsLength flags_length_; + FlagsAndLength flags_length_; } FlipFrame; // A Data Frame. diff --git a/net/flip/flip_session.cc b/net/flip/flip_session.cc index 51631f9..c8cb53d 100644 --- a/net/flip/flip_session.cc +++ b/net/flip/flip_session.cc @@ -267,7 +267,8 @@ int FlipSession::CreateStream(FlipDelegate* delegate) { // Create a SYN_STREAM packet and add to the output queue. flip::FlipSynStreamControlFrame* syn_frame = - flip_framer_.CreateSynStream(stream_id, priority, false, &headers); + flip_framer_.CreateSynStream(stream_id, priority, + flip::CONTROL_FLAG_NONE, false, &headers); int length = sizeof(flip::FlipFrame) + syn_frame->length(); IOBufferWithSize* buffer = new IOBufferWithSize(length); |