|  | // 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_ |