| // 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 QUICHE_SPDY_CORE_SPDY_FRAMER_H_ |
| #define QUICHE_SPDY_CORE_SPDY_FRAMER_H_ |
| |
| #include <stddef.h> |
| |
| #include <cstdint> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/strings/string_view.h" |
| #include "common/platform/api/quiche_export.h" |
| #include "spdy/core/hpack/hpack_encoder.h" |
| #include "spdy/core/spdy_alt_svc_wire_format.h" |
| #include "spdy/core/spdy_header_block.h" |
| #include "spdy/core/spdy_headers_handler_interface.h" |
| #include "spdy/core/spdy_protocol.h" |
| #include "spdy/core/zero_copy_output_buffer.h" |
| |
| namespace spdy { |
| |
| namespace test { |
| |
| class SpdyFramerPeer; |
| class SpdyFramerTest_MultipleContinuationFramesWithIterator_Test; |
| class SpdyFramerTest_PushPromiseFramesWithIterator_Test; |
| |
| } // namespace test |
| |
| class QUICHE_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 QUICHE_EXPORT_PRIVATE SpdyFramer { |
| public: |
| enum CompressionOption { |
| ENABLE_COMPRESSION, |
| DISABLE_COMPRESSION, |
| }; |
| |
| // Create a SpdyFrameSequence to serialize |frame_ir|. |
| static std::unique_ptr<SpdyFrameSequence> CreateIterator( |
| SpdyFramer* framer, |
| std::unique_ptr<const SpdyFrameIR> frame_ir); |
| |
| // Gets the serialized flags for the given |frame|. |
| static uint8_t GetSerializedFlags(const SpdyFrameIR& frame); |
| |
| // 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 a PRIORITY_UPDATE frame. |
| // See https://httpwg.org/http-extensions/draft-ietf-httpbis-priority.html. |
| SpdySerializedFrame SerializePriorityUpdate( |
| const SpdyPriorityUpdateIR& priority_update) const; |
| |
| // Serializes an ACCEPT_CH frame. See |
| // https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02. |
| SpdySerializedFrame SerializeAcceptCh(const SpdyAcceptChIR& accept_ch) 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 a PRIORITY_UPDATE frame. |
| // See https://httpwg.org/http-extensions/draft-ietf-httpbis-priority.html. |
| bool SerializePriorityUpdate(const SpdyPriorityUpdateIR& priority_update, |
| ZeroCopyOutputBuffer* output) const; |
| |
| // Serializes an ACCEPT_CH frame. See |
| // https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02. |
| bool SerializeAcceptCh(const SpdyAcceptChIR& accept_ch, |
| 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); |
| |
| // Returns the maximum size of the header encoder compression table. |
| size_t header_encoder_table_size() const; |
| |
| // Get (and lazily initialize) the HPACK encoder state. |
| HpackEncoder* GetHpackEncoder(); |
| |
| // Gets the HPACK encoder state. Returns nullptr if the encoder has not been |
| // initialized. |
| const HpackEncoder* GetHpackEncoder() const { return hpack_encoder_.get(); } |
| |
| protected: |
| 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<SpdyFrameSequence> it = CreateIterator(framer, frame_ir); |
| // while (it->HasNextFrame()) { |
| // if(it->NextFrame(output) == 0) { |
| // // Write failed; |
| // } |
| // } |
| class QUICHE_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 std::string& 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<HpackEncoder::ProgressiveEncoder> 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 QUICHE_EXPORT_PRIVATE SpdyHeaderFrameIterator |
| : public SpdyFrameIterator { |
| public: |
| // Does not take ownership of |framer|. Take ownership of |headers_ir|. |
| SpdyHeaderFrameIterator(SpdyFramer* framer, |
| std::unique_ptr<const SpdyHeadersIR> headers_ir); |
| |
| ~SpdyHeaderFrameIterator() override; |
| |
| private: |
| const SpdyFrameIR& GetIR() const override; |
| size_t GetFrameSizeSansBlock() const override; |
| bool SerializeGivenEncoding(const std::string& encoding, |
| ZeroCopyOutputBuffer* output) const override; |
| |
| const std::unique_ptr<const SpdyHeadersIR> headers_ir_; |
| }; |
| |
| // Iteratively converts a SpdyPushPromiseIR (with a possibly huge |
| // SpdyHeaderBlock) into an appropriate sequence of SpdySerializedFrames, and |
| // write to the output. |
| class QUICHE_EXPORT_PRIVATE SpdyPushPromiseFrameIterator |
| : public SpdyFrameIterator { |
| public: |
| // Does not take ownership of |framer|. Take ownership of |push_promise_ir|. |
| SpdyPushPromiseFrameIterator( |
| SpdyFramer* framer, |
| std::unique_ptr<const SpdyPushPromiseIR> push_promise_ir); |
| |
| ~SpdyPushPromiseFrameIterator() override; |
| |
| private: |
| const SpdyFrameIR& GetIR() const override; |
| size_t GetFrameSizeSansBlock() const override; |
| bool SerializeGivenEncoding(const std::string& encoding, |
| ZeroCopyOutputBuffer* output) const override; |
| |
| const std::unique_ptr<const SpdyPushPromiseIR> push_promise_ir_; |
| }; |
| |
| // Converts a SpdyFrameIR into one Spdy frame (a sequence of length 1), and |
| // write it to the output. |
| class QUICHE_EXPORT_PRIVATE SpdyControlFrameIterator |
| : public SpdyFrameSequence { |
| public: |
| SpdyControlFrameIterator(SpdyFramer* framer, |
| std::unique_ptr<const SpdyFrameIR> 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<const SpdyFrameIR> frame_ir_; |
| bool has_next_frame_ = true; |
| }; |
| |
| private: |
| void SerializeHeadersBuilderHelper(const SpdyHeadersIR& headers, |
| uint8_t* flags, |
| size_t* size, |
| std::string* hpack_encoding, |
| int* weight, |
| size_t* length_field); |
| void SerializePushPromiseBuilderHelper(const SpdyPushPromiseIR& push_promise, |
| uint8_t* flags, |
| std::string* hpack_encoding, |
| size_t* size); |
| |
| std::unique_ptr<HpackEncoder> hpack_encoder_; |
| |
| SpdyFramerDebugVisitorInterface* debug_visitor_; |
| |
| // Determines whether HPACK compression is used. |
| const CompressionOption compression_option_; |
| }; |
| |
| } // namespace spdy |
| |
| #endif // QUICHE_SPDY_CORE_SPDY_FRAMER_H_ |