// Copyright (c) 2012 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_SPDY_CORE_SPDY_FRAMER_H_ #define NET_SPDY_CORE_SPDY_FRAMER_H_ #include #include #include #include #include #include "base/sys_byteorder.h" #include "net/spdy/core/hpack/hpack_encoder.h" #include "net/spdy/core/spdy_alt_svc_wire_format.h" #include "net/spdy/core/spdy_header_block.h" #include "net/spdy/core/spdy_headers_handler_interface.h" #include "net/spdy/core/spdy_protocol.h" #include "net/spdy/core/zero_copy_output_buffer.h" #include "net/spdy/platform/api/spdy_export.h" #include "net/spdy/platform/api/spdy_string.h" #include "net/spdy/platform/api/spdy_string_piece.h" namespace net { class HttpNetworkLayer; class HttpNetworkTransactionTest; class HttpProxyClientSocketPoolTest; class SpdyHttpStreamTest; class SpdyNetworkTransactionTest; class SpdyProxyClientSocketTest; class SpdySessionTest; class SpdyStreamTest; namespace test { class SpdyFramerPeer; class SpdyFramerTest_MultipleContinuationFramesWithIterator_Test; class SpdyFramerTest_PushPromiseFramesWithIterator_Test; } // namespace test class SPDY_EXPORT_PRIVATE SpdyFrameSequence { public: virtual ~SpdyFrameSequence() {} // Serializes the next frame in the sequence to |output|. Returns the number // of bytes written to |output|. virtual size_t NextFrame(ZeroCopyOutputBuffer* output) = 0; // Returns true iff there is at least one more frame in the sequence. virtual bool HasNextFrame() const = 0; // Get SpdyFrameIR of the frame to be serialized. virtual const SpdyFrameIR& GetIR() const = 0; }; class SPDY_EXPORT_PRIVATE SpdyFramer { public: enum CompressionOption { ENABLE_COMPRESSION, DISABLE_COMPRESSION, }; // Create a SpdyFrameSequence to serialize |frame_ir|. static std::unique_ptr CreateIterator( SpdyFramer* framer, std::unique_ptr frame_ir); // Gets the serialized flags for the given |frame|. static uint8_t GetSerializedFlags(const SpdyFrameIR& frame); // The maximum size of the control frames that we send, including the size of // the header. This limit is arbitrary. We can enforce it here or at the // application layer. We chose the framing layer, but this can be changed (or // removed) if necessary later down the line. static const size_t kMaxControlFrameSendSize; // Serialize a data frame. static SpdySerializedFrame SerializeData(const SpdyDataIR& data_ir); // Serializes the data frame header and optionally padding length fields, // excluding actual data payload and padding. static SpdySerializedFrame SerializeDataFrameHeaderWithPaddingLengthField( const SpdyDataIR& data_ir); // Serializes a WINDOW_UPDATE frame. The WINDOW_UPDATE // frame is used to implement per stream flow control. static SpdySerializedFrame SerializeWindowUpdate( const SpdyWindowUpdateIR& window_update); explicit SpdyFramer(CompressionOption option); virtual ~SpdyFramer(); // Set debug callbacks to be called from the framer. The debug visitor is // completely optional and need not be set in order for normal operation. // If this is called multiple times, only the last visitor will be used. void set_debug_visitor(SpdyFramerDebugVisitorInterface* debug_visitor); SpdySerializedFrame SerializeRstStream( const SpdyRstStreamIR& rst_stream) const; // Serializes a SETTINGS frame. The SETTINGS frame is // used to communicate name/value pairs relevant to the communication channel. SpdySerializedFrame SerializeSettings(const SpdySettingsIR& settings) const; // Serializes a PING frame. The unique_id is used to // identify the ping request/response. SpdySerializedFrame SerializePing(const SpdyPingIR& ping) const; // Serializes a GOAWAY frame. 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. SpdySerializedFrame SerializeGoAway(const SpdyGoAwayIR& goaway) const; // Serializes a HEADERS frame. The HEADERS frame is used // for sending headers. SpdySerializedFrame SerializeHeaders(const SpdyHeadersIR& headers); // Serializes a PUSH_PROMISE frame. The PUSH_PROMISE frame is used // to inform the client that it will be receiving an additional stream // in response to the original request. The frame includes synthesized // headers to explain the upcoming data. SpdySerializedFrame SerializePushPromise( const SpdyPushPromiseIR& push_promise); // Serializes a CONTINUATION frame. The CONTINUATION frame is used // to continue a sequence of header block fragments. SpdySerializedFrame SerializeContinuation( const SpdyContinuationIR& continuation) const; // Serializes an ALTSVC frame. The ALTSVC frame advertises the // availability of an alternative service to the client. SpdySerializedFrame SerializeAltSvc(const SpdyAltSvcIR& altsvc); // Serializes a PRIORITY frame. The PRIORITY frame advises a change in // the relative priority of the given stream. SpdySerializedFrame SerializePriority(const SpdyPriorityIR& priority) const; // Serializes an unknown frame given a frame header and payload. SpdySerializedFrame SerializeUnknown(const SpdyUnknownIR& unknown) const; // Serialize a frame of unknown type. SpdySerializedFrame SerializeFrame(const SpdyFrameIR& frame); // Serialize a data frame. bool SerializeData(const SpdyDataIR& data, ZeroCopyOutputBuffer* output) const; // Serializes the data frame header and optionally padding length fields, // excluding actual data payload and padding. bool SerializeDataFrameHeaderWithPaddingLengthField( const SpdyDataIR& data, ZeroCopyOutputBuffer* output) const; bool SerializeRstStream(const SpdyRstStreamIR& rst_stream, ZeroCopyOutputBuffer* output) const; // Serializes a SETTINGS frame. The SETTINGS frame is // used to communicate name/value pairs relevant to the communication channel. bool SerializeSettings(const SpdySettingsIR& settings, ZeroCopyOutputBuffer* output) const; // Serializes a PING frame. The unique_id is used to // identify the ping request/response. bool SerializePing(const SpdyPingIR& ping, ZeroCopyOutputBuffer* output) const; // Serializes a GOAWAY frame. 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. bool SerializeGoAway(const SpdyGoAwayIR& goaway, ZeroCopyOutputBuffer* output) const; // Serializes a HEADERS frame. The HEADERS frame is used // for sending headers. bool SerializeHeaders(const SpdyHeadersIR& headers, ZeroCopyOutputBuffer* output); // Serializes a WINDOW_UPDATE frame. The WINDOW_UPDATE // frame is used to implement per stream flow control. bool SerializeWindowUpdate(const SpdyWindowUpdateIR& window_update, ZeroCopyOutputBuffer* output) const; // Serializes a PUSH_PROMISE frame. The PUSH_PROMISE frame is used // to inform the client that it will be receiving an additional stream // in response to the original request. The frame includes synthesized // headers to explain the upcoming data. bool SerializePushPromise(const SpdyPushPromiseIR& push_promise, ZeroCopyOutputBuffer* output); // Serializes a CONTINUATION frame. The CONTINUATION frame is used // to continue a sequence of header block fragments. bool SerializeContinuation(const SpdyContinuationIR& continuation, ZeroCopyOutputBuffer* output) const; // Serializes an ALTSVC frame. The ALTSVC frame advertises the // availability of an alternative service to the client. bool SerializeAltSvc(const SpdyAltSvcIR& altsvc, ZeroCopyOutputBuffer* output); // Serializes a PRIORITY frame. The PRIORITY frame advises a change in // the relative priority of the given stream. bool SerializePriority(const SpdyPriorityIR& priority, ZeroCopyOutputBuffer* output) const; // Serializes an unknown frame given a frame header and payload. bool SerializeUnknown(const SpdyUnknownIR& unknown, ZeroCopyOutputBuffer* output) const; // Serialize a frame of unknown type. size_t SerializeFrame(const SpdyFrameIR& frame, ZeroCopyOutputBuffer* output); // Returns whether this SpdyFramer will compress header blocks using HPACK. bool compression_enabled() const { return compression_option_ == ENABLE_COMPRESSION; } void SetHpackIndexingPolicy(HpackEncoder::IndexingPolicy policy) { GetHpackEncoder()->SetIndexingPolicy(std::move(policy)); } // Updates the maximum size of the header encoder compression table. void UpdateHeaderEncoderTableSize(uint32_t value); // Updates the maximum size of the header decoder compression table. void UpdateHeaderDecoderTableSize(uint32_t value); // Returns the maximum size of the header encoder compression table. size_t header_encoder_table_size() const; void SetEncoderHeaderTableDebugVisitor( std::unique_ptr visitor); // Get (and lazily initialize) the HPACK encoder state. HpackEncoder* GetHpackEncoder(); // Returns the estimate of dynamically allocated memory in bytes. size_t EstimateMemoryUsage() const; protected: friend class BufferedSpdyFramer; friend class HttpNetworkLayer; // This is temporary for the server. friend class HttpNetworkTransactionTest; friend class HttpProxyClientSocketPoolTest; friend class SpdyHttpStreamTest; friend class SpdyNetworkTransactionTest; friend class SpdyProxyClientSocketTest; friend class SpdySessionTest; friend class SpdyStreamTest; friend class test::SpdyFramerPeer; friend class test::SpdyFramerTest_MultipleContinuationFramesWithIterator_Test; friend class test::SpdyFramerTest_PushPromiseFramesWithIterator_Test; // Iteratively converts a SpdyFrameIR into an appropriate sequence of Spdy // frames. // Example usage: // std::unique_ptr it = CreateIterator(framer, frame_ir); // while (it->HasNextFrame()) { // if(it->NextFrame(output) == 0) { // // Write failed; // } // } class SPDY_EXPORT_PRIVATE SpdyFrameIterator : public SpdyFrameSequence { public: // Creates an iterator with the provided framer. // Does not take ownership of |framer|. // |framer| must outlive this instance. explicit SpdyFrameIterator(SpdyFramer* framer); ~SpdyFrameIterator() override; // Serializes the next frame in the sequence to |output|. Returns the number // of bytes written to |output|. size_t NextFrame(ZeroCopyOutputBuffer* output) override; // Returns true iff there is at least one more frame in the sequence. bool HasNextFrame() const override; // SpdyFrameIterator is neither copyable nor movable. SpdyFrameIterator(const SpdyFrameIterator&) = delete; SpdyFrameIterator& operator=(const SpdyFrameIterator&) = delete; protected: virtual size_t GetFrameSizeSansBlock() const = 0; virtual bool SerializeGivenEncoding(const SpdyString& encoding, ZeroCopyOutputBuffer* output) const = 0; SpdyFramer* GetFramer() const { return framer_; } void SetEncoder(const SpdyFrameWithHeaderBlockIR* ir) { encoder_ = framer_->GetHpackEncoder()->EncodeHeaderSet(ir->header_block()); } bool has_next_frame() const { return has_next_frame_; } private: SpdyFramer* const framer_; std::unique_ptr encoder_; bool is_first_frame_; bool has_next_frame_; }; // Iteratively converts a SpdyHeadersIR (with a possibly huge // SpdyHeaderBlock) into an appropriate sequence of SpdySerializedFrames, and // write to the output. class SPDY_EXPORT_PRIVATE SpdyHeaderFrameIterator : public SpdyFrameIterator { public: // Does not take ownership of |framer|. Take ownership of |headers_ir|. SpdyHeaderFrameIterator(SpdyFramer* framer, std::unique_ptr headers_ir); ~SpdyHeaderFrameIterator() override; private: const SpdyFrameIR& GetIR() const override; size_t GetFrameSizeSansBlock() const override; bool SerializeGivenEncoding(const SpdyString& encoding, ZeroCopyOutputBuffer* output) const override; const std::unique_ptr headers_ir_; }; // Iteratively converts a SpdyPushPromiseIR (with a possibly huge // SpdyHeaderBlock) into an appropriate sequence of SpdySerializedFrames, and // write to the output. class SPDY_EXPORT_PRIVATE SpdyPushPromiseFrameIterator : public SpdyFrameIterator { public: // Does not take ownership of |framer|. Take ownership of |push_promise_ir|. SpdyPushPromiseFrameIterator( SpdyFramer* framer, std::unique_ptr push_promise_ir); ~SpdyPushPromiseFrameIterator() override; private: const SpdyFrameIR& GetIR() const override; size_t GetFrameSizeSansBlock() const override; bool SerializeGivenEncoding(const SpdyString& encoding, ZeroCopyOutputBuffer* output) const override; const std::unique_ptr push_promise_ir_; }; // Converts a SpdyFrameIR into one Spdy frame (a sequence of length 1), and // write it to the output. class SPDY_EXPORT_PRIVATE SpdyControlFrameIterator : public SpdyFrameSequence { public: SpdyControlFrameIterator(SpdyFramer* framer, std::unique_ptr frame_ir); ~SpdyControlFrameIterator() override; size_t NextFrame(ZeroCopyOutputBuffer* output) override; bool HasNextFrame() const override; const SpdyFrameIR& GetIR() const override; private: SpdyFramer* const framer_; std::unique_ptr frame_ir_; bool has_next_frame_ = true; }; private: static size_t GetNumberRequiredContinuationFrames(size_t size); void SerializeHeadersBuilderHelper(const SpdyHeadersIR& headers, uint8_t* flags, size_t* size, SpdyString* hpack_encoding, int* weight, size_t* length_field); void SerializePushPromiseBuilderHelper(const SpdyPushPromiseIR& push_promise, uint8_t* flags, SpdyString* hpack_encoding, size_t* size); std::unique_ptr hpack_encoder_; SpdyFramerDebugVisitorInterface* debug_visitor_; // Determines whether HPACK compression is used. const CompressionOption compression_option_; }; } // namespace net #endif // NET_SPDY_CORE_SPDY_FRAMER_H_