xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_session.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2015 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 #include "quiche/quic/core/http/quic_spdy_session.h"
6 
7 #include <algorithm>
8 #include <cstdint>
9 #include <limits>
10 #include <memory>
11 #include <optional>
12 #include <string>
13 #include <utility>
14 
15 
16 #include "absl/base/attributes.h"
17 #include "absl/strings/numbers.h"
18 #include "absl/strings/str_cat.h"
19 #include "absl/strings/string_view.h"
20 #include "quiche/quic/core/http/http_constants.h"
21 #include "quiche/quic/core/http/http_decoder.h"
22 #include "quiche/quic/core/http/http_frames.h"
23 #include "quiche/quic/core/http/quic_headers_stream.h"
24 #include "quiche/quic/core/http/quic_spdy_stream.h"
25 #include "quiche/quic/core/http/web_transport_http3.h"
26 #include "quiche/quic/core/quic_error_codes.h"
27 #include "quiche/quic/core/quic_session.h"
28 #include "quiche/quic/core/quic_types.h"
29 #include "quiche/quic/core/quic_utils.h"
30 #include "quiche/quic/core/quic_versions.h"
31 #include "quiche/quic/platform/api/quic_bug_tracker.h"
32 #include "quiche/quic/platform/api/quic_exported_stats.h"
33 #include "quiche/quic/platform/api/quic_flag_utils.h"
34 #include "quiche/quic/platform/api/quic_flags.h"
35 #include "quiche/quic/platform/api/quic_logging.h"
36 #include "quiche/quic/platform/api/quic_stack_trace.h"
37 #include "quiche/common/platform/api/quiche_mem_slice.h"
38 #include "quiche/spdy/core/http2_frame_decoder_adapter.h"
39 
40 using http2::Http2DecoderAdapter;
41 using spdy::Http2HeaderBlock;
42 using spdy::Http2WeightToSpdy3Priority;
43 using spdy::Spdy3PriorityToHttp2Weight;
44 using spdy::SpdyErrorCode;
45 using spdy::SpdyFramer;
46 using spdy::SpdyFramerDebugVisitorInterface;
47 using spdy::SpdyFramerVisitorInterface;
48 using spdy::SpdyFrameType;
49 using spdy::SpdyHeadersHandlerInterface;
50 using spdy::SpdyHeadersIR;
51 using spdy::SpdyPingId;
52 using spdy::SpdyPriority;
53 using spdy::SpdyPriorityIR;
54 using spdy::SpdySerializedFrame;
55 using spdy::SpdySettingsId;
56 using spdy::SpdyStreamId;
57 
58 namespace quic {
59 
60 ABSL_CONST_INIT const size_t kMaxUnassociatedWebTransportStreams = 24;
61 
62 namespace {
63 
64 // Limit on HPACK encoder dynamic table size.
65 // Only used for Google QUIC, not IETF QUIC.
66 constexpr uint64_t kHpackEncoderDynamicTableSizeLimit = 16384;
67 
68 constexpr QuicStreamCount kDefaultMaxWebTransportSessions = 16;
69 
70 #define ENDPOINT \
71   (perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ")
72 
73 // Class to forward ACCEPT_CH frame to QuicSpdySession,
74 // and ignore every other frame.
75 class AlpsFrameDecoder : public HttpDecoder::Visitor {
76  public:
AlpsFrameDecoder(QuicSpdySession * session)77   explicit AlpsFrameDecoder(QuicSpdySession* session) : session_(session) {}
78   ~AlpsFrameDecoder() override = default;
79 
80   // HttpDecoder::Visitor implementation.
OnError(HttpDecoder *)81   void OnError(HttpDecoder* /*decoder*/) override {}
OnMaxPushIdFrame()82   bool OnMaxPushIdFrame() override {
83     error_detail_ = "MAX_PUSH_ID frame forbidden";
84     return false;
85   }
OnGoAwayFrame(const GoAwayFrame &)86   bool OnGoAwayFrame(const GoAwayFrame& /*frame*/) override {
87     error_detail_ = "GOAWAY frame forbidden";
88     return false;
89   }
OnSettingsFrameStart(QuicByteCount)90   bool OnSettingsFrameStart(QuicByteCount /*header_length*/) override {
91     return true;
92   }
OnSettingsFrame(const SettingsFrame & frame)93   bool OnSettingsFrame(const SettingsFrame& frame) override {
94     if (settings_frame_received_via_alps_) {
95       error_detail_ = "multiple SETTINGS frames";
96       return false;
97     }
98 
99     settings_frame_received_via_alps_ = true;
100 
101     error_detail_ = session_->OnSettingsFrameViaAlps(frame);
102     return !error_detail_;
103   }
OnDataFrameStart(QuicByteCount,QuicByteCount)104   bool OnDataFrameStart(QuicByteCount /*header_length*/, QuicByteCount
105                         /*payload_length*/) override {
106     error_detail_ = "DATA frame forbidden";
107     return false;
108   }
OnDataFramePayload(absl::string_view)109   bool OnDataFramePayload(absl::string_view /*payload*/) override {
110     QUICHE_NOTREACHED();
111     return false;
112   }
OnDataFrameEnd()113   bool OnDataFrameEnd() override {
114     QUICHE_NOTREACHED();
115     return false;
116   }
OnHeadersFrameStart(QuicByteCount,QuicByteCount)117   bool OnHeadersFrameStart(QuicByteCount /*header_length*/,
118                            QuicByteCount /*payload_length*/) override {
119     error_detail_ = "HEADERS frame forbidden";
120     return false;
121   }
OnHeadersFramePayload(absl::string_view)122   bool OnHeadersFramePayload(absl::string_view /*payload*/) override {
123     QUICHE_NOTREACHED();
124     return false;
125   }
OnHeadersFrameEnd()126   bool OnHeadersFrameEnd() override {
127     QUICHE_NOTREACHED();
128     return false;
129   }
OnPriorityUpdateFrameStart(QuicByteCount)130   bool OnPriorityUpdateFrameStart(QuicByteCount /*header_length*/) override {
131     error_detail_ = "PRIORITY_UPDATE frame forbidden";
132     return false;
133   }
OnPriorityUpdateFrame(const PriorityUpdateFrame &)134   bool OnPriorityUpdateFrame(const PriorityUpdateFrame& /*frame*/) override {
135     QUICHE_NOTREACHED();
136     return false;
137   }
OnAcceptChFrameStart(QuicByteCount)138   bool OnAcceptChFrameStart(QuicByteCount /*header_length*/) override {
139     return true;
140   }
OnAcceptChFrame(const AcceptChFrame & frame)141   bool OnAcceptChFrame(const AcceptChFrame& frame) override {
142     session_->OnAcceptChFrameReceivedViaAlps(frame);
143     return true;
144   }
OnWebTransportStreamFrameType(QuicByteCount,WebTransportSessionId)145   void OnWebTransportStreamFrameType(
146       QuicByteCount /*header_length*/,
147       WebTransportSessionId /*session_id*/) override {
148     QUICHE_NOTREACHED();
149   }
OnMetadataFrameStart(QuicByteCount,QuicByteCount)150   bool OnMetadataFrameStart(QuicByteCount /*header_length*/,
151                             QuicByteCount /*payload_length*/) override {
152     error_detail_ = "METADATA frame forbidden";
153     return false;
154   }
OnMetadataFramePayload(absl::string_view)155   bool OnMetadataFramePayload(absl::string_view /*payload*/) override {
156     QUICHE_NOTREACHED();
157     return false;
158   }
OnMetadataFrameEnd()159   bool OnMetadataFrameEnd() override {
160     QUICHE_NOTREACHED();
161     return false;
162   }
OnUnknownFrameStart(uint64_t,QuicByteCount,QuicByteCount)163   bool OnUnknownFrameStart(uint64_t /*frame_type*/,
164                            QuicByteCount
165                            /*header_length*/,
166                            QuicByteCount /*payload_length*/) override {
167     return true;
168   }
OnUnknownFramePayload(absl::string_view)169   bool OnUnknownFramePayload(absl::string_view /*payload*/) override {
170     return true;
171   }
OnUnknownFrameEnd()172   bool OnUnknownFrameEnd() override { return true; }
173 
error_detail() const174   const std::optional<std::string>& error_detail() const {
175     return error_detail_;
176   }
177 
178  private:
179   QuicSpdySession* const session_;
180   std::optional<std::string> error_detail_;
181 
182   // True if SETTINGS frame has been received via ALPS.
183   bool settings_frame_received_via_alps_ = false;
184 };
185 
GetDefaultQpackMaximumDynamicTableCapacity(Perspective perspective)186 uint64_t GetDefaultQpackMaximumDynamicTableCapacity(Perspective perspective) {
187   if (perspective == Perspective::IS_SERVER &&
188       GetQuicFlag(quic_server_disable_qpack_dynamic_table)) {
189     return 0;
190   }
191 
192   return kDefaultQpackMaxDynamicTableCapacity;
193 }
194 
195 }  // namespace
196 
197 // A SpdyFramerVisitor that passes HEADERS frames to the QuicSpdyStream, and
198 // closes the connection if any unexpected frames are received.
199 class QuicSpdySession::SpdyFramerVisitor
200     : public SpdyFramerVisitorInterface,
201       public SpdyFramerDebugVisitorInterface {
202  public:
SpdyFramerVisitor(QuicSpdySession * session)203   explicit SpdyFramerVisitor(QuicSpdySession* session) : session_(session) {}
204   SpdyFramerVisitor(const SpdyFramerVisitor&) = delete;
205   SpdyFramerVisitor& operator=(const SpdyFramerVisitor&) = delete;
206 
OnHeaderFrameStart(SpdyStreamId)207   SpdyHeadersHandlerInterface* OnHeaderFrameStart(
208       SpdyStreamId /* stream_id */) override {
209     QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version()));
210     return &header_list_;
211   }
212 
OnHeaderFrameEnd(SpdyStreamId)213   void OnHeaderFrameEnd(SpdyStreamId /* stream_id */) override {
214     QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version()));
215 
216     LogHeaderCompressionRatioHistogram(
217         /* using_qpack = */ false,
218         /* is_sent = */ false, header_list_.compressed_header_bytes(),
219         header_list_.uncompressed_header_bytes());
220 
221     // Ignore pushed request headers.
222     if (session_->IsConnected() && !expecting_pushed_headers_) {
223       session_->OnHeaderList(header_list_);
224     }
225     expecting_pushed_headers_ = false;
226     header_list_.Clear();
227   }
228 
OnStreamFrameData(SpdyStreamId,const char *,size_t)229   void OnStreamFrameData(SpdyStreamId /*stream_id*/, const char* /*data*/,
230                          size_t /*len*/) override {
231     QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version()));
232     CloseConnection("SPDY DATA frame received.",
233                     QUIC_INVALID_HEADERS_STREAM_DATA);
234   }
235 
OnStreamEnd(SpdyStreamId)236   void OnStreamEnd(SpdyStreamId /*stream_id*/) override {
237     // The framer invokes OnStreamEnd after processing a frame that had the fin
238     // bit set.
239   }
240 
OnStreamPadding(SpdyStreamId,size_t)241   void OnStreamPadding(SpdyStreamId /*stream_id*/, size_t /*len*/) override {
242     CloseConnection("SPDY frame padding received.",
243                     QUIC_INVALID_HEADERS_STREAM_DATA);
244   }
245 
OnError(Http2DecoderAdapter::SpdyFramerError error,std::string detailed_error)246   void OnError(Http2DecoderAdapter::SpdyFramerError error,
247                std::string detailed_error) override {
248     QuicErrorCode code;
249     switch (error) {
250       case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_INDEX_VARINT_ERROR:
251         code = QUIC_HPACK_INDEX_VARINT_ERROR;
252         break;
253       case Http2DecoderAdapter::SpdyFramerError::
254           SPDY_HPACK_NAME_LENGTH_VARINT_ERROR:
255         code = QUIC_HPACK_NAME_LENGTH_VARINT_ERROR;
256         break;
257       case Http2DecoderAdapter::SpdyFramerError::
258           SPDY_HPACK_VALUE_LENGTH_VARINT_ERROR:
259         code = QUIC_HPACK_VALUE_LENGTH_VARINT_ERROR;
260         break;
261       case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_NAME_TOO_LONG:
262         code = QUIC_HPACK_NAME_TOO_LONG;
263         break;
264       case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_VALUE_TOO_LONG:
265         code = QUIC_HPACK_VALUE_TOO_LONG;
266         break;
267       case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_NAME_HUFFMAN_ERROR:
268         code = QUIC_HPACK_NAME_HUFFMAN_ERROR;
269         break;
270       case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_VALUE_HUFFMAN_ERROR:
271         code = QUIC_HPACK_VALUE_HUFFMAN_ERROR;
272         break;
273       case Http2DecoderAdapter::SpdyFramerError::
274           SPDY_HPACK_MISSING_DYNAMIC_TABLE_SIZE_UPDATE:
275         code = QUIC_HPACK_MISSING_DYNAMIC_TABLE_SIZE_UPDATE;
276         break;
277       case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_INVALID_INDEX:
278         code = QUIC_HPACK_INVALID_INDEX;
279         break;
280       case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_INVALID_NAME_INDEX:
281         code = QUIC_HPACK_INVALID_NAME_INDEX;
282         break;
283       case Http2DecoderAdapter::SpdyFramerError::
284           SPDY_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_NOT_ALLOWED:
285         code = QUIC_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_NOT_ALLOWED;
286         break;
287       case Http2DecoderAdapter::SpdyFramerError::
288           SPDY_HPACK_INITIAL_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_LOW_WATER_MARK:
289         code = QUIC_HPACK_INITIAL_TABLE_SIZE_UPDATE_IS_ABOVE_LOW_WATER_MARK;
290         break;
291       case Http2DecoderAdapter::SpdyFramerError::
292           SPDY_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_ACKNOWLEDGED_SETTING:
293         code = QUIC_HPACK_TABLE_SIZE_UPDATE_IS_ABOVE_ACKNOWLEDGED_SETTING;
294         break;
295       case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_TRUNCATED_BLOCK:
296         code = QUIC_HPACK_TRUNCATED_BLOCK;
297         break;
298       case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_FRAGMENT_TOO_LONG:
299         code = QUIC_HPACK_FRAGMENT_TOO_LONG;
300         break;
301       case Http2DecoderAdapter::SpdyFramerError::
302           SPDY_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT:
303         code = QUIC_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT;
304         break;
305       case Http2DecoderAdapter::SpdyFramerError::SPDY_DECOMPRESS_FAILURE:
306         code = QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE;
307         break;
308       default:
309         code = QUIC_INVALID_HEADERS_STREAM_DATA;
310     }
311     CloseConnection(
312         absl::StrCat("SPDY framing error: ", detailed_error,
313                      Http2DecoderAdapter::SpdyFramerErrorToString(error)),
314         code);
315   }
316 
OnDataFrameHeader(SpdyStreamId,size_t,bool)317   void OnDataFrameHeader(SpdyStreamId /*stream_id*/, size_t /*length*/,
318                          bool /*fin*/) override {
319     QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version()));
320     CloseConnection("SPDY DATA frame received.",
321                     QUIC_INVALID_HEADERS_STREAM_DATA);
322   }
323 
OnRstStream(SpdyStreamId,SpdyErrorCode)324   void OnRstStream(SpdyStreamId /*stream_id*/,
325                    SpdyErrorCode /*error_code*/) override {
326     CloseConnection("SPDY RST_STREAM frame received.",
327                     QUIC_INVALID_HEADERS_STREAM_DATA);
328   }
329 
OnSetting(SpdySettingsId id,uint32_t value)330   void OnSetting(SpdySettingsId id, uint32_t value) override {
331     QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version()));
332     session_->OnSetting(id, value);
333   }
334 
OnSettingsEnd()335   void OnSettingsEnd() override {
336     QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version()));
337   }
338 
OnPing(SpdyPingId,bool)339   void OnPing(SpdyPingId /*unique_id*/, bool /*is_ack*/) override {
340     CloseConnection("SPDY PING frame received.",
341                     QUIC_INVALID_HEADERS_STREAM_DATA);
342   }
343 
OnGoAway(SpdyStreamId,SpdyErrorCode)344   void OnGoAway(SpdyStreamId /*last_accepted_stream_id*/,
345                 SpdyErrorCode /*error_code*/) override {
346     CloseConnection("SPDY GOAWAY frame received.",
347                     QUIC_INVALID_HEADERS_STREAM_DATA);
348   }
349 
OnHeaders(SpdyStreamId stream_id,size_t,bool has_priority,int weight,SpdyStreamId,bool,bool fin,bool)350   void OnHeaders(SpdyStreamId stream_id, size_t /*payload_length*/,
351                  bool has_priority, int weight,
352                  SpdyStreamId /*parent_stream_id*/, bool /*exclusive*/,
353                  bool fin, bool /*end*/) override {
354     if (!session_->IsConnected()) {
355       return;
356     }
357 
358     if (VersionUsesHttp3(session_->transport_version())) {
359       CloseConnection("HEADERS frame not allowed on headers stream.",
360                       QUIC_INVALID_HEADERS_STREAM_DATA);
361       return;
362     }
363 
364     QUIC_BUG_IF(quic_bug_12477_1,
365                 session_->destruction_indicator() != 123456789)
366         << "QuicSpdyStream use after free. "
367         << session_->destruction_indicator() << QuicStackTrace();
368 
369     SpdyPriority priority =
370         has_priority ? Http2WeightToSpdy3Priority(weight) : 0;
371     session_->OnHeaders(stream_id, has_priority,
372                         spdy::SpdyStreamPrecedence(priority), fin);
373   }
374 
OnWindowUpdate(SpdyStreamId,int)375   void OnWindowUpdate(SpdyStreamId /*stream_id*/,
376                       int /*delta_window_size*/) override {
377     CloseConnection("SPDY WINDOW_UPDATE frame received.",
378                     QUIC_INVALID_HEADERS_STREAM_DATA);
379   }
380 
OnPushPromise(SpdyStreamId,SpdyStreamId promised_stream_id,bool)381   void OnPushPromise(SpdyStreamId /*stream_id*/,
382                      SpdyStreamId promised_stream_id, bool /*end*/) override {
383     QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version()));
384     if (session_->perspective() != Perspective::IS_CLIENT) {
385       // PUSH_PROMISE sent by a client is a protocol violation.
386       CloseConnection("PUSH_PROMISE not supported.",
387                       QUIC_INVALID_HEADERS_STREAM_DATA);
388       return;
389     }
390 
391     // Push streams are ignored anyway, reset the stream to save bandwidth.
392     session_->MaybeSendRstStreamFrame(
393         promised_stream_id,
394         QuicResetStreamError::FromInternal(QUIC_REFUSED_STREAM),
395         /* bytes_written = */ 0);
396 
397     QUICHE_DCHECK(!expecting_pushed_headers_);
398     expecting_pushed_headers_ = true;
399   }
400 
OnContinuation(SpdyStreamId,size_t,bool)401   void OnContinuation(SpdyStreamId /*stream_id*/, size_t /*payload_size*/,
402                       bool /*end*/) override {}
403 
OnPriority(SpdyStreamId stream_id,SpdyStreamId,int weight,bool)404   void OnPriority(SpdyStreamId stream_id, SpdyStreamId /* parent_id */,
405                   int weight, bool /* exclusive */) override {
406     QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version()));
407     if (!session_->IsConnected()) {
408       return;
409     }
410     SpdyPriority priority = Http2WeightToSpdy3Priority(weight);
411     session_->OnPriority(stream_id, spdy::SpdyStreamPrecedence(priority));
412   }
413 
OnPriorityUpdate(SpdyStreamId,absl::string_view)414   void OnPriorityUpdate(SpdyStreamId /*prioritized_stream_id*/,
415                         absl::string_view /*priority_field_value*/) override {}
416 
OnUnknownFrame(SpdyStreamId,uint8_t)417   bool OnUnknownFrame(SpdyStreamId /*stream_id*/,
418                       uint8_t /*frame_type*/) override {
419     CloseConnection("Unknown frame type received.",
420                     QUIC_INVALID_HEADERS_STREAM_DATA);
421     return false;
422   }
423 
OnUnknownFrameStart(SpdyStreamId,size_t,uint8_t,uint8_t)424   void OnUnknownFrameStart(SpdyStreamId /*stream_id*/, size_t /*length*/,
425                            uint8_t /*type*/, uint8_t /*flags*/) override {}
426 
OnUnknownFramePayload(SpdyStreamId,absl::string_view)427   void OnUnknownFramePayload(SpdyStreamId /*stream_id*/,
428                              absl::string_view /*payload*/) override {}
429 
430   // SpdyFramerDebugVisitorInterface implementation
OnSendCompressedFrame(SpdyStreamId,SpdyFrameType,size_t payload_len,size_t frame_len)431   void OnSendCompressedFrame(SpdyStreamId /*stream_id*/, SpdyFrameType /*type*/,
432                              size_t payload_len, size_t frame_len) override {
433     if (payload_len == 0) {
434       QUIC_BUG(quic_bug_10360_1) << "Zero payload length.";
435       return;
436     }
437     int compression_pct = 100 - (100 * frame_len) / payload_len;
438     QUIC_DVLOG(1) << "Net.QuicHpackCompressionPercentage: " << compression_pct;
439   }
440 
OnReceiveCompressedFrame(SpdyStreamId,SpdyFrameType,size_t frame_len)441   void OnReceiveCompressedFrame(SpdyStreamId /*stream_id*/,
442                                 SpdyFrameType /*type*/,
443                                 size_t frame_len) override {
444     if (session_->IsConnected()) {
445       session_->OnCompressedFrameSize(frame_len);
446     }
447   }
448 
set_max_header_list_size(size_t max_header_list_size)449   void set_max_header_list_size(size_t max_header_list_size) {
450     header_list_.set_max_header_list_size(max_header_list_size);
451   }
452 
453  private:
CloseConnection(const std::string & details,QuicErrorCode code)454   void CloseConnection(const std::string& details, QuicErrorCode code) {
455     if (session_->IsConnected()) {
456       session_->CloseConnectionWithDetails(code, details);
457     }
458   }
459 
460   QuicSpdySession* session_;
461   QuicHeaderList header_list_;
462 
463   // True if the next OnHeaderFrameEnd() call signals the end of pushed request
464   // headers.
465   bool expecting_pushed_headers_ = false;
466 };
467 
Http3DebugVisitor()468 Http3DebugVisitor::Http3DebugVisitor() {}
469 
~Http3DebugVisitor()470 Http3DebugVisitor::~Http3DebugVisitor() {}
471 
472 // Expected unidirectional static streams Requirement can be found at
473 // https://tools.ietf.org/html/draft-ietf-quic-http-22#section-6.2.
QuicSpdySession(QuicConnection * connection,QuicSession::Visitor * visitor,const QuicConfig & config,const ParsedQuicVersionVector & supported_versions)474 QuicSpdySession::QuicSpdySession(
475     QuicConnection* connection, QuicSession::Visitor* visitor,
476     const QuicConfig& config, const ParsedQuicVersionVector& supported_versions)
477     : QuicSession(connection, visitor, config, supported_versions,
478                   /*num_expected_unidirectional_static_streams = */
479                   VersionUsesHttp3(connection->transport_version())
480                       ? static_cast<QuicStreamCount>(
481                             kHttp3StaticUnidirectionalStreamCount)
482                       : 0u,
483                   std::make_unique<DatagramObserver>(this)),
484       send_control_stream_(nullptr),
485       receive_control_stream_(nullptr),
486       qpack_encoder_receive_stream_(nullptr),
487       qpack_decoder_receive_stream_(nullptr),
488       qpack_encoder_send_stream_(nullptr),
489       qpack_decoder_send_stream_(nullptr),
490       qpack_maximum_dynamic_table_capacity_(
491           GetDefaultQpackMaximumDynamicTableCapacity(perspective())),
492       qpack_maximum_blocked_streams_(kDefaultMaximumBlockedStreams),
493       max_inbound_header_list_size_(kDefaultMaxUncompressedHeaderSize),
494       max_outbound_header_list_size_(std::numeric_limits<size_t>::max()),
495       stream_id_(
496           QuicUtils::GetInvalidStreamId(connection->transport_version())),
497       frame_len_(0),
498       fin_(false),
499       spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
500       spdy_framer_visitor_(new SpdyFramerVisitor(this)),
501       debug_visitor_(nullptr),
502       destruction_indicator_(123456789),
503       allow_extended_connect_(perspective() == Perspective::IS_SERVER &&
504                               VersionUsesHttp3(transport_version())),
505       force_buffer_requests_until_settings_(false) {
506   h2_deframer_.set_visitor(spdy_framer_visitor_.get());
507   h2_deframer_.set_debug_visitor(spdy_framer_visitor_.get());
508   spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
509 }
510 
~QuicSpdySession()511 QuicSpdySession::~QuicSpdySession() {
512   QUIC_BUG_IF(quic_bug_12477_2, destruction_indicator_ != 123456789)
513       << "QuicSpdySession use after free. " << destruction_indicator_
514       << QuicStackTrace();
515   destruction_indicator_ = 987654321;
516 }
517 
Initialize()518 void QuicSpdySession::Initialize() {
519   QuicSession::Initialize();
520 
521   FillSettingsFrame();
522   if (!VersionUsesHttp3(transport_version())) {
523     if (perspective() == Perspective::IS_SERVER) {
524       set_largest_peer_created_stream_id(
525           QuicUtils::GetHeadersStreamId(transport_version()));
526     } else {
527       QuicStreamId headers_stream_id = GetNextOutgoingBidirectionalStreamId();
528       QUICHE_DCHECK_EQ(headers_stream_id,
529                        QuicUtils::GetHeadersStreamId(transport_version()));
530     }
531     auto headers_stream = std::make_unique<QuicHeadersStream>((this));
532     QUICHE_DCHECK_EQ(QuicUtils::GetHeadersStreamId(transport_version()),
533                      headers_stream->id());
534 
535     headers_stream_ = headers_stream.get();
536     ActivateStream(std::move(headers_stream));
537   } else {
538     qpack_encoder_ = std::make_unique<QpackEncoder>(this, huffman_encoding_);
539     qpack_decoder_ =
540         std::make_unique<QpackDecoder>(qpack_maximum_dynamic_table_capacity_,
541                                        qpack_maximum_blocked_streams_, this);
542     MaybeInitializeHttp3UnidirectionalStreams();
543   }
544 
545   spdy_framer_visitor_->set_max_header_list_size(max_inbound_header_list_size_);
546 
547   // Limit HPACK buffering to 2x header list size limit.
548   h2_deframer_.GetHpackDecoder().set_max_decode_buffer_size_bytes(
549       2 * max_inbound_header_list_size_);
550 }
551 
FillSettingsFrame()552 void QuicSpdySession::FillSettingsFrame() {
553   settings_.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] =
554       qpack_maximum_dynamic_table_capacity_;
555   settings_.values[SETTINGS_QPACK_BLOCKED_STREAMS] =
556       qpack_maximum_blocked_streams_;
557   settings_.values[SETTINGS_MAX_FIELD_SECTION_SIZE] =
558       max_inbound_header_list_size_;
559   if (version().UsesHttp3()) {
560     switch (LocalHttpDatagramSupport()) {
561       case HttpDatagramSupport::kNone:
562         break;
563       case HttpDatagramSupport::kDraft04:
564         settings_.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1;
565         break;
566       case HttpDatagramSupport::kRfc:
567         settings_.values[SETTINGS_H3_DATAGRAM] = 1;
568         break;
569       case HttpDatagramSupport::kRfcAndDraft04:
570         settings_.values[SETTINGS_H3_DATAGRAM] = 1;
571         settings_.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1;
572         break;
573     }
574   }
575   if (WillNegotiateWebTransport()) {
576     WebTransportHttp3VersionSet versions =
577         LocallySupportedWebTransportVersions();
578     if (versions.IsSet(WebTransportHttp3Version::kDraft02)) {
579       settings_.values[SETTINGS_WEBTRANS_DRAFT00] = 1;
580     }
581     if (versions.IsSet(WebTransportHttp3Version::kDraft07)) {
582       QUICHE_BUG_IF(
583           WT_enabled_extended_connect_disabled,
584           perspective() == Perspective::IS_SERVER && !allow_extended_connect())
585           << "WebTransport enabled, but extended CONNECT is not";
586       settings_.values[SETTINGS_WEBTRANS_MAX_SESSIONS_DRAFT07] =
587           kDefaultMaxWebTransportSessions;
588     }
589   }
590   if (allow_extended_connect()) {
591     settings_.values[SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1;
592   }
593 }
594 
OnDecoderStreamError(QuicErrorCode error_code,absl::string_view error_message)595 void QuicSpdySession::OnDecoderStreamError(QuicErrorCode error_code,
596                                            absl::string_view error_message) {
597   QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
598 
599   CloseConnectionWithDetails(
600       error_code, absl::StrCat("Decoder stream error: ", error_message));
601 }
602 
OnEncoderStreamError(QuicErrorCode error_code,absl::string_view error_message)603 void QuicSpdySession::OnEncoderStreamError(QuicErrorCode error_code,
604                                            absl::string_view error_message) {
605   QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
606 
607   CloseConnectionWithDetails(
608       error_code, absl::StrCat("Encoder stream error: ", error_message));
609 }
610 
OnStreamHeadersPriority(QuicStreamId stream_id,const spdy::SpdyStreamPrecedence & precedence)611 void QuicSpdySession::OnStreamHeadersPriority(
612     QuicStreamId stream_id, const spdy::SpdyStreamPrecedence& precedence) {
613   QuicSpdyStream* stream = GetOrCreateSpdyDataStream(stream_id);
614   if (!stream) {
615     // It's quite possible to receive headers after a stream has been reset.
616     return;
617   }
618   stream->OnStreamHeadersPriority(precedence);
619 }
620 
OnStreamHeaderList(QuicStreamId stream_id,bool fin,size_t frame_len,const QuicHeaderList & header_list)621 void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id, bool fin,
622                                          size_t frame_len,
623                                          const QuicHeaderList& header_list) {
624   if (IsStaticStream(stream_id)) {
625     connection()->CloseConnection(
626         QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
627         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
628     return;
629   }
630   QuicSpdyStream* stream = GetOrCreateSpdyDataStream(stream_id);
631   if (stream == nullptr) {
632     // The stream no longer exists, but trailing headers may contain the final
633     // byte offset necessary for flow control and open stream accounting.
634     size_t final_byte_offset = 0;
635     for (const auto& header : header_list) {
636       const std::string& header_key = header.first;
637       const std::string& header_value = header.second;
638       if (header_key == kFinalOffsetHeaderKey) {
639         if (!absl::SimpleAtoi(header_value, &final_byte_offset)) {
640           connection()->CloseConnection(
641               QUIC_INVALID_HEADERS_STREAM_DATA,
642               "Trailers are malformed (no final offset)",
643               ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
644           return;
645         }
646         QUIC_DVLOG(1) << ENDPOINT
647                       << "Received final byte offset in trailers for stream "
648                       << stream_id << ", which no longer exists.";
649         OnFinalByteOffsetReceived(stream_id, final_byte_offset);
650       }
651     }
652 
653     // It's quite possible to receive headers after a stream has been reset.
654     return;
655   }
656   stream->OnStreamHeaderList(fin, frame_len, header_list);
657 }
658 
OnPriorityFrame(QuicStreamId stream_id,const spdy::SpdyStreamPrecedence & precedence)659 void QuicSpdySession::OnPriorityFrame(
660     QuicStreamId stream_id, const spdy::SpdyStreamPrecedence& precedence) {
661   QuicSpdyStream* stream = GetOrCreateSpdyDataStream(stream_id);
662   if (!stream) {
663     // It's quite possible to receive a PRIORITY frame after a stream has been
664     // reset.
665     return;
666   }
667   stream->OnPriorityFrame(precedence);
668 }
669 
OnPriorityUpdateForRequestStream(QuicStreamId stream_id,HttpStreamPriority priority)670 bool QuicSpdySession::OnPriorityUpdateForRequestStream(
671     QuicStreamId stream_id, HttpStreamPriority priority) {
672   if (perspective() == Perspective::IS_CLIENT ||
673       !QuicUtils::IsBidirectionalStreamId(stream_id, version()) ||
674       !QuicUtils::IsClientInitiatedStreamId(transport_version(), stream_id)) {
675     return true;
676   }
677 
678   QuicStreamCount advertised_max_incoming_bidirectional_streams =
679       GetAdvertisedMaxIncomingBidirectionalStreams();
680   if (advertised_max_incoming_bidirectional_streams == 0 ||
681       stream_id > QuicUtils::GetFirstBidirectionalStreamId(
682                       transport_version(), Perspective::IS_CLIENT) +
683                       QuicUtils::StreamIdDelta(transport_version()) *
684                           (advertised_max_incoming_bidirectional_streams - 1)) {
685     connection()->CloseConnection(
686         QUIC_INVALID_STREAM_ID,
687         "PRIORITY_UPDATE frame received for invalid stream.",
688         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
689     return false;
690   }
691 
692   if (MaybeSetStreamPriority(stream_id, QuicStreamPriority(priority))) {
693     return true;
694   }
695 
696   if (IsClosedStream(stream_id)) {
697     return true;
698   }
699 
700   buffered_stream_priorities_[stream_id] = priority;
701 
702   if (buffered_stream_priorities_.size() >
703       10 * max_open_incoming_bidirectional_streams()) {
704     // This should never happen, because |buffered_stream_priorities_| should
705     // only contain entries for streams that are allowed to be open by the peer
706     // but have not been opened yet.
707     std::string error_message =
708         absl::StrCat("Too many stream priority values buffered: ",
709                      buffered_stream_priorities_.size(),
710                      ", which should not exceed the incoming stream limit of ",
711                      max_open_incoming_bidirectional_streams());
712     QUIC_BUG(quic_bug_10360_2) << error_message;
713     connection()->CloseConnection(
714         QUIC_INTERNAL_ERROR, error_message,
715         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
716     return false;
717   }
718 
719   return true;
720 }
721 
ProcessHeaderData(const struct iovec & iov)722 size_t QuicSpdySession::ProcessHeaderData(const struct iovec& iov) {
723   QUIC_BUG_IF(quic_bug_12477_4, destruction_indicator_ != 123456789)
724       << "QuicSpdyStream use after free. " << destruction_indicator_
725       << QuicStackTrace();
726   return h2_deframer_.ProcessInput(static_cast<char*>(iov.iov_base),
727                                    iov.iov_len);
728 }
729 
WriteHeadersOnHeadersStream(QuicStreamId id,Http2HeaderBlock headers,bool fin,const spdy::SpdyStreamPrecedence & precedence,quiche::QuicheReferenceCountedPointer<QuicAckListenerInterface> ack_listener)730 size_t QuicSpdySession::WriteHeadersOnHeadersStream(
731     QuicStreamId id, Http2HeaderBlock headers, bool fin,
732     const spdy::SpdyStreamPrecedence& precedence,
733     quiche::QuicheReferenceCountedPointer<QuicAckListenerInterface>
734         ack_listener) {
735   QUICHE_DCHECK(!VersionUsesHttp3(transport_version()));
736 
737   return WriteHeadersOnHeadersStreamImpl(
738       id, std::move(headers), fin,
739       /* parent_stream_id = */ 0,
740       Spdy3PriorityToHttp2Weight(precedence.spdy3_priority()),
741       /* exclusive = */ false, std::move(ack_listener));
742 }
743 
WritePriority(QuicStreamId stream_id,QuicStreamId parent_stream_id,int weight,bool exclusive)744 size_t QuicSpdySession::WritePriority(QuicStreamId stream_id,
745                                       QuicStreamId parent_stream_id, int weight,
746                                       bool exclusive) {
747   QUICHE_DCHECK(!VersionUsesHttp3(transport_version()));
748   SpdyPriorityIR priority_frame(stream_id, parent_stream_id, weight, exclusive);
749   SpdySerializedFrame frame(spdy_framer_.SerializeFrame(priority_frame));
750   headers_stream()->WriteOrBufferData(
751       absl::string_view(frame.data(), frame.size()), false, nullptr);
752   return frame.size();
753 }
754 
WriteHttp3PriorityUpdate(QuicStreamId stream_id,HttpStreamPriority priority)755 void QuicSpdySession::WriteHttp3PriorityUpdate(QuicStreamId stream_id,
756                                                HttpStreamPriority priority) {
757   QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
758 
759   send_control_stream_->WritePriorityUpdate(stream_id, priority);
760 }
761 
OnHttp3GoAway(uint64_t id)762 void QuicSpdySession::OnHttp3GoAway(uint64_t id) {
763   QUIC_BUG_IF(quic_bug_12477_5, !version().UsesHttp3())
764       << "HTTP/3 GOAWAY received on version " << version();
765 
766   if (last_received_http3_goaway_id_.has_value() &&
767       id > *last_received_http3_goaway_id_) {
768     CloseConnectionWithDetails(
769         QUIC_HTTP_GOAWAY_ID_LARGER_THAN_PREVIOUS,
770         absl::StrCat("GOAWAY received with ID ", id,
771                      " greater than previously received ID ",
772                      *last_received_http3_goaway_id_));
773     return;
774   }
775   last_received_http3_goaway_id_ = id;
776 
777   if (perspective() == Perspective::IS_SERVER) {
778     // TODO(b/151749109): Cancel server pushes with push ID larger than |id|.
779     return;
780   }
781 
782   // QuicStreamId is uint32_t.  Casting to this narrower type is well-defined
783   // and preserves the lower 32 bits.  Both IsBidirectionalStreamId() and
784   // IsIncomingStream() give correct results, because their return value is
785   // determined by the least significant two bits.
786   QuicStreamId stream_id = static_cast<QuicStreamId>(id);
787   if (!QuicUtils::IsBidirectionalStreamId(stream_id, version()) ||
788       IsIncomingStream(stream_id)) {
789     CloseConnectionWithDetails(QUIC_HTTP_GOAWAY_INVALID_STREAM_ID,
790                                "GOAWAY with invalid stream ID");
791     return;
792   }
793 
794   if (SupportsWebTransport()) {
795     PerformActionOnActiveStreams([](QuicStream* stream) {
796       if (!QuicUtils::IsBidirectionalStreamId(stream->id(),
797                                               stream->version()) ||
798           !QuicUtils::IsClientInitiatedStreamId(
799               stream->version().transport_version, stream->id())) {
800         return true;
801       }
802       QuicSpdyStream* spdy_stream = static_cast<QuicSpdyStream*>(stream);
803       WebTransportHttp3* web_transport = spdy_stream->web_transport();
804       if (web_transport == nullptr) {
805         return true;
806       }
807       web_transport->OnGoAwayReceived();
808       return true;
809     });
810   }
811 
812   // TODO(b/161252736): Cancel client requests with ID larger than |id|.
813   // If |id| is larger than numeric_limits<QuicStreamId>::max(), then use
814   // max() instead of downcast value.
815 }
816 
OnStreamsBlockedFrame(const QuicStreamsBlockedFrame & frame)817 bool QuicSpdySession::OnStreamsBlockedFrame(
818     const QuicStreamsBlockedFrame& frame) {
819   if (!QuicSession::OnStreamsBlockedFrame(frame)) {
820     return false;
821   }
822 
823   // The peer asked for stream space more than this implementation has. Send
824   // goaway.
825   if (perspective() == Perspective::IS_SERVER &&
826       frame.stream_count >= QuicUtils::GetMaxStreamCount()) {
827     QUICHE_DCHECK_EQ(frame.stream_count, QuicUtils::GetMaxStreamCount());
828     SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "stream count too large");
829   }
830   return true;
831 }
832 
SendHttp3GoAway(QuicErrorCode error_code,const std::string & reason)833 void QuicSpdySession::SendHttp3GoAway(QuicErrorCode error_code,
834                                       const std::string& reason) {
835   QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
836   if (!IsEncryptionEstablished()) {
837     QUIC_CODE_COUNT(quic_h3_goaway_before_encryption_established);
838     connection()->CloseConnection(
839         error_code, reason,
840         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
841     return;
842   }
843   ietf_streamid_manager().StopIncreasingIncomingMaxStreams();
844 
845   QuicStreamId stream_id =
846       QuicUtils::GetMaxClientInitiatedBidirectionalStreamId(
847           transport_version());
848   if (last_sent_http3_goaway_id_.has_value() &&
849       *last_sent_http3_goaway_id_ <= stream_id) {
850     // Do not send GOAWAY frame with a higher id, because it is forbidden.
851     // Do not send one with same stream id as before, since frames on the
852     // control stream are guaranteed to be processed in order.
853     return;
854   }
855 
856   send_control_stream_->SendGoAway(stream_id);
857   last_sent_http3_goaway_id_ = stream_id;
858 }
859 
SendInitialData()860 void QuicSpdySession::SendInitialData() {
861   if (!VersionUsesHttp3(transport_version())) {
862     return;
863   }
864   QuicConnection::ScopedPacketFlusher flusher(connection());
865   send_control_stream_->MaybeSendSettingsFrame();
866 }
867 
CheckStreamWriteBlocked(QuicStream * stream) const868 bool QuicSpdySession::CheckStreamWriteBlocked(QuicStream* stream) const {
869   if (GetQuicRestartFlag(quic_opport_bundle_qpack_decoder_data4) &&
870       qpack_decoder_send_stream_ != nullptr &&
871       stream->id() == qpack_decoder_send_stream_->id()) {
872     // Decoder data is always bundled opportunistically.
873     return true;
874   }
875   return QuicSession::CheckStreamWriteBlocked(stream);
876 }
877 
qpack_encoder()878 QpackEncoder* QuicSpdySession::qpack_encoder() {
879   QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
880 
881   return qpack_encoder_.get();
882 }
883 
qpack_decoder()884 QpackDecoder* QuicSpdySession::qpack_decoder() {
885   QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
886 
887   return qpack_decoder_.get();
888 }
889 
OnStreamCreated(QuicSpdyStream * stream)890 void QuicSpdySession::OnStreamCreated(QuicSpdyStream* stream) {
891   auto it = buffered_stream_priorities_.find(stream->id());
892   if (it == buffered_stream_priorities_.end()) {
893     return;
894   }
895 
896   stream->SetPriority(QuicStreamPriority(it->second));
897   buffered_stream_priorities_.erase(it);
898 }
899 
GetOrCreateSpdyDataStream(const QuicStreamId stream_id)900 QuicSpdyStream* QuicSpdySession::GetOrCreateSpdyDataStream(
901     const QuicStreamId stream_id) {
902   QuicStream* stream = GetOrCreateStream(stream_id);
903   if (stream && stream->is_static()) {
904     QUIC_BUG(quic_bug_10360_5)
905         << "GetOrCreateSpdyDataStream returns static stream " << stream_id
906         << " in version " << transport_version() << "\n"
907         << QuicStackTrace();
908     connection()->CloseConnection(
909         QUIC_INVALID_STREAM_ID,
910         absl::StrCat("stream ", stream_id, " is static"),
911         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
912     return nullptr;
913   }
914   return static_cast<QuicSpdyStream*>(stream);
915 }
916 
OnNewEncryptionKeyAvailable(EncryptionLevel level,std::unique_ptr<QuicEncrypter> encrypter)917 void QuicSpdySession::OnNewEncryptionKeyAvailable(
918     EncryptionLevel level, std::unique_ptr<QuicEncrypter> encrypter) {
919   QuicSession::OnNewEncryptionKeyAvailable(level, std::move(encrypter));
920   if (IsEncryptionEstablished()) {
921     // Send H3 SETTINGs once encryption is established.
922     SendInitialData();
923   }
924 }
925 
ShouldNegotiateWebTransport() const926 bool QuicSpdySession::ShouldNegotiateWebTransport() const {
927   return LocallySupportedWebTransportVersions().Any();
928 }
929 
930 WebTransportHttp3VersionSet
LocallySupportedWebTransportVersions() const931 QuicSpdySession::LocallySupportedWebTransportVersions() const {
932   return WebTransportHttp3VersionSet();
933 }
934 
WillNegotiateWebTransport()935 bool QuicSpdySession::WillNegotiateWebTransport() {
936   return LocalHttpDatagramSupport() != HttpDatagramSupport::kNone &&
937          version().UsesHttp3() && ShouldNegotiateWebTransport();
938 }
939 
940 // True if there are open HTTP requests.
ShouldKeepConnectionAlive() const941 bool QuicSpdySession::ShouldKeepConnectionAlive() const {
942   QUICHE_DCHECK(VersionUsesHttp3(transport_version()) ||
943                 0u == pending_streams_size());
944   return GetNumActiveStreams() + pending_streams_size() > 0;
945 }
946 
UsesPendingStreamForFrame(QuicFrameType type,QuicStreamId stream_id) const947 bool QuicSpdySession::UsesPendingStreamForFrame(QuicFrameType type,
948                                                 QuicStreamId stream_id) const {
949   // Pending streams can only be used to handle unidirectional stream with
950   // STREAM & RESET_STREAM frames in IETF QUIC.
951   return VersionUsesHttp3(transport_version()) &&
952          (type == STREAM_FRAME || type == RST_STREAM_FRAME) &&
953          QuicUtils::GetStreamType(stream_id, perspective(),
954                                   IsIncomingStream(stream_id),
955                                   version()) == READ_UNIDIRECTIONAL;
956 }
957 
WriteHeadersOnHeadersStreamImpl(QuicStreamId id,spdy::Http2HeaderBlock headers,bool fin,QuicStreamId parent_stream_id,int weight,bool exclusive,quiche::QuicheReferenceCountedPointer<QuicAckListenerInterface> ack_listener)958 size_t QuicSpdySession::WriteHeadersOnHeadersStreamImpl(
959     QuicStreamId id, spdy::Http2HeaderBlock headers, bool fin,
960     QuicStreamId parent_stream_id, int weight, bool exclusive,
961     quiche::QuicheReferenceCountedPointer<QuicAckListenerInterface>
962         ack_listener) {
963   QUICHE_DCHECK(!VersionUsesHttp3(transport_version()));
964 
965   const QuicByteCount uncompressed_size = headers.TotalBytesUsed();
966   SpdyHeadersIR headers_frame(id, std::move(headers));
967   headers_frame.set_fin(fin);
968   if (perspective() == Perspective::IS_CLIENT) {
969     headers_frame.set_has_priority(true);
970     headers_frame.set_parent_stream_id(parent_stream_id);
971     headers_frame.set_weight(weight);
972     headers_frame.set_exclusive(exclusive);
973   }
974   SpdySerializedFrame frame(spdy_framer_.SerializeFrame(headers_frame));
975   headers_stream()->WriteOrBufferData(
976       absl::string_view(frame.data(), frame.size()), false,
977       std::move(ack_listener));
978 
979   // Calculate compressed header block size without framing overhead.
980   QuicByteCount compressed_size = frame.size();
981   compressed_size -= spdy::kFrameHeaderSize;
982   if (perspective() == Perspective::IS_CLIENT) {
983     // Exclusive bit and Stream Dependency are four bytes, weight is one more.
984     compressed_size -= 5;
985   }
986 
987   LogHeaderCompressionRatioHistogram(
988       /* using_qpack = */ false,
989       /* is_sent = */ true, compressed_size, uncompressed_size);
990 
991   return frame.size();
992 }
993 
ResumeApplicationState(ApplicationState * cached_state)994 bool QuicSpdySession::ResumeApplicationState(ApplicationState* cached_state) {
995   QUICHE_DCHECK_EQ(perspective(), Perspective::IS_CLIENT);
996   QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
997 
998   SettingsFrame out;
999   if (!HttpDecoder::DecodeSettings(
1000           reinterpret_cast<char*>(cached_state->data()), cached_state->size(),
1001           &out)) {
1002     return false;
1003   }
1004 
1005   if (debug_visitor_ != nullptr) {
1006     debug_visitor_->OnSettingsFrameResumed(out);
1007   }
1008   QUICHE_DCHECK(streams_waiting_for_settings_.empty());
1009   for (const auto& setting : out.values) {
1010     OnSetting(setting.first, setting.second);
1011   }
1012   return true;
1013 }
1014 
OnAlpsData(const uint8_t * alps_data,size_t alps_length)1015 std::optional<std::string> QuicSpdySession::OnAlpsData(const uint8_t* alps_data,
1016                                                        size_t alps_length) {
1017   AlpsFrameDecoder alps_frame_decoder(this);
1018   HttpDecoder decoder(&alps_frame_decoder);
1019   decoder.ProcessInput(reinterpret_cast<const char*>(alps_data), alps_length);
1020   if (alps_frame_decoder.error_detail()) {
1021     return alps_frame_decoder.error_detail();
1022   }
1023 
1024   if (decoder.error() != QUIC_NO_ERROR) {
1025     return decoder.error_detail();
1026   }
1027 
1028   if (!decoder.AtFrameBoundary()) {
1029     return "incomplete HTTP/3 frame";
1030   }
1031 
1032   return std::nullopt;
1033 }
1034 
OnAcceptChFrameReceivedViaAlps(const AcceptChFrame & frame)1035 void QuicSpdySession::OnAcceptChFrameReceivedViaAlps(
1036     const AcceptChFrame& frame) {
1037   if (debug_visitor_) {
1038     debug_visitor_->OnAcceptChFrameReceivedViaAlps(frame);
1039   }
1040 }
1041 
OnSettingsFrame(const SettingsFrame & frame)1042 bool QuicSpdySession::OnSettingsFrame(const SettingsFrame& frame) {
1043   QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
1044   if (debug_visitor_ != nullptr) {
1045     debug_visitor_->OnSettingsFrameReceived(frame);
1046   }
1047   for (const auto& setting : frame.values) {
1048     if (!OnSetting(setting.first, setting.second)) {
1049       return false;
1050     }
1051   }
1052 
1053   if (!ValidateWebTransportSettingsConsistency()) {
1054     return false;
1055   }
1056 
1057   // This is the last point in the connection when we can receive new SETTINGS
1058   // values (ALPS and settings from the session ticket come before, and only one
1059   // SETTINGS frame per connection is allowed).  Notify all the streams that are
1060   // blocking on having the definitive settings list.
1061   QUICHE_DCHECK(!settings_received_);
1062   settings_received_ = true;
1063   for (QuicStreamId stream_id : streams_waiting_for_settings_) {
1064     QUICHE_RELOADABLE_FLAG_COUNT_N(quic_block_until_settings_received_copt, 4,
1065                                    4);
1066     QUICHE_DCHECK(ShouldBufferRequestsUntilSettings());
1067     QuicSpdyStream* stream = GetOrCreateSpdyDataStream(stream_id);
1068     if (stream == nullptr) {
1069       // The stream may no longer exist, since it is possible for a stream to
1070       // get reset while waiting for the SETTINGS frame.
1071       continue;
1072     }
1073     stream->OnDataAvailable();
1074   }
1075   streams_waiting_for_settings_.clear();
1076 
1077   return true;
1078 }
1079 
ValidateWebTransportSettingsConsistency()1080 bool QuicSpdySession::ValidateWebTransportSettingsConsistency() {
1081   // Only apply the following checks to draft-07 or later.
1082   std::optional<WebTransportHttp3Version> version =
1083       NegotiatedWebTransportVersion();
1084   if (!version.has_value() || *version == WebTransportHttp3Version::kDraft02) {
1085     return true;
1086   }
1087 
1088   if (!allow_extended_connect_) {
1089     CloseConnectionWithDetails(
1090         QUIC_HTTP_INVALID_SETTING_VALUE,
1091         "Negotiated use of WebTransport over HTTP/3 (draft-07 or later), but "
1092         "failed to negotiate extended CONNECT");
1093     return false;
1094   }
1095 
1096   if (http_datagram_support_ == HttpDatagramSupport::kDraft04) {
1097     CloseConnectionWithDetails(
1098         QUIC_HTTP_INVALID_SETTING_VALUE,
1099         "WebTransport over HTTP/3 version draft-07 and beyond requires the "
1100         "RFC version of HTTP datagrams");
1101     return false;
1102   }
1103 
1104   if (http_datagram_support_ != HttpDatagramSupport::kRfc) {
1105     CloseConnectionWithDetails(
1106         QUIC_HTTP_INVALID_SETTING_VALUE,
1107         "WebTransport over HTTP/3 requires HTTP datagrams support");
1108     return false;
1109   }
1110 
1111   return true;
1112 }
1113 
OnSettingsFrameViaAlps(const SettingsFrame & frame)1114 std::optional<std::string> QuicSpdySession::OnSettingsFrameViaAlps(
1115     const SettingsFrame& frame) {
1116   QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
1117 
1118   if (debug_visitor_ != nullptr) {
1119     debug_visitor_->OnSettingsFrameReceivedViaAlps(frame);
1120   }
1121   for (const auto& setting : frame.values) {
1122     if (!OnSetting(setting.first, setting.second)) {
1123       // Do not bother adding the setting identifier or value to the error
1124       // message, because OnSetting() already closed the connection, therefore
1125       // the error message will be ignored.
1126       return "error parsing setting";
1127     }
1128   }
1129   return std::nullopt;
1130 }
1131 
VerifySettingIsZeroOrOne(uint64_t id,uint64_t value)1132 bool QuicSpdySession::VerifySettingIsZeroOrOne(uint64_t id, uint64_t value) {
1133   if (value == 0 || value == 1) {
1134     return true;
1135   }
1136   std::string error_details = absl::StrCat(
1137       "Received ",
1138       H3SettingsToString(static_cast<Http3AndQpackSettingsIdentifiers>(id)),
1139       " with invalid value ", value);
1140   QUIC_PEER_BUG(bad received setting) << ENDPOINT << error_details;
1141   CloseConnectionWithDetails(QUIC_HTTP_INVALID_SETTING_VALUE, error_details);
1142   return false;
1143 }
1144 
OnSetting(uint64_t id,uint64_t value)1145 bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) {
1146   if (VersionUsesHttp3(transport_version())) {
1147     // SETTINGS frame received on the control stream.
1148     switch (id) {
1149       case SETTINGS_QPACK_MAX_TABLE_CAPACITY: {
1150         QUIC_DVLOG(1)
1151             << ENDPOINT
1152             << "SETTINGS_QPACK_MAX_TABLE_CAPACITY received with value "
1153             << value;
1154         // Communicate |value| to encoder, because it is used for encoding
1155         // Required Insert Count.
1156         if (!qpack_encoder_->SetMaximumDynamicTableCapacity(value)) {
1157           CloseConnectionWithDetails(
1158               was_zero_rtt_rejected()
1159                   ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH
1160                   : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
1161               absl::StrCat(was_zero_rtt_rejected()
1162                                ? "Server rejected 0-RTT, aborting because "
1163                                : "",
1164                            "Server sent an SETTINGS_QPACK_MAX_TABLE_CAPACITY: ",
1165                            value, " while current value is: ",
1166                            qpack_encoder_->MaximumDynamicTableCapacity()));
1167           return false;
1168         }
1169         // However, limit the dynamic table capacity to
1170         // |qpack_maximum_dynamic_table_capacity_|.
1171         qpack_encoder_->SetDynamicTableCapacity(
1172             std::min(value, qpack_maximum_dynamic_table_capacity_));
1173         break;
1174       }
1175       case SETTINGS_MAX_FIELD_SECTION_SIZE:
1176         QUIC_DVLOG(1) << ENDPOINT
1177                       << "SETTINGS_MAX_FIELD_SECTION_SIZE received with value "
1178                       << value;
1179         if (max_outbound_header_list_size_ !=
1180                 std::numeric_limits<size_t>::max() &&
1181             max_outbound_header_list_size_ > value) {
1182           CloseConnectionWithDetails(
1183               was_zero_rtt_rejected()
1184                   ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH
1185                   : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
1186               absl::StrCat(was_zero_rtt_rejected()
1187                                ? "Server rejected 0-RTT, aborting because "
1188                                : "",
1189                            "Server sent an SETTINGS_MAX_FIELD_SECTION_SIZE: ",
1190                            value, " which reduces current value: ",
1191                            max_outbound_header_list_size_));
1192           return false;
1193         }
1194         max_outbound_header_list_size_ = value;
1195         break;
1196       case SETTINGS_QPACK_BLOCKED_STREAMS: {
1197         QUIC_DVLOG(1) << ENDPOINT
1198                       << "SETTINGS_QPACK_BLOCKED_STREAMS received with value "
1199                       << value;
1200         if (!qpack_encoder_->SetMaximumBlockedStreams(value)) {
1201           CloseConnectionWithDetails(
1202               was_zero_rtt_rejected()
1203                   ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH
1204                   : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
1205               absl::StrCat(was_zero_rtt_rejected()
1206                                ? "Server rejected 0-RTT, aborting because "
1207                                : "",
1208                            "Server sent an SETTINGS_QPACK_BLOCKED_STREAMS: ",
1209                            value, " which reduces current value: ",
1210                            qpack_encoder_->maximum_blocked_streams()));
1211           return false;
1212         }
1213         break;
1214       }
1215       case SETTINGS_ENABLE_CONNECT_PROTOCOL: {
1216         QUIC_DVLOG(1) << ENDPOINT
1217                       << "SETTINGS_ENABLE_CONNECT_PROTOCOL received with value "
1218                       << value;
1219         if (!VerifySettingIsZeroOrOne(id, value)) {
1220           return false;
1221         }
1222         if (perspective() == Perspective::IS_CLIENT) {
1223           allow_extended_connect_ = value != 0;
1224         }
1225         break;
1226       }
1227       case spdy::SETTINGS_ENABLE_PUSH:
1228         ABSL_FALLTHROUGH_INTENDED;
1229       case spdy::SETTINGS_MAX_CONCURRENT_STREAMS:
1230         ABSL_FALLTHROUGH_INTENDED;
1231       case spdy::SETTINGS_INITIAL_WINDOW_SIZE:
1232         ABSL_FALLTHROUGH_INTENDED;
1233       case spdy::SETTINGS_MAX_FRAME_SIZE:
1234         CloseConnectionWithDetails(
1235             QUIC_HTTP_RECEIVE_SPDY_SETTING,
1236             absl::StrCat("received HTTP/2 specific setting in HTTP/3 session: ",
1237                          id));
1238         return false;
1239       case SETTINGS_H3_DATAGRAM_DRAFT04: {
1240         HttpDatagramSupport local_http_datagram_support =
1241             LocalHttpDatagramSupport();
1242         if (local_http_datagram_support != HttpDatagramSupport::kDraft04 &&
1243             local_http_datagram_support !=
1244                 HttpDatagramSupport::kRfcAndDraft04) {
1245           break;
1246         }
1247         QUIC_DVLOG(1) << ENDPOINT
1248                       << "SETTINGS_H3_DATAGRAM_DRAFT04 received with value "
1249                       << value;
1250         if (!version().UsesHttp3()) {
1251           break;
1252         }
1253         if (!VerifySettingIsZeroOrOne(id, value)) {
1254           return false;
1255         }
1256         if (value && http_datagram_support_ != HttpDatagramSupport::kRfc) {
1257           // If both RFC 9297 and draft-04 are supported, we use the RFC. This
1258           // is implemented by ignoring SETTINGS_H3_DATAGRAM_DRAFT04 when we've
1259           // already parsed SETTINGS_H3_DATAGRAM.
1260           http_datagram_support_ = HttpDatagramSupport::kDraft04;
1261         }
1262         break;
1263       }
1264       case SETTINGS_H3_DATAGRAM: {
1265         HttpDatagramSupport local_http_datagram_support =
1266             LocalHttpDatagramSupport();
1267         if (local_http_datagram_support != HttpDatagramSupport::kRfc &&
1268             local_http_datagram_support !=
1269                 HttpDatagramSupport::kRfcAndDraft04) {
1270           break;
1271         }
1272         QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_H3_DATAGRAM received with value "
1273                       << value;
1274         if (!version().UsesHttp3()) {
1275           break;
1276         }
1277         if (!VerifySettingIsZeroOrOne(id, value)) {
1278           return false;
1279         }
1280         if (value) {
1281           http_datagram_support_ = HttpDatagramSupport::kRfc;
1282         }
1283         break;
1284       }
1285       case SETTINGS_WEBTRANS_DRAFT00:
1286         if (!WillNegotiateWebTransport()) {
1287           break;
1288         }
1289         QUIC_DVLOG(1) << ENDPOINT
1290                       << "SETTINGS_ENABLE_WEBTRANSPORT(02) received with value "
1291                       << value;
1292         if (!VerifySettingIsZeroOrOne(id, value)) {
1293           return false;
1294         }
1295         if (value == 1) {
1296           peer_web_transport_versions_.Set(WebTransportHttp3Version::kDraft02);
1297           if (perspective() == Perspective::IS_CLIENT) {
1298             allow_extended_connect_ = true;
1299           }
1300         }
1301         break;
1302       case SETTINGS_WEBTRANS_MAX_SESSIONS_DRAFT07:
1303         if (!WillNegotiateWebTransport()) {
1304           break;
1305         }
1306         QUIC_DVLOG(1)
1307             << ENDPOINT
1308             << "SETTINGS_WEBTRANS_MAX_SESSIONS_DRAFT07 received with value "
1309             << value;
1310         if (value > 0) {
1311           peer_web_transport_versions_.Set(WebTransportHttp3Version::kDraft07);
1312           if (perspective() == Perspective::IS_CLIENT) {
1313             max_webtransport_sessions_[WebTransportHttp3Version::kDraft07] =
1314                 value;
1315           }
1316         }
1317         break;
1318       default:
1319         QUIC_DVLOG(1) << ENDPOINT << "Unknown setting identifier " << id
1320                       << " received with value " << value;
1321         // Ignore unknown settings.
1322         break;
1323     }
1324     return true;
1325   }
1326 
1327   // SETTINGS frame received on the headers stream.
1328   switch (id) {
1329     case spdy::SETTINGS_HEADER_TABLE_SIZE:
1330       QUIC_DVLOG(1) << ENDPOINT
1331                     << "SETTINGS_HEADER_TABLE_SIZE received with value "
1332                     << value;
1333       spdy_framer_.UpdateHeaderEncoderTableSize(
1334           std::min<uint64_t>(value, kHpackEncoderDynamicTableSizeLimit));
1335       break;
1336     case spdy::SETTINGS_ENABLE_PUSH:
1337       if (perspective() == Perspective::IS_SERVER) {
1338         // See rfc7540, Section 6.5.2.
1339         if (value > 1) {
1340           QUIC_DLOG(ERROR) << ENDPOINT << "Invalid value " << value
1341                            << " received for SETTINGS_ENABLE_PUSH.";
1342           if (IsConnected()) {
1343             CloseConnectionWithDetails(
1344                 QUIC_INVALID_HEADERS_STREAM_DATA,
1345                 absl::StrCat("Invalid value for SETTINGS_ENABLE_PUSH: ",
1346                              value));
1347           }
1348           return true;
1349         }
1350         QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_ENABLE_PUSH received with value "
1351                       << value << ", ignoring.";
1352         break;
1353       } else {
1354         QUIC_DLOG(ERROR)
1355             << ENDPOINT
1356             << "Invalid SETTINGS_ENABLE_PUSH received by client with value "
1357             << value;
1358         if (IsConnected()) {
1359           CloseConnectionWithDetails(
1360               QUIC_INVALID_HEADERS_STREAM_DATA,
1361               absl::StrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id));
1362         }
1363       }
1364       break;
1365     case spdy::SETTINGS_MAX_HEADER_LIST_SIZE:
1366       QUIC_DVLOG(1) << ENDPOINT
1367                     << "SETTINGS_MAX_HEADER_LIST_SIZE received with value "
1368                     << value;
1369       max_outbound_header_list_size_ = value;
1370       break;
1371     default:
1372       QUIC_DLOG(ERROR) << ENDPOINT << "Unknown setting identifier " << id
1373                        << " received with value " << value;
1374       if (IsConnected()) {
1375         CloseConnectionWithDetails(
1376             QUIC_INVALID_HEADERS_STREAM_DATA,
1377             absl::StrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id));
1378       }
1379   }
1380   return true;
1381 }
1382 
ShouldReleaseHeadersStreamSequencerBuffer()1383 bool QuicSpdySession::ShouldReleaseHeadersStreamSequencerBuffer() {
1384   return false;
1385 }
1386 
OnHeaders(SpdyStreamId stream_id,bool has_priority,const spdy::SpdyStreamPrecedence & precedence,bool fin)1387 void QuicSpdySession::OnHeaders(SpdyStreamId stream_id, bool has_priority,
1388                                 const spdy::SpdyStreamPrecedence& precedence,
1389                                 bool fin) {
1390   if (has_priority) {
1391     if (perspective() == Perspective::IS_CLIENT) {
1392       CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
1393                                  "Server must not send priorities.");
1394       return;
1395     }
1396     OnStreamHeadersPriority(stream_id, precedence);
1397   } else {
1398     if (perspective() == Perspective::IS_SERVER) {
1399       CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
1400                                  "Client must send priorities.");
1401       return;
1402     }
1403   }
1404   QUICHE_DCHECK_EQ(QuicUtils::GetInvalidStreamId(transport_version()),
1405                    stream_id_);
1406   stream_id_ = stream_id;
1407   fin_ = fin;
1408 }
1409 
1410 // TODO (wangyix): Why is SpdyStreamId used instead of QuicStreamId?
1411 // This occurs in many places in this file.
OnPriority(SpdyStreamId stream_id,const spdy::SpdyStreamPrecedence & precedence)1412 void QuicSpdySession::OnPriority(SpdyStreamId stream_id,
1413                                  const spdy::SpdyStreamPrecedence& precedence) {
1414   if (perspective() == Perspective::IS_CLIENT) {
1415     CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
1416                                "Server must not send PRIORITY frames.");
1417     return;
1418   }
1419   OnPriorityFrame(stream_id, precedence);
1420 }
1421 
OnHeaderList(const QuicHeaderList & header_list)1422 void QuicSpdySession::OnHeaderList(const QuicHeaderList& header_list) {
1423   QUIC_DVLOG(1) << ENDPOINT << "Received header list for stream " << stream_id_
1424                 << ": " << header_list.DebugString();
1425   QUICHE_DCHECK(!VersionUsesHttp3(transport_version()));
1426 
1427   OnStreamHeaderList(stream_id_, fin_, frame_len_, header_list);
1428 
1429   // Reset state for the next frame.
1430   stream_id_ = QuicUtils::GetInvalidStreamId(transport_version());
1431   fin_ = false;
1432   frame_len_ = 0;
1433 }
1434 
OnCompressedFrameSize(size_t frame_len)1435 void QuicSpdySession::OnCompressedFrameSize(size_t frame_len) {
1436   frame_len_ += frame_len;
1437 }
1438 
CloseConnectionWithDetails(QuicErrorCode error,const std::string & details)1439 void QuicSpdySession::CloseConnectionWithDetails(QuicErrorCode error,
1440                                                  const std::string& details) {
1441   connection()->CloseConnection(
1442       error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
1443 }
1444 
HasActiveRequestStreams() const1445 bool QuicSpdySession::HasActiveRequestStreams() const {
1446   return GetNumActiveStreams() + num_draining_streams() > 0;
1447 }
1448 
ProcessReadUnidirectionalPendingStream(PendingStream * pending)1449 QuicStream* QuicSpdySession::ProcessReadUnidirectionalPendingStream(
1450     PendingStream* pending) {
1451   struct iovec iov;
1452   if (!pending->sequencer()->GetReadableRegion(&iov)) {
1453     // The first byte hasn't been received yet.
1454     return nullptr;
1455   }
1456 
1457   QuicDataReader reader(static_cast<char*>(iov.iov_base), iov.iov_len);
1458   uint8_t stream_type_length = reader.PeekVarInt62Length();
1459   uint64_t stream_type = 0;
1460   if (!reader.ReadVarInt62(&stream_type)) {
1461     if (pending->sequencer()->NumBytesBuffered() ==
1462         pending->sequencer()->close_offset()) {
1463       // Stream received FIN but there are not enough bytes for stream type.
1464       // Mark all bytes consumed in order to close stream.
1465       pending->MarkConsumed(pending->sequencer()->close_offset());
1466     }
1467     return nullptr;
1468   }
1469   pending->MarkConsumed(stream_type_length);
1470 
1471   switch (stream_type) {
1472     case kControlStream: {  // HTTP/3 control stream.
1473       if (receive_control_stream_) {
1474         CloseConnectionOnDuplicateHttp3UnidirectionalStreams("Control");
1475         return nullptr;
1476       }
1477       auto receive_stream =
1478           std::make_unique<QuicReceiveControlStream>(pending, this);
1479       receive_control_stream_ = receive_stream.get();
1480       ActivateStream(std::move(receive_stream));
1481       QUIC_DVLOG(1) << ENDPOINT << "Receive Control stream is created";
1482       if (debug_visitor_ != nullptr) {
1483         debug_visitor_->OnPeerControlStreamCreated(
1484             receive_control_stream_->id());
1485       }
1486       return receive_control_stream_;
1487     }
1488     case kServerPushStream: {  // Push Stream.
1489       CloseConnectionWithDetails(QUIC_HTTP_RECEIVE_SERVER_PUSH,
1490                                  "Received server push stream");
1491       return nullptr;
1492     }
1493     case kQpackEncoderStream: {  // QPACK encoder stream.
1494       if (qpack_encoder_receive_stream_) {
1495         CloseConnectionOnDuplicateHttp3UnidirectionalStreams("QPACK encoder");
1496         return nullptr;
1497       }
1498       auto encoder_receive = std::make_unique<QpackReceiveStream>(
1499           pending, this, qpack_decoder_->encoder_stream_receiver());
1500       qpack_encoder_receive_stream_ = encoder_receive.get();
1501       ActivateStream(std::move(encoder_receive));
1502       QUIC_DVLOG(1) << ENDPOINT << "Receive QPACK Encoder stream is created";
1503       if (debug_visitor_ != nullptr) {
1504         debug_visitor_->OnPeerQpackEncoderStreamCreated(
1505             qpack_encoder_receive_stream_->id());
1506       }
1507       return qpack_encoder_receive_stream_;
1508     }
1509     case kQpackDecoderStream: {  // QPACK decoder stream.
1510       if (qpack_decoder_receive_stream_) {
1511         CloseConnectionOnDuplicateHttp3UnidirectionalStreams("QPACK decoder");
1512         return nullptr;
1513       }
1514       auto decoder_receive = std::make_unique<QpackReceiveStream>(
1515           pending, this, qpack_encoder_->decoder_stream_receiver());
1516       qpack_decoder_receive_stream_ = decoder_receive.get();
1517       ActivateStream(std::move(decoder_receive));
1518       QUIC_DVLOG(1) << ENDPOINT << "Receive QPACK Decoder stream is created";
1519       if (debug_visitor_ != nullptr) {
1520         debug_visitor_->OnPeerQpackDecoderStreamCreated(
1521             qpack_decoder_receive_stream_->id());
1522       }
1523       return qpack_decoder_receive_stream_;
1524     }
1525     case kWebTransportUnidirectionalStream: {
1526       // Note that this checks whether WebTransport is enabled on the receiver
1527       // side, as we may receive WebTransport streams before peer's SETTINGS are
1528       // received.
1529       // TODO(b/184156476): consider whether this means we should drop buffered
1530       // streams if we don't receive indication of WebTransport support.
1531       if (!WillNegotiateWebTransport()) {
1532         // Treat as unknown stream type.
1533         break;
1534       }
1535       QUIC_DVLOG(1) << ENDPOINT << "Created an incoming WebTransport stream "
1536                     << pending->id();
1537       auto stream_owned =
1538           std::make_unique<WebTransportHttp3UnidirectionalStream>(pending,
1539                                                                   this);
1540       WebTransportHttp3UnidirectionalStream* stream = stream_owned.get();
1541       ActivateStream(std::move(stream_owned));
1542       return stream;
1543     }
1544     default:
1545       break;
1546   }
1547   MaybeSendStopSendingFrame(
1548       pending->id(),
1549       QuicResetStreamError::FromInternal(QUIC_STREAM_STREAM_CREATION_ERROR));
1550   pending->StopReading();
1551   return nullptr;
1552 }
1553 
MaybeInitializeHttp3UnidirectionalStreams()1554 void QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams() {
1555   QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
1556   if (!send_control_stream_ && CanOpenNextOutgoingUnidirectionalStream()) {
1557     auto send_control = std::make_unique<QuicSendControlStream>(
1558         GetNextOutgoingUnidirectionalStreamId(), this, settings_);
1559     send_control_stream_ = send_control.get();
1560     ActivateStream(std::move(send_control));
1561     if (debug_visitor_) {
1562       debug_visitor_->OnControlStreamCreated(send_control_stream_->id());
1563     }
1564   }
1565 
1566   if (!qpack_decoder_send_stream_ &&
1567       CanOpenNextOutgoingUnidirectionalStream()) {
1568     auto decoder_send = std::make_unique<QpackSendStream>(
1569         GetNextOutgoingUnidirectionalStreamId(), this, kQpackDecoderStream);
1570     qpack_decoder_send_stream_ = decoder_send.get();
1571     ActivateStream(std::move(decoder_send));
1572     qpack_decoder_->set_qpack_stream_sender_delegate(
1573         qpack_decoder_send_stream_);
1574     if (debug_visitor_) {
1575       debug_visitor_->OnQpackDecoderStreamCreated(
1576           qpack_decoder_send_stream_->id());
1577     }
1578   }
1579 
1580   if (!qpack_encoder_send_stream_ &&
1581       CanOpenNextOutgoingUnidirectionalStream()) {
1582     auto encoder_send = std::make_unique<QpackSendStream>(
1583         GetNextOutgoingUnidirectionalStreamId(), this, kQpackEncoderStream);
1584     qpack_encoder_send_stream_ = encoder_send.get();
1585     ActivateStream(std::move(encoder_send));
1586     qpack_encoder_->set_qpack_stream_sender_delegate(
1587         qpack_encoder_send_stream_);
1588     if (debug_visitor_) {
1589       debug_visitor_->OnQpackEncoderStreamCreated(
1590           qpack_encoder_send_stream_->id());
1591     }
1592   }
1593 }
1594 
BeforeConnectionCloseSent()1595 void QuicSpdySession::BeforeConnectionCloseSent() {
1596   if (!VersionUsesHttp3(transport_version()) || !IsEncryptionEstablished()) {
1597     return;
1598   }
1599 
1600   QUICHE_DCHECK_EQ(perspective(), Perspective::IS_SERVER);
1601 
1602   QuicStreamId stream_id =
1603       GetLargestPeerCreatedStreamId(/*unidirectional = */ false);
1604 
1605   if (stream_id == QuicUtils::GetInvalidStreamId(transport_version())) {
1606     // No client-initiated bidirectional streams received yet.
1607     // Send 0 to let client know that all requests can be retried.
1608     stream_id = 0;
1609   } else {
1610     // Tell client that streams starting with the next after the largest
1611     // received one can be retried.
1612     stream_id += QuicUtils::StreamIdDelta(transport_version());
1613   }
1614   if (last_sent_http3_goaway_id_.has_value() &&
1615       *last_sent_http3_goaway_id_ <= stream_id) {
1616     // Do not send GOAWAY frame with a higher id, because it is forbidden.
1617     // Do not send one with same stream id as before, since frames on the
1618     // control stream are guaranteed to be processed in order.
1619     return;
1620   }
1621 
1622   send_control_stream_->SendGoAway(stream_id);
1623   last_sent_http3_goaway_id_ = stream_id;
1624 }
1625 
MaybeBundleOpportunistically()1626 void QuicSpdySession::MaybeBundleOpportunistically() {
1627   if (qpack_decoder_ != nullptr) {
1628     qpack_decoder_->FlushDecoderStream();
1629   }
1630 }
1631 
OnCanCreateNewOutgoingStream(bool unidirectional)1632 void QuicSpdySession::OnCanCreateNewOutgoingStream(bool unidirectional) {
1633   if (unidirectional && VersionUsesHttp3(transport_version())) {
1634     MaybeInitializeHttp3UnidirectionalStreams();
1635   }
1636 }
1637 
goaway_received() const1638 bool QuicSpdySession::goaway_received() const {
1639   return VersionUsesHttp3(transport_version())
1640              ? last_received_http3_goaway_id_.has_value()
1641              : transport_goaway_received();
1642 }
1643 
goaway_sent() const1644 bool QuicSpdySession::goaway_sent() const {
1645   return VersionUsesHttp3(transport_version())
1646              ? last_sent_http3_goaway_id_.has_value()
1647              : transport_goaway_sent();
1648 }
1649 
CloseConnectionOnDuplicateHttp3UnidirectionalStreams(absl::string_view type)1650 void QuicSpdySession::CloseConnectionOnDuplicateHttp3UnidirectionalStreams(
1651     absl::string_view type) {
1652   QUIC_PEER_BUG(quic_peer_bug_10360_9) << absl::StrCat(
1653       "Received a duplicate ", type, " stream: Closing connection.");
1654   CloseConnectionWithDetails(QUIC_HTTP_DUPLICATE_UNIDIRECTIONAL_STREAM,
1655                              absl::StrCat(type, " stream is received twice."));
1656 }
1657 
1658 // static
LogHeaderCompressionRatioHistogram(bool using_qpack,bool is_sent,QuicByteCount compressed,QuicByteCount uncompressed)1659 void QuicSpdySession::LogHeaderCompressionRatioHistogram(
1660     bool using_qpack, bool is_sent, QuicByteCount compressed,
1661     QuicByteCount uncompressed) {
1662   if (compressed <= 0 || uncompressed <= 0) {
1663     return;
1664   }
1665 
1666   int ratio = 100 * (compressed) / (uncompressed);
1667   if (ratio < 1) {
1668     ratio = 1;
1669   } else if (ratio > 200) {
1670     ratio = 200;
1671   }
1672 
1673   // Note that when using histogram macros in Chromium, the histogram name must
1674   // be the same across calls for any given call site.
1675   if (using_qpack) {
1676     if (is_sent) {
1677       QUIC_HISTOGRAM_COUNTS("QuicSession.HeaderCompressionRatioQpackSent",
1678                             ratio, 1, 200, 200,
1679                             "Header compression ratio as percentage for sent "
1680                             "headers using QPACK.");
1681     } else {
1682       QUIC_HISTOGRAM_COUNTS("QuicSession.HeaderCompressionRatioQpackReceived",
1683                             ratio, 1, 200, 200,
1684                             "Header compression ratio as percentage for "
1685                             "received headers using QPACK.");
1686     }
1687   } else {
1688     if (is_sent) {
1689       QUIC_HISTOGRAM_COUNTS("QuicSession.HeaderCompressionRatioHpackSent",
1690                             ratio, 1, 200, 200,
1691                             "Header compression ratio as percentage for sent "
1692                             "headers using HPACK.");
1693     } else {
1694       QUIC_HISTOGRAM_COUNTS("QuicSession.HeaderCompressionRatioHpackReceived",
1695                             ratio, 1, 200, 200,
1696                             "Header compression ratio as percentage for "
1697                             "received headers using HPACK.");
1698     }
1699   }
1700 }
1701 
SendHttp3Datagram(QuicStreamId stream_id,absl::string_view payload)1702 MessageStatus QuicSpdySession::SendHttp3Datagram(QuicStreamId stream_id,
1703                                                  absl::string_view payload) {
1704   if (!SupportsH3Datagram()) {
1705     QUIC_BUG(send http datagram too early)
1706         << "Refusing to send HTTP Datagram before SETTINGS received";
1707     return MESSAGE_STATUS_INTERNAL_ERROR;
1708   }
1709   // Stream ID is sent divided by four as per the specification.
1710   uint64_t stream_id_to_write = stream_id / kHttpDatagramStreamIdDivisor;
1711   size_t slice_length =
1712       QuicDataWriter::GetVarInt62Len(stream_id_to_write) + payload.length();
1713   quiche::QuicheBuffer buffer(
1714       connection()->helper()->GetStreamSendBufferAllocator(), slice_length);
1715   QuicDataWriter writer(slice_length, buffer.data());
1716   if (!writer.WriteVarInt62(stream_id_to_write)) {
1717     QUIC_BUG(h3 datagram stream ID write fail)
1718         << "Failed to write HTTP/3 datagram stream ID";
1719     return MESSAGE_STATUS_INTERNAL_ERROR;
1720   }
1721   if (!writer.WriteBytes(payload.data(), payload.length())) {
1722     QUIC_BUG(h3 datagram payload write fail)
1723         << "Failed to write HTTP/3 datagram payload";
1724     return MESSAGE_STATUS_INTERNAL_ERROR;
1725   }
1726 
1727   quiche::QuicheMemSlice slice(std::move(buffer));
1728   return datagram_queue()->SendOrQueueDatagram(std::move(slice));
1729 }
1730 
SetMaxDatagramTimeInQueueForStreamId(QuicStreamId,QuicTime::Delta max_time_in_queue)1731 void QuicSpdySession::SetMaxDatagramTimeInQueueForStreamId(
1732     QuicStreamId /*stream_id*/, QuicTime::Delta max_time_in_queue) {
1733   // TODO(b/184598230): implement this in a way that works for multiple sessions
1734   // on a same connection.
1735   datagram_queue()->SetMaxTimeInQueue(max_time_in_queue);
1736 }
1737 
OnMessageReceived(absl::string_view message)1738 void QuicSpdySession::OnMessageReceived(absl::string_view message) {
1739   QuicSession::OnMessageReceived(message);
1740   if (!SupportsH3Datagram()) {
1741     QUIC_DLOG(INFO) << "Ignoring unexpected received HTTP/3 datagram";
1742     return;
1743   }
1744   QuicDataReader reader(message);
1745   uint64_t stream_id64;
1746   if (!reader.ReadVarInt62(&stream_id64)) {
1747     QUIC_DLOG(ERROR) << "Failed to parse stream ID in received HTTP/3 datagram";
1748     return;
1749   }
1750   // Stream ID is sent divided by four as per the specification.
1751   if (stream_id64 >
1752       std::numeric_limits<QuicStreamId>::max() / kHttpDatagramStreamIdDivisor) {
1753     CloseConnectionWithDetails(
1754         QUIC_HTTP_FRAME_ERROR,
1755         absl::StrCat("Received HTTP Datagram with invalid quarter stream ID ",
1756                      stream_id64));
1757     return;
1758   }
1759   stream_id64 *= kHttpDatagramStreamIdDivisor;
1760   QuicStreamId stream_id = static_cast<QuicStreamId>(stream_id64);
1761   QuicSpdyStream* stream =
1762       static_cast<QuicSpdyStream*>(GetActiveStream(stream_id));
1763   if (stream == nullptr) {
1764     QUIC_DLOG(INFO) << "Received HTTP/3 datagram for unknown stream ID "
1765                     << stream_id;
1766     // TODO(b/181256914) buffer HTTP/3 datagrams with unknown stream IDs for a
1767     // short period of time in case they were reordered.
1768     return;
1769   }
1770   stream->OnDatagramReceived(&reader);
1771 }
1772 
SupportsWebTransport()1773 bool QuicSpdySession::SupportsWebTransport() {
1774   return WillNegotiateWebTransport() && SupportsH3Datagram() &&
1775          NegotiatedWebTransportVersion().has_value() && allow_extended_connect_;
1776 }
1777 
1778 std::optional<WebTransportHttp3Version>
SupportedWebTransportVersion()1779 QuicSpdySession::SupportedWebTransportVersion() {
1780   if (!SupportsWebTransport()) {
1781     return std::nullopt;
1782   }
1783   return NegotiatedWebTransportVersion();
1784 }
1785 
SupportsH3Datagram() const1786 bool QuicSpdySession::SupportsH3Datagram() const {
1787   return http_datagram_support_ != HttpDatagramSupport::kNone;
1788 }
1789 
GetWebTransportSession(WebTransportSessionId id)1790 WebTransportHttp3* QuicSpdySession::GetWebTransportSession(
1791     WebTransportSessionId id) {
1792   if (!SupportsWebTransport()) {
1793     return nullptr;
1794   }
1795   if (!IsValidWebTransportSessionId(id, version())) {
1796     return nullptr;
1797   }
1798   QuicSpdyStream* connect_stream = GetOrCreateSpdyDataStream(id);
1799   if (connect_stream == nullptr) {
1800     return nullptr;
1801   }
1802   return connect_stream->web_transport();
1803 }
1804 
ShouldProcessIncomingRequests()1805 bool QuicSpdySession::ShouldProcessIncomingRequests() {
1806   if (!ShouldBufferRequestsUntilSettings()) {
1807     return true;
1808   }
1809 
1810   QUICHE_RELOADABLE_FLAG_COUNT_N(quic_block_until_settings_received_copt, 2, 4);
1811   return settings_received_;
1812 }
1813 
OnStreamWaitingForClientSettings(QuicStreamId id)1814 void QuicSpdySession::OnStreamWaitingForClientSettings(QuicStreamId id) {
1815   QUICHE_DCHECK(ShouldBufferRequestsUntilSettings());
1816   QUICHE_DCHECK(QuicUtils::IsBidirectionalStreamId(id, version()));
1817   QUICHE_RELOADABLE_FLAG_COUNT_N(quic_block_until_settings_received_copt, 3, 4);
1818   streams_waiting_for_settings_.insert(id);
1819 }
1820 
AssociateIncomingWebTransportStreamWithSession(WebTransportSessionId session_id,QuicStreamId stream_id)1821 void QuicSpdySession::AssociateIncomingWebTransportStreamWithSession(
1822     WebTransportSessionId session_id, QuicStreamId stream_id) {
1823   if (QuicUtils::IsOutgoingStreamId(version(), stream_id, perspective())) {
1824     QUIC_BUG(AssociateIncomingWebTransportStreamWithSession got outgoing stream)
1825         << ENDPOINT
1826         << "AssociateIncomingWebTransportStreamWithSession() got an outgoing "
1827            "stream ID: "
1828         << stream_id;
1829     return;
1830   }
1831   WebTransportHttp3* session = GetWebTransportSession(session_id);
1832   if (session != nullptr) {
1833     QUIC_DVLOG(1) << ENDPOINT
1834                   << "Successfully associated incoming WebTransport stream "
1835                   << stream_id << " with session ID " << session_id;
1836 
1837     session->AssociateStream(stream_id);
1838     return;
1839   }
1840   // Evict the oldest streams until we are under the limit.
1841   while (buffered_streams_.size() >= kMaxUnassociatedWebTransportStreams) {
1842     QUIC_DVLOG(1) << ENDPOINT << "Removing stream "
1843                   << buffered_streams_.front().stream_id
1844                   << " from buffered streams as the queue is full.";
1845     ResetStream(buffered_streams_.front().stream_id,
1846                 QUIC_STREAM_WEBTRANSPORT_BUFFERED_STREAMS_LIMIT_EXCEEDED);
1847     buffered_streams_.pop_front();
1848   }
1849   QUIC_DVLOG(1) << ENDPOINT << "Received a WebTransport stream " << stream_id
1850                 << " for session ID " << session_id
1851                 << " but cannot associate it; buffering instead.";
1852   buffered_streams_.push_back(
1853       BufferedWebTransportStream{session_id, stream_id});
1854 }
1855 
ProcessBufferedWebTransportStreamsForSession(WebTransportHttp3 * session)1856 void QuicSpdySession::ProcessBufferedWebTransportStreamsForSession(
1857     WebTransportHttp3* session) {
1858   const WebTransportSessionId session_id = session->id();
1859   QUIC_DVLOG(1) << "Processing buffered WebTransport streams for "
1860                 << session_id;
1861   auto it = buffered_streams_.begin();
1862   while (it != buffered_streams_.end()) {
1863     if (it->session_id == session_id) {
1864       QUIC_DVLOG(1) << "Unbuffered and associated WebTransport stream "
1865                     << it->stream_id << " with session " << it->session_id;
1866       session->AssociateStream(it->stream_id);
1867       it = buffered_streams_.erase(it);
1868     } else {
1869       it++;
1870     }
1871   }
1872 }
1873 
1874 WebTransportHttp3UnidirectionalStream*
CreateOutgoingUnidirectionalWebTransportStream(WebTransportHttp3 * session)1875 QuicSpdySession::CreateOutgoingUnidirectionalWebTransportStream(
1876     WebTransportHttp3* session) {
1877   if (!CanOpenNextOutgoingUnidirectionalStream()) {
1878     return nullptr;
1879   }
1880 
1881   QuicStreamId stream_id = GetNextOutgoingUnidirectionalStreamId();
1882   auto stream_owned = std::make_unique<WebTransportHttp3UnidirectionalStream>(
1883       stream_id, this, session->id());
1884   WebTransportHttp3UnidirectionalStream* stream = stream_owned.get();
1885   ActivateStream(std::move(stream_owned));
1886   stream->WritePreamble();
1887   session->AssociateStream(stream_id);
1888   return stream;
1889 }
1890 
CreateOutgoingBidirectionalWebTransportStream(WebTransportHttp3 * session)1891 QuicSpdyStream* QuicSpdySession::CreateOutgoingBidirectionalWebTransportStream(
1892     WebTransportHttp3* session) {
1893   QuicSpdyStream* stream = CreateOutgoingBidirectionalStream();
1894   if (stream == nullptr) {
1895     return nullptr;
1896   }
1897   QuicStreamId stream_id = stream->id();
1898   stream->ConvertToWebTransportDataStream(session->id());
1899   if (stream->web_transport_stream() == nullptr) {
1900     // An error in ConvertToWebTransportDataStream() would result in
1901     // CONNECTION_CLOSE, thus we don't need to do anything here.
1902     return nullptr;
1903   }
1904   session->AssociateStream(stream_id);
1905   return stream;
1906 }
1907 
OnDatagramProcessed(std::optional<MessageStatus>)1908 void QuicSpdySession::OnDatagramProcessed(
1909     std::optional<MessageStatus> /*status*/) {
1910   // TODO(b/184598230): make this work with multiple datagram flows.
1911 }
1912 
OnDatagramProcessed(std::optional<MessageStatus> status)1913 void QuicSpdySession::DatagramObserver::OnDatagramProcessed(
1914     std::optional<MessageStatus> status) {
1915   session_->OnDatagramProcessed(status);
1916 }
1917 
LocalHttpDatagramSupport()1918 HttpDatagramSupport QuicSpdySession::LocalHttpDatagramSupport() {
1919   return HttpDatagramSupport::kRfc;
1920 }
1921 
HttpDatagramSupportToString(HttpDatagramSupport http_datagram_support)1922 std::string HttpDatagramSupportToString(
1923     HttpDatagramSupport http_datagram_support) {
1924   switch (http_datagram_support) {
1925     case HttpDatagramSupport::kNone:
1926       return "None";
1927     case HttpDatagramSupport::kDraft04:
1928       return "Draft04";
1929     case HttpDatagramSupport::kRfc:
1930       return "Rfc";
1931     case HttpDatagramSupport::kRfcAndDraft04:
1932       return "RfcAndDraft04";
1933   }
1934   return absl::StrCat("Unknown(", static_cast<int>(http_datagram_support), ")");
1935 }
1936 
operator <<(std::ostream & os,const HttpDatagramSupport & http_datagram_support)1937 std::ostream& operator<<(std::ostream& os,
1938                          const HttpDatagramSupport& http_datagram_support) {
1939   os << HttpDatagramSupportToString(http_datagram_support);
1940   return os;
1941 }
1942 
1943 // Must not be called after Initialize().
set_allow_extended_connect(bool allow_extended_connect)1944 void QuicSpdySession::set_allow_extended_connect(bool allow_extended_connect) {
1945   QUIC_BUG_IF(extended connect wrong version,
1946               !VersionUsesHttp3(transport_version()))
1947       << "Try to enable/disable extended CONNECT in Google QUIC";
1948   QUIC_BUG_IF(extended connect on client,
1949               perspective() == Perspective::IS_CLIENT)
1950       << "Enabling/disabling extended CONNECT on the client side has no effect";
1951   if (ShouldNegotiateWebTransport()) {
1952     QUIC_BUG_IF(disable extended connect, !allow_extended_connect)
1953         << "Disabling extended CONNECT with web transport enabled has no "
1954            "effect.";
1955     return;
1956   }
1957   allow_extended_connect_ = allow_extended_connect;
1958 }
1959 
OnConfigNegotiated()1960 void QuicSpdySession::OnConfigNegotiated() {
1961   QuicSession::OnConfigNegotiated();
1962 
1963   if (GetQuicReloadableFlag(quic_block_until_settings_received_copt) &&
1964       perspective() == Perspective::IS_SERVER &&
1965       config()->HasClientSentConnectionOption(kBSUS, Perspective::IS_SERVER)) {
1966     QUICHE_RELOADABLE_FLAG_COUNT_N(quic_block_until_settings_received_copt, 1,
1967                                    4);
1968     force_buffer_requests_until_settings_ = true;
1969   }
1970 }
1971 
1972 #undef ENDPOINT  // undef for jumbo builds
1973 
1974 }  // namespace quic
1975