// 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. #ifndef NET_FLIP_FLIP_FRAMER_H_ #define NET_FLIP_FLIP_FRAMER_H_ #ifdef _WIN32 #include <winsock2.h> #else #include <arpa/inet.h> #endif #include <map> #include <string> #include "base/basictypes.h" #include "base/logging.h" #include "base/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest_prod.h" #include "flip_protocol.h" // cross-google3 directory naming. typedef struct z_stream_s z_stream; // Forward declaration for zlib. namespace net { class FlipNetworkTransactionTest; class HttpNetworkLayer; } namespace flip { class FlipFramer; class FlipFramerTest; namespace test { class TestFlipVisitor; void FramerSetEnableCompressionHelper(FlipFramer* framer, bool compress); } // namespace test // A datastructure for holding a set of headers from either a // SYN_STREAM or SYN_REPLY frame. typedef std::map<std::string, std::string> FlipHeaderBlock; // FlipFramerVisitorInterface is a set of callbacks for the FlipFramer. // Implement this interface to receive event callbacks as frames are // decoded from the framer. class FlipFramerVisitorInterface { public: virtual ~FlipFramerVisitorInterface() {} // Called if an error is detected in the FlipFrame protocol. virtual void OnError(FlipFramer* framer) = 0; // Called when a Control Frame is received. 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; }; class FlipFramer { public: // Flip states. // TODO(mbelshe): Can we move these into the implementation // and avoid exposing through the header. (Needed for test) enum FlipState { FLIP_ERROR, FLIP_DONE, FLIP_RESET, FLIP_AUTO_RESET, FLIP_READING_COMMON_HEADER, FLIP_INTERPRET_CONTROL_FRAME_COMMON_HEADER, FLIP_CONTROL_FRAME_PAYLOAD, FLIP_IGNORE_REMAINING_PAYLOAD, FLIP_FORWARD_STREAM_FRAME }; // Flip error codes. enum FlipError { FLIP_NO_ERROR, FLIP_UNKNOWN_CONTROL_TYPE, // Control frame is an unknown type. FLIP_INVALID_CONTROL_FRAME, // Control frame is mal-formatted. FLIP_CONTROL_PAYLOAD_TOO_LARGE, // Control frame payload was too large. FLIP_ZLIB_INIT_FAILURE, // The Zlib library could not initialize. FLIP_UNSUPPORTED_VERSION, // Control frame has unsupported version. FLIP_DECOMPRESS_FAILURE, // There was an error decompressing. }; // Create a new Framer. FlipFramer(); virtual ~FlipFramer(); // Set callbacks to be called from the framer. A visitor must be set, or // else the framer will likely crash. It is acceptable for the visitor // to do nothing. If this is called multiple times, only the last visitor // will be used. void set_visitor(FlipFramerVisitorInterface* visitor) { visitor_ = visitor; } // Pass data into the framer for parsing. // Returns the number of bytes consumed. It is safe to pass more bytes in // than may be consumed. size_t ProcessInput(const char* data, size_t len); // Resets the framer state after a frame has been successfully decoded. // TODO(mbelshe): can we make this private? void Reset(); // Check the state of the framer. FlipError error_code() const { return error_code_; } FlipState state() const { return state_; } bool MessageFullyRead() { return state_ == FLIP_DONE || state_ == FLIP_AUTO_RESET; } bool HasError() { return state_ == FLIP_ERROR; } // Further parsing utilities. // Given a control frame, parse out a FlipHeaderBlock. Only // valid for SYN_STREAM and SYN_REPLY frames. // Returns true if successfully parsed, false otherwise. bool ParseHeaderBlock(const FlipFrame* frame, FlipHeaderBlock* block); // 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(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 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 // the session is over TCP, frames are sent serially. The client & server // can each compress frames in the same order and then compress them in that // order, and the remote can do the reverse. However, we ultimately want // the creation of frames to be less sensitive to order so that they can be // placed over a UDP based protocol and yet still benefit from some // compression. We don't know of any good compression protocol which does // not build its state in a serial (stream based) manner.... For now, we're // using zlib anyway. // Compresses a FlipFrame. // On success, returns a new FlipFrame with the payload compressed. // Compression state is maintained as part of the FlipFramer. // Returned frame must be freed with free(). // On failure, returns NULL. FlipFrame* CompressFrame(const FlipFrame* frame); // Decompresses a FlipFrame. // On success, returns a new FlipFrame with the payload decompressed. // Compression state is maintained as part of the FlipFramer. // Returned frame must be freed with free(). // On failure, returns NULL. FlipFrame* DecompressFrame(const FlipFrame* frame); // Create a copy of a frame. FlipFrame* DuplicateFrame(const FlipFrame* frame); // For debugging. static const char* StateToString(int state); static const char* ErrorCodeToString(int error_code); protected: FRIEND_TEST(FlipFramerTest, HeaderBlockBarfsOnOutOfOrderHeaders); friend class net::FlipNetworkTransactionTest; friend class net::HttpNetworkLayer; // This is temporary for the server. friend class test::TestFlipVisitor; friend void test::FramerSetEnableCompressionHelper(FlipFramer* framer, bool compress); // For ease of testing we can tweak compression on/off. void set_enable_compression(bool value); static void set_enable_compression_default(bool value); private: // Internal breakout from ProcessInput. Returns the number of bytes // consumed from the data. size_t ProcessCommonHeader(const char* data, size_t len); void ProcessControlFrameHeader(); size_t ProcessControlFramePayload(const char* data, size_t len); size_t ProcessDataFramePayload(const char* data, size_t len); // Initialize the ZLib state. bool InitializeCompressor(); bool InitializeDecompressor(); // Not used (yet) size_t BytesSafeToRead() const; // Set the error code. void set_error(FlipError error); // Expands the control frame buffer to accomodate a particular payload size. void ExpandControlFrameBuffer(size_t size); // Given a frame, breakdown the variable payload length, the static header // header length, and variable payload pointer. bool GetFrameBoundaries(const FlipFrame* frame, int* payload_length, int* header_length, const char** payload) const; FlipState state_; FlipError error_code_; size_t remaining_payload_; size_t remaining_control_payload_; char* current_frame_buffer_; size_t current_frame_len_; // Number of bytes read into the current_frame_. size_t current_frame_capacity_; bool enable_compression_; scoped_ptr<z_stream> compressor_; scoped_ptr<z_stream> decompressor_; FlipFramerVisitorInterface* visitor_; static bool compression_default_; }; } // namespace flip #endif // NET_FLIP_FLIP_FRAMER_H_