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