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