Index: net/spdy/spdy_framer.h |
=================================================================== |
--- net/spdy/spdy_framer.h (revision 126086) |
+++ net/spdy/spdy_framer.h (working copy) |
@@ -37,19 +37,51 @@ |
namespace spdy { |
class SpdyFramer; |
-class SpdyFramerTest; |
+class SpdyFrameBuilder; |
+class SpdyFramerSpdy2Test; |
+class SpdyFramerSpdy3Test; |
-namespace test { |
+namespace test_spdy2 { |
+ |
class TestSpdyVisitor; |
-void FramerSetEnableCompressionHelper(SpdyFramer* framer, bool compress); |
-} // namespace test |
+} // namespace test_spdy2 |
+ |
+namespace test_spdy3 { |
+ |
+class TestSpdyVisitor; |
+ |
+} // namespace test_spdy3 |
+ |
// A datastructure for holding a set of headers from either a |
// SYN_STREAM or SYN_REPLY frame. |
typedef std::map<std::string, std::string> SpdyHeaderBlock; |
+// A datastructure for holding the ID and flag fields for SETTINGS. |
+// Conveniently handles converstion to/from wire format. |
+class NET_EXPORT_PRIVATE SettingsFlagsAndId { |
+ public: |
+ static SettingsFlagsAndId FromWireFormat(int version, uint32 wire); |
+ |
+ SettingsFlagsAndId() : flags_(0), id_(0) {} |
+ |
+ // TODO(hkhalil): restrict to enums instead of free-form ints. |
+ SettingsFlagsAndId(uint8 flags, uint32 id); |
+ |
+ uint32 GetWireFormat(int version) const; |
+ |
+ uint32 id() const { return id_; } |
+ uint8 flags() const { return flags_; } |
+ |
+ private: |
+ static void ConvertFlagsAndIdForSpdy2(uint32* val); |
+ |
+ uint8 flags_; |
+ uint32 id_; |
+}; |
+ |
// A datastructure for holding a set of ID/value pairs for a SETTINGS frame. |
-typedef std::pair<spdy::SettingsFlagsAndId, uint32> SpdySetting; |
+typedef std::pair<SettingsFlagsAndId, uint32> SpdySetting; |
typedef std::list<SpdySetting> SpdySettings; |
// A datastrcture for holding the contents of a CREDENTIAL frame. |
@@ -62,6 +94,27 @@ |
std::string proof; |
}; |
+// Scratch space necessary for processing SETTINGS frames. |
+struct NET_EXPORT_PRIVATE SpdySettingsScratch { |
+ SpdySettingsScratch() { Reset(); } |
+ |
+ void Reset() { |
+ setting_buf_len = 0; |
+ last_setting_id = 0; |
+ } |
+ |
+ // Buffer contains up to one complete key/value pair. |
+ char setting_buf[8]; |
+ |
+ // The amount of the buffer that is filled with valid data. |
+ size_t setting_buf_len; |
+ |
+ // The ID of the last setting that was processed in the current SETTINGS |
+ // frame. Used for detecting out-of-order or duplicate keys within a settings |
+ // frame. Set to 0 before first key/value pair is processed. |
+ uint32 last_setting_id; |
+}; |
+ |
// SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer. |
// Implement this interface to receive event callbacks as frames are |
// decoded from the framer. |
@@ -113,13 +166,13 @@ |
// Called when a chunk of payload data for a credential frame is available. |
// This is called after OnControl() is called with the credential frame |
// associated with the payload being delivered here. |
- // |frame_data| A buffer containing the header data chunk received. |
+ // |header_data| A buffer containing the header data chunk received. |
// |len| The length of the header data buffer. A length of zero indicates |
// that the header data block has been completely sent. |
// When this function returns true the visitor indicates that it accepted |
// all of the data. Returning false indicates that that an unrecoverable |
// error has occurred, such as bad header data or resource exhaustion. |
- virtual bool OnCredentialFrameData(const char* frame_data, |
+ virtual bool OnCredentialFrameData(const char* header_data, |
size_t len) = 0; |
// Called when a data frame header is received. The frame's data |
@@ -136,6 +189,10 @@ |
virtual void OnStreamFrameData(SpdyStreamId stream_id, |
const char* data, |
size_t len) = 0; |
+ |
+ // Called when a complete setting within a SETTINGS frame has been parsed and |
+ // validated. |
+ virtual void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) = 0; |
}; |
class NET_EXPORT_PRIVATE SpdyFramer { |
@@ -155,6 +212,7 @@ |
SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, |
SPDY_CONTROL_FRAME_HEADER_BLOCK, |
SPDY_CREDENTIAL_FRAME_PAYLOAD, |
+ SPDY_SETTINGS_FRAME_PAYLOAD, |
}; |
// SPDY error codes. |
@@ -179,8 +237,8 @@ |
// purposes.) |
static const size_t kHeaderDataChunkMaxSize; |
- // Create a new Framer. |
- SpdyFramer(); |
+ // Create a new Framer, provided a SPDY version. |
+ explicit SpdyFramer(int version); |
virtual ~SpdyFramer(); |
// Set callbacks to be called from the framer. A visitor must be set, or |
@@ -209,31 +267,26 @@ |
} |
bool HasError() { return state_ == SPDY_ERROR; } |
- // Further parsing utilities. |
- // Given a control frame, parse out a SpdyHeaderBlock. Only |
- // valid for SYN_STREAM and SYN_REPLY frames. |
- // Returns true if successfully parsed, false otherwise. |
- bool ParseHeaderBlock(const SpdyFrame* frame, SpdyHeaderBlock* block); |
- |
// Given a buffer containing a decompressed header block in SPDY |
// serialized format, parse out a SpdyHeaderBlock, putting the results |
// in the given header block. |
// Returns true if successfully parsed, false otherwise. |
- static bool ParseHeaderBlockInBuffer(const char* header_data, |
- size_t header_length, |
- SpdyHeaderBlock* block); |
+ bool ParseHeaderBlockInBuffer(const char* header_data, |
+ size_t header_length, |
+ SpdyHeaderBlock* block); |
// Create a SpdySynStreamControlFrame. |
// |stream_id| is the id for this stream. |
// |associated_stream_id| is the associated stream id for this stream. |
- // |priority| is the priority (0-3) for this stream. |
+ // |priority| is the priority (GetHighestPriority()-GetLowestPriority) for |
+ // this stream. |
// |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. |
SpdySynStreamControlFrame* CreateSynStream(SpdyStreamId stream_id, |
SpdyStreamId associated_stream_id, |
- int priority, |
+ SpdyPriority priority, |
SpdyControlFlags flags, |
bool compressed, |
const SpdyHeaderBlock* headers); |
@@ -249,26 +302,23 @@ |
bool compressed, |
const SpdyHeaderBlock* headers); |
- static SpdyRstStreamControlFrame* CreateRstStream(SpdyStreamId stream_id, |
- SpdyStatusCodes status); |
+ SpdyRstStreamControlFrame* CreateRstStream(SpdyStreamId stream_id, |
+ SpdyStatusCodes status) const; |
// Creates an instance of SpdySettingsControlFrame. The SETTINGS frame is |
// used to communicate name/value pairs relevant to the communication channel. |
- // TODO(mbelshe): add the name/value pairs!! |
- static SpdySettingsControlFrame* CreateSettings(const SpdySettings& values); |
+ SpdySettingsControlFrame* CreateSettings(const SpdySettings& values) const; |
- static SpdyNoOpControlFrame* CreateNopFrame(); |
- |
// Creates an instance of SpdyPingControlFrame. The unique_id is used to |
// identify the ping request/response. |
- static SpdyPingControlFrame* CreatePingFrame(uint32 unique_id); |
+ SpdyPingControlFrame* CreatePingFrame(uint32 unique_id) const; |
// Creates an instance of SpdyGoAwayControlFrame. The GOAWAY frame is used |
// prior to the shutting down of the TCP connection, and includes the |
// stream_id of the last stream the sender of the frame is willing to process |
// to completion. |
- static SpdyGoAwayControlFrame* CreateGoAway( |
- SpdyStreamId last_accepted_stream_id); |
+ SpdyGoAwayControlFrame* CreateGoAway( |
+ SpdyStreamId last_accepted_stream_id) const; |
// Creates an instance of SpdyHeadersControlFrame. The HEADERS frame is used |
// for sending additional headers outside of a SYN_STREAM/SYN_REPLY. The |
@@ -280,15 +330,15 @@ |
// Creates an instance of SpdyWindowUpdateControlFrame. The WINDOW_UPDATE |
// frame is used to implement per stream flow control in SPDY. |
- static SpdyWindowUpdateControlFrame* CreateWindowUpdate( |
+ SpdyWindowUpdateControlFrame* CreateWindowUpdate( |
SpdyStreamId stream_id, |
- uint32 delta_window_size); |
+ uint32 delta_window_size) const; |
// Creates an instance of SpdyCredentialControlFrame. The CREDENTIAL |
// frame is used to send a client certificate to the server when |
// request more than one origin are sent over the same SPDY session. |
- static SpdyCredentialControlFrame* CreateCredentialFrame( |
- const SpdyCredential& credential); |
+ SpdyCredentialControlFrame* CreateCredentialFrame( |
+ const SpdyCredential& credential) const; |
// Given a SpdySettingsControlFrame, extract the settings. |
// Returns true on successful parse, false otherwise. |
@@ -330,13 +380,6 @@ |
// On failure, returns NULL. |
SpdyFrame* CompressFrame(const SpdyFrame& frame); |
- // Decompresses a SpdyFrame. |
- // On success, returns a new SpdyFrame with the payload decompressed. |
- // Compression state is maintained as part of the SpdyFramer. |
- // Returned frame must be freed with "delete". |
- // On failure, returns NULL. |
- SpdyFrame* DecompressFrame(const SpdyFrame& frame); |
- |
// Create a copy of a frame. |
// Returned frame must be freed with "delete". |
SpdyFrame* DuplicateFrame(const SpdyFrame& frame); |
@@ -373,18 +416,44 @@ |
static const char* StatusCodeToString(int status_code); |
static const char* ControlTypeToString(SpdyControlType type); |
- static void set_protocol_version(int version) { spdy_version_= version; } |
- static int protocol_version() { return spdy_version_; } |
+ // TODO(hkhalil): Remove SpdyFramer::set_protocol_version() |
+ void set_protocol_version(int version) { spdy_version_= version; } |
+ int protocol_version() const { return spdy_version_; } |
- // Export the compression dictionary |
- static const char kDictionary[]; |
- static const int kDictionarySize; |
+ bool probable_http_response() { return probable_http_response_; } |
+ SpdyPriority GetLowestPriority() const { return spdy_version_ < 3 ? 3 : 7; } |
+ SpdyPriority GetHighestPriority() const { return 0; } |
+ |
protected: |
- FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, HeaderCompression); |
- FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ExpandBuffer_HeapSmash); |
- FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, HugeHeaderBlock); |
- FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, UnclosedStreamDataCompressors); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, BasicCompression); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, ControlFrameSizesAreValidated); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, HeaderCompression); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, DecompressUncompressedFrame); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, ExpandBuffer_HeapSmash); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, HugeHeaderBlock); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, UnclosedStreamDataCompressors); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, |
+ UnclosedStreamDataCompressorsOneByteAtATime); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, |
+ UncompressLargerThanFrameBufferInitialSize); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, ReadLargeSettingsFrame); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, |
+ ReadLargeSettingsFrameInSmallChunks); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, BasicCompression); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, ControlFrameSizesAreValidated); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, HeaderCompression); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, DecompressUncompressedFrame); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, ExpandBuffer_HeapSmash); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, HugeHeaderBlock); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, UnclosedStreamDataCompressors); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, |
+ UnclosedStreamDataCompressorsOneByteAtATime); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, |
+ UncompressLargerThanFrameBufferInitialSize); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, ReadLargeSettingsFrame); |
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, |
+ ReadLargeSettingsFrameInSmallChunks); |
friend class net::HttpNetworkLayer; // This is temporary for the server. |
friend class net::HttpNetworkTransactionTest; |
friend class net::HttpProxyClientSocketPoolTest; |
@@ -395,45 +464,37 @@ |
friend class net::SpdyStreamTest; |
friend class net::SpdyWebSocketStreamTest; |
friend class net::WebSocketJobTest; |
- friend class test::TestSpdyVisitor; |
- friend void test::FramerSetEnableCompressionHelper(SpdyFramer* framer, |
- bool compress); |
+ friend class test_spdy2::TestSpdyVisitor; |
+ friend class test_spdy3::TestSpdyVisitor; |
private: |
typedef std::map<SpdyStreamId, z_stream*> CompressorMap; |
- // Internal breakout from ProcessInput. Returns the number of bytes |
+ // Internal breakouts from ProcessInput. Each 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 ProcessCredentialFramePayload(const char* data, size_t len); |
size_t ProcessControlFrameBeforeHeaderBlock(const char* data, size_t len); |
size_t ProcessControlFrameHeaderBlock(const char* data, size_t len); |
+ size_t ProcessSettingsFramePayload(const char* data, size_t len); |
size_t ProcessDataFramePayload(const char* data, size_t len); |
+ // Helpers for above internal breakouts from ProcessInput. |
+ void ProcessControlFrameHeader(); |
+ bool ProcessSetting(const char* data); // Always passed exactly 8 bytes. |
+ |
// Get (and lazily initialize) the ZLib state. |
z_stream* GetHeaderCompressor(); |
z_stream* GetHeaderDecompressor(); |
- z_stream* GetStreamCompressor(SpdyStreamId id); |
z_stream* GetStreamDecompressor(SpdyStreamId id); |
// Compression helpers |
SpdyControlFrame* CompressControlFrame(const SpdyControlFrame& frame); |
- SpdyDataFrame* CompressDataFrame(const SpdyDataFrame& frame); |
- SpdyControlFrame* DecompressControlFrame(const SpdyControlFrame& frame); |
- SpdyDataFrame* DecompressDataFrame(const SpdyDataFrame& frame); |
- SpdyFrame* CompressFrameWithZStream(const SpdyFrame& frame, |
- z_stream* compressor); |
- SpdyFrame* DecompressFrameWithZStream(const SpdyFrame& frame, |
- z_stream* decompressor); |
void CleanupCompressorForStream(SpdyStreamId id); |
void CleanupDecompressorForStream(SpdyStreamId id); |
void CleanupStreamCompressorsAndDecompressors(); |
- // Not used (yet) |
- size_t BytesSafeToRead() const; |
- |
// Deliver the given control frame's compressed headers block to the visitor |
// in decompressed form, in chunks. Returns true if the visitor has |
// accepted all of the chunks. |
@@ -458,6 +519,13 @@ |
size_t UpdateCurrentFrameBuffer(const char** data, size_t* len, |
size_t max_bytes); |
+ // Retrieve serialized length of SpdyHeaderBlock. |
+ size_t GetSerializedLength(const SpdyHeaderBlock* headers) const; |
+ |
+ // Serializes a SpdyHeaderBlock. |
+ void WriteHeaderBlock(SpdyFrameBuilder* frame, |
+ const SpdyHeaderBlock* headers) const; |
+ |
// Set the error code and moves the framer into the error state. |
void set_error(SpdyError error); |
@@ -486,19 +554,14 @@ |
// Since this is only used for control frame headers, the maximum control |
// frame header size (18B) is sufficient; all remaining control frame data is |
// streamed to the visitor. |
- // In addition to the maximum control frame header size, we account for any |
- // LOAS checksumming (16B) that may occur in the VTL case. |
// TODO(hkhalil): Remove post code-yellow once streamed inflate is properly |
// implemented. |
- static const size_t kUncompressedControlFrameBufferInitialSize = 18 + 16; |
+ static size_t kUncompressedControlFrameBufferInitialSize; |
// 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; |
- |
SpdyState state_; |
SpdyError error_code_; |
size_t remaining_data_; |
@@ -516,6 +579,11 @@ |
size_t current_frame_len_; // Number of bytes read into the current_frame_. |
size_t current_frame_capacity_; |
+ // Scratch space for handling SETTINGS frames. |
+ // TODO(hkhalil): Unify memory for this scratch space with |
+ // current_frame_buffer_. |
+ SpdySettingsScratch settings_scratch_; |
+ |
bool validate_control_frame_sizes_; |
bool enable_compression_; // Controls all compression |
// SPDY header compressors. |
@@ -530,8 +598,24 @@ |
std::string display_protocol_; |
+ int spdy_version_; |
+ |
+ // Tracks if we've ever gotten far enough in framing to see a control frame of |
+ // type SYN_STREAM or SYN_REPLY. |
+ // |
+ // If we ever get something which looks like a data frame before we've had a |
+ // SYN, we explicitly check to see if it looks like we got an HTTP response to |
+ // a SPDY request. This boolean lets us do that. |
+ bool syn_frame_processed_; |
+ |
+ // If we ever get a data frame before a SYN frame, we check to see if it |
+ // starts with HTTP. If it does, we likely have an HTTP response. This |
+ // isn't guaranteed though: we could have gotten a settings frame and then |
+ // corrupt data that just looks like HTTP, but deterministic checking requires |
+ // a lot more state. |
+ bool probable_http_response_; |
+ |
static bool compression_default_; |
- static int spdy_version_; |
}; |
} // namespace spdy |