1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef QUICHE_SPDY_CORE_SPDY_FRAMER_H_ 6 #define QUICHE_SPDY_CORE_SPDY_FRAMER_H_ 7 8 #include <stddef.h> 9 10 #include <cstdint> 11 #include <memory> 12 #include <string> 13 #include <utility> 14 15 #include "quiche/common/platform/api/quiche_export.h" 16 #include "quiche/spdy/core/hpack/hpack_encoder.h" 17 #include "quiche/spdy/core/spdy_protocol.h" 18 #include "quiche/spdy/core/zero_copy_output_buffer.h" 19 20 namespace spdy { 21 22 namespace test { 23 24 class SpdyFramerPeer; 25 class SpdyFramerTest_MultipleContinuationFramesWithIterator_Test; 26 class SpdyFramerTest_PushPromiseFramesWithIterator_Test; 27 28 } // namespace test 29 30 class QUICHE_EXPORT SpdyFrameSequence { 31 public: ~SpdyFrameSequence()32 virtual ~SpdyFrameSequence() {} 33 34 // Serializes the next frame in the sequence to |output|. Returns the number 35 // of bytes written to |output|. 36 virtual size_t NextFrame(ZeroCopyOutputBuffer* output) = 0; 37 38 // Returns true iff there is at least one more frame in the sequence. 39 virtual bool HasNextFrame() const = 0; 40 41 // Get SpdyFrameIR of the frame to be serialized. 42 virtual const SpdyFrameIR& GetIR() const = 0; 43 }; 44 45 class QUICHE_EXPORT SpdyFramer { 46 public: 47 enum CompressionOption { 48 ENABLE_COMPRESSION, 49 DISABLE_COMPRESSION, 50 }; 51 52 // Create a SpdyFrameSequence to serialize |frame_ir|. 53 static std::unique_ptr<SpdyFrameSequence> CreateIterator( 54 SpdyFramer* framer, std::unique_ptr<const SpdyFrameIR> frame_ir); 55 56 // Gets the serialized flags for the given |frame|. 57 static uint8_t GetSerializedFlags(const SpdyFrameIR& frame); 58 59 // Serialize a data frame. 60 static SpdySerializedFrame SerializeData(const SpdyDataIR& data_ir); 61 // Serializes the data frame header and optionally padding length fields, 62 // excluding actual data payload and padding. 63 static SpdySerializedFrame SerializeDataFrameHeaderWithPaddingLengthField( 64 const SpdyDataIR& data_ir); 65 66 // Serializes a WINDOW_UPDATE frame. The WINDOW_UPDATE 67 // frame is used to implement per stream flow control. 68 static SpdySerializedFrame SerializeWindowUpdate( 69 const SpdyWindowUpdateIR& window_update); 70 71 explicit SpdyFramer(CompressionOption option); 72 73 virtual ~SpdyFramer(); 74 75 // Set debug callbacks to be called from the framer. The debug visitor is 76 // completely optional and need not be set in order for normal operation. 77 // If this is called multiple times, only the last visitor will be used. 78 void set_debug_visitor(SpdyFramerDebugVisitorInterface* debug_visitor); 79 80 SpdySerializedFrame SerializeRstStream( 81 const SpdyRstStreamIR& rst_stream) const; 82 83 // Serializes a SETTINGS frame. The SETTINGS frame is 84 // used to communicate name/value pairs relevant to the communication channel. 85 SpdySerializedFrame SerializeSettings(const SpdySettingsIR& settings) const; 86 87 // Serializes a PING frame. The unique_id is used to 88 // identify the ping request/response. 89 SpdySerializedFrame SerializePing(const SpdyPingIR& ping) const; 90 91 // Serializes a GOAWAY frame. The GOAWAY frame is used 92 // prior to the shutting down of the TCP connection, and includes the 93 // stream_id of the last stream the sender of the frame is willing to process 94 // to completion. 95 SpdySerializedFrame SerializeGoAway(const SpdyGoAwayIR& goaway) const; 96 97 // Serializes a HEADERS frame. The HEADERS frame is used 98 // for sending headers. 99 SpdySerializedFrame SerializeHeaders(const SpdyHeadersIR& headers); 100 101 // Serializes a PUSH_PROMISE frame. The PUSH_PROMISE frame is used 102 // to inform the client that it will be receiving an additional stream 103 // in response to the original request. The frame includes synthesized 104 // headers to explain the upcoming data. 105 SpdySerializedFrame SerializePushPromise( 106 const SpdyPushPromiseIR& push_promise); 107 108 // Serializes a CONTINUATION frame. The CONTINUATION frame is used 109 // to continue a sequence of header block fragments. 110 SpdySerializedFrame SerializeContinuation( 111 const SpdyContinuationIR& continuation) const; 112 113 // Serializes an ALTSVC frame. The ALTSVC frame advertises the 114 // availability of an alternative service to the client. 115 SpdySerializedFrame SerializeAltSvc(const SpdyAltSvcIR& altsvc); 116 117 // Serializes a PRIORITY frame. The PRIORITY frame advises a change in 118 // the relative priority of the given stream. 119 SpdySerializedFrame SerializePriority(const SpdyPriorityIR& priority) const; 120 121 // Serializes a PRIORITY_UPDATE frame. 122 // See https://httpwg.org/http-extensions/draft-ietf-httpbis-priority.html. 123 SpdySerializedFrame SerializePriorityUpdate( 124 const SpdyPriorityUpdateIR& priority_update) const; 125 126 // Serializes an ACCEPT_CH frame. See 127 // https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02. 128 SpdySerializedFrame SerializeAcceptCh(const SpdyAcceptChIR& accept_ch) const; 129 130 // Serializes an unknown frame given a frame header and payload. 131 SpdySerializedFrame SerializeUnknown(const SpdyUnknownIR& unknown) const; 132 133 // Serialize a frame of unknown type. 134 SpdySerializedFrame SerializeFrame(const SpdyFrameIR& frame); 135 136 // Serialize a data frame. 137 bool SerializeData(const SpdyDataIR& data, 138 ZeroCopyOutputBuffer* output) const; 139 140 // Serializes the data frame header and optionally padding length fields, 141 // excluding actual data payload and padding. 142 bool SerializeDataFrameHeaderWithPaddingLengthField( 143 const SpdyDataIR& data, ZeroCopyOutputBuffer* output) const; 144 145 bool SerializeRstStream(const SpdyRstStreamIR& rst_stream, 146 ZeroCopyOutputBuffer* output) const; 147 148 // Serializes a SETTINGS frame. The SETTINGS frame is 149 // used to communicate name/value pairs relevant to the communication channel. 150 bool SerializeSettings(const SpdySettingsIR& settings, 151 ZeroCopyOutputBuffer* output) const; 152 153 // Serializes a PING frame. The unique_id is used to 154 // identify the ping request/response. 155 bool SerializePing(const SpdyPingIR& ping, 156 ZeroCopyOutputBuffer* output) const; 157 158 // Serializes a GOAWAY frame. The GOAWAY frame is used 159 // prior to the shutting down of the TCP connection, and includes the 160 // stream_id of the last stream the sender of the frame is willing to process 161 // to completion. 162 bool SerializeGoAway(const SpdyGoAwayIR& goaway, 163 ZeroCopyOutputBuffer* output) const; 164 165 // Serializes a HEADERS frame. The HEADERS frame is used 166 // for sending headers. 167 bool SerializeHeaders(const SpdyHeadersIR& headers, 168 ZeroCopyOutputBuffer* output); 169 170 // Serializes a WINDOW_UPDATE frame. The WINDOW_UPDATE 171 // frame is used to implement per stream flow control. 172 bool SerializeWindowUpdate(const SpdyWindowUpdateIR& window_update, 173 ZeroCopyOutputBuffer* output) const; 174 175 // Serializes a PUSH_PROMISE frame. The PUSH_PROMISE frame is used 176 // to inform the client that it will be receiving an additional stream 177 // in response to the original request. The frame includes synthesized 178 // headers to explain the upcoming data. 179 bool SerializePushPromise(const SpdyPushPromiseIR& push_promise, 180 ZeroCopyOutputBuffer* output); 181 182 // Serializes a CONTINUATION frame. The CONTINUATION frame is used 183 // to continue a sequence of header block fragments. 184 bool SerializeContinuation(const SpdyContinuationIR& continuation, 185 ZeroCopyOutputBuffer* output) const; 186 187 // Serializes an ALTSVC frame. The ALTSVC frame advertises the 188 // availability of an alternative service to the client. 189 bool SerializeAltSvc(const SpdyAltSvcIR& altsvc, 190 ZeroCopyOutputBuffer* output); 191 192 // Serializes a PRIORITY frame. The PRIORITY frame advises a change in 193 // the relative priority of the given stream. 194 bool SerializePriority(const SpdyPriorityIR& priority, 195 ZeroCopyOutputBuffer* output) const; 196 197 // Serializes a PRIORITY_UPDATE frame. 198 // See https://httpwg.org/http-extensions/draft-ietf-httpbis-priority.html. 199 bool SerializePriorityUpdate(const SpdyPriorityUpdateIR& priority_update, 200 ZeroCopyOutputBuffer* output) const; 201 202 // Serializes an ACCEPT_CH frame. See 203 // https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02. 204 bool SerializeAcceptCh(const SpdyAcceptChIR& accept_ch, 205 ZeroCopyOutputBuffer* output) const; 206 207 // Serializes an unknown frame given a frame header and payload. 208 bool SerializeUnknown(const SpdyUnknownIR& unknown, 209 ZeroCopyOutputBuffer* output) const; 210 211 // Serialize a frame of unknown type. 212 size_t SerializeFrame(const SpdyFrameIR& frame, ZeroCopyOutputBuffer* output); 213 214 // Returns whether this SpdyFramer will compress header blocks using HPACK. compression_enabled()215 bool compression_enabled() const { 216 return compression_option_ == ENABLE_COMPRESSION; 217 } 218 SetHpackIndexingPolicy(HpackEncoder::IndexingPolicy policy)219 void SetHpackIndexingPolicy(HpackEncoder::IndexingPolicy policy) { 220 GetHpackEncoder()->SetIndexingPolicy(std::move(policy)); 221 } 222 223 // Updates the maximum size of the header encoder compression table. 224 void UpdateHeaderEncoderTableSize(uint32_t value); 225 226 // Returns the maximum size of the header encoder compression table. 227 size_t header_encoder_table_size() const; 228 229 // Get (and lazily initialize) the HPACK encoder state. 230 HpackEncoder* GetHpackEncoder(); 231 232 // Gets the HPACK encoder state. Returns nullptr if the encoder has not been 233 // initialized. GetHpackEncoder()234 const HpackEncoder* GetHpackEncoder() const { return hpack_encoder_.get(); } 235 236 protected: 237 friend class test::SpdyFramerPeer; 238 friend class test::SpdyFramerTest_MultipleContinuationFramesWithIterator_Test; 239 friend class test::SpdyFramerTest_PushPromiseFramesWithIterator_Test; 240 241 // Iteratively converts a SpdyFrameIR into an appropriate sequence of Spdy 242 // frames. 243 // Example usage: 244 // std::unique_ptr<SpdyFrameSequence> it = CreateIterator(framer, frame_ir); 245 // while (it->HasNextFrame()) { 246 // if(it->NextFrame(output) == 0) { 247 // // Write failed; 248 // } 249 // } 250 class QUICHE_EXPORT SpdyFrameIterator : public SpdyFrameSequence { 251 public: 252 // Creates an iterator with the provided framer. 253 // Does not take ownership of |framer|. 254 // |framer| must outlive this instance. 255 explicit SpdyFrameIterator(SpdyFramer* framer); 256 ~SpdyFrameIterator() override; 257 258 // Serializes the next frame in the sequence to |output|. Returns the number 259 // of bytes written to |output|. 260 size_t NextFrame(ZeroCopyOutputBuffer* output) override; 261 262 // Returns true iff there is at least one more frame in the sequence. 263 bool HasNextFrame() const override; 264 265 // SpdyFrameIterator is neither copyable nor movable. 266 SpdyFrameIterator(const SpdyFrameIterator&) = delete; 267 SpdyFrameIterator& operator=(const SpdyFrameIterator&) = delete; 268 269 protected: 270 virtual size_t GetFrameSizeSansBlock() const = 0; 271 virtual bool SerializeGivenEncoding(const std::string& encoding, 272 ZeroCopyOutputBuffer* output) const = 0; 273 GetFramer()274 SpdyFramer* GetFramer() const { return framer_; } 275 SetEncoder(const SpdyFrameWithHeaderBlockIR * ir)276 void SetEncoder(const SpdyFrameWithHeaderBlockIR* ir) { 277 encoder_ = 278 framer_->GetHpackEncoder()->EncodeHeaderSet(ir->header_block()); 279 } 280 has_next_frame()281 bool has_next_frame() const { return has_next_frame_; } 282 283 private: 284 SpdyFramer* const framer_; 285 std::unique_ptr<HpackEncoder::ProgressiveEncoder> encoder_; 286 bool is_first_frame_; 287 bool has_next_frame_; 288 }; 289 290 // Iteratively converts a SpdyHeadersIR (with a possibly huge 291 // Http2HeaderBlock) into an appropriate sequence of SpdySerializedFrames, and 292 // write to the output. 293 class QUICHE_EXPORT SpdyHeaderFrameIterator : public SpdyFrameIterator { 294 public: 295 // Does not take ownership of |framer|. Take ownership of |headers_ir|. 296 SpdyHeaderFrameIterator(SpdyFramer* framer, 297 std::unique_ptr<const SpdyHeadersIR> headers_ir); 298 299 ~SpdyHeaderFrameIterator() override; 300 301 private: 302 const SpdyFrameIR& GetIR() const override; 303 size_t GetFrameSizeSansBlock() const override; 304 bool SerializeGivenEncoding(const std::string& encoding, 305 ZeroCopyOutputBuffer* output) const override; 306 307 const std::unique_ptr<const SpdyHeadersIR> headers_ir_; 308 }; 309 310 // Iteratively converts a SpdyPushPromiseIR (with a possibly huge 311 // Http2HeaderBlock) into an appropriate sequence of SpdySerializedFrames, and 312 // write to the output. 313 class QUICHE_EXPORT SpdyPushPromiseFrameIterator : public SpdyFrameIterator { 314 public: 315 // Does not take ownership of |framer|. Take ownership of |push_promise_ir|. 316 SpdyPushPromiseFrameIterator( 317 SpdyFramer* framer, 318 std::unique_ptr<const SpdyPushPromiseIR> push_promise_ir); 319 320 ~SpdyPushPromiseFrameIterator() override; 321 322 private: 323 const SpdyFrameIR& GetIR() const override; 324 size_t GetFrameSizeSansBlock() const override; 325 bool SerializeGivenEncoding(const std::string& encoding, 326 ZeroCopyOutputBuffer* output) const override; 327 328 const std::unique_ptr<const SpdyPushPromiseIR> push_promise_ir_; 329 }; 330 331 // Converts a SpdyFrameIR into one Spdy frame (a sequence of length 1), and 332 // write it to the output. 333 class QUICHE_EXPORT SpdyControlFrameIterator : public SpdyFrameSequence { 334 public: 335 SpdyControlFrameIterator(SpdyFramer* framer, 336 std::unique_ptr<const SpdyFrameIR> frame_ir); 337 ~SpdyControlFrameIterator() override; 338 339 size_t NextFrame(ZeroCopyOutputBuffer* output) override; 340 341 bool HasNextFrame() const override; 342 343 const SpdyFrameIR& GetIR() const override; 344 345 private: 346 SpdyFramer* const framer_; 347 std::unique_ptr<const SpdyFrameIR> frame_ir_; 348 bool has_next_frame_ = true; 349 }; 350 351 private: 352 void SerializeHeadersBuilderHelper(const SpdyHeadersIR& headers, 353 uint8_t* flags, size_t* size, 354 std::string* hpack_encoding, int* weight, 355 size_t* length_field); 356 void SerializePushPromiseBuilderHelper(const SpdyPushPromiseIR& push_promise, 357 uint8_t* flags, 358 std::string* hpack_encoding, 359 size_t* size); 360 361 std::unique_ptr<HpackEncoder> hpack_encoder_; 362 363 SpdyFramerDebugVisitorInterface* debug_visitor_; 364 365 // Determines whether HPACK compression is used. 366 const CompressionOption compression_option_; 367 }; 368 369 } // namespace spdy 370 371 #endif // QUICHE_SPDY_CORE_SPDY_FRAMER_H_ 372