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