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