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