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 "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
6
7 #include <algorithm>
8 #include <cstdint>
9 #include <limits>
10 #include <string>
11 #include <utility>
12
13 #include "absl/base/attributes.h"
14 #include "absl/strings/numbers.h"
15 #include "absl/strings/string_view.h"
16 #include "net/third_party/quiche/src/quic/core/http/http_constants.h"
17 #include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h"
18 #include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
19 #include "net/third_party/quiche/src/quic/core/quic_types.h"
20 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
21 #include "net/third_party/quiche/src/quic/core/quic_versions.h"
22 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
23 #include "net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h"
24 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
25 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
26 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
27 #include "net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h"
28 #include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h"
29 #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
30 #include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h"
31
32 using http2::Http2DecoderAdapter;
33 using spdy::HpackEntry;
34 using spdy::HpackHeaderTable;
35 using spdy::Http2WeightToSpdy3Priority;
36 using spdy::Spdy3PriorityToHttp2Weight;
37 using spdy::SpdyErrorCode;
38 using spdy::SpdyFramer;
39 using spdy::SpdyFramerDebugVisitorInterface;
40 using spdy::SpdyFramerVisitorInterface;
41 using spdy::SpdyFrameType;
42 using spdy::SpdyHeaderBlock;
43 using spdy::SpdyHeadersHandlerInterface;
44 using spdy::SpdyHeadersIR;
45 using spdy::SpdyKnownSettingsId;
46 using spdy::SpdyPingId;
47 using spdy::SpdyPriority;
48 using spdy::SpdyPriorityIR;
49 using spdy::SpdyPushPromiseIR;
50 using spdy::SpdySerializedFrame;
51 using spdy::SpdySettingsId;
52 using spdy::SpdyStreamId;
53
54 namespace quic {
55
56 namespace {
57
58 #define ENDPOINT \
59 (perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ")
60
61 class HeaderTableDebugVisitor : public HpackHeaderTable::DebugVisitorInterface {
62 public:
HeaderTableDebugVisitor(const QuicClock * clock,std::unique_ptr<QuicHpackDebugVisitor> visitor)63 HeaderTableDebugVisitor(const QuicClock* clock,
64 std::unique_ptr<QuicHpackDebugVisitor> visitor)
65 : clock_(clock), headers_stream_hpack_visitor_(std::move(visitor)) {}
66 HeaderTableDebugVisitor(const HeaderTableDebugVisitor&) = delete;
67 HeaderTableDebugVisitor& operator=(const HeaderTableDebugVisitor&) = delete;
68
OnNewEntry(const HpackEntry & entry)69 int64_t OnNewEntry(const HpackEntry& entry) override {
70 QUIC_DVLOG(1) << entry.GetDebugString();
71 return (clock_->ApproximateNow() - QuicTime::Zero()).ToMicroseconds();
72 }
73
OnUseEntry(const HpackEntry & entry)74 void OnUseEntry(const HpackEntry& entry) override {
75 const QuicTime::Delta elapsed(
76 clock_->ApproximateNow() -
77 QuicTime::Delta::FromMicroseconds(entry.time_added()) -
78 QuicTime::Zero());
79 QUIC_DVLOG(1) << entry.GetDebugString() << " " << elapsed.ToMilliseconds()
80 << " ms";
81 headers_stream_hpack_visitor_->OnUseEntry(elapsed);
82 }
83
84 private:
85 const QuicClock* clock_;
86 std::unique_ptr<QuicHpackDebugVisitor> headers_stream_hpack_visitor_;
87 };
88
89 } // namespace
90
91 // A SpdyFramerVisitor that passes HEADERS frames to the QuicSpdyStream, and
92 // closes the connection if any unexpected frames are received.
93 class QuicSpdySession::SpdyFramerVisitor
94 : public SpdyFramerVisitorInterface,
95 public SpdyFramerDebugVisitorInterface {
96 public:
SpdyFramerVisitor(QuicSpdySession * session)97 explicit SpdyFramerVisitor(QuicSpdySession* session) : session_(session) {}
98 SpdyFramerVisitor(const SpdyFramerVisitor&) = delete;
99 SpdyFramerVisitor& operator=(const SpdyFramerVisitor&) = delete;
100
OnHeaderFrameStart(SpdyStreamId)101 SpdyHeadersHandlerInterface* OnHeaderFrameStart(
102 SpdyStreamId /* stream_id */) override {
103 DCHECK(!VersionUsesHttp3(session_->transport_version()));
104 return &header_list_;
105 }
106
OnHeaderFrameEnd(SpdyStreamId)107 void OnHeaderFrameEnd(SpdyStreamId /* stream_id */) override {
108 DCHECK(!VersionUsesHttp3(session_->transport_version()));
109
110 LogHeaderCompressionRatioHistogram(
111 /* using_qpack = */ false,
112 /* is_sent = */ false, header_list_.compressed_header_bytes(),
113 header_list_.uncompressed_header_bytes());
114
115 if (session_->IsConnected()) {
116 session_->OnHeaderList(header_list_);
117 }
118 header_list_.Clear();
119 }
120
OnStreamFrameData(SpdyStreamId,const char *,size_t)121 void OnStreamFrameData(SpdyStreamId /*stream_id*/,
122 const char* /*data*/,
123 size_t /*len*/) override {
124 DCHECK(!VersionUsesHttp3(session_->transport_version()));
125 CloseConnection("SPDY DATA frame received.",
126 QUIC_INVALID_HEADERS_STREAM_DATA);
127 }
128
OnStreamEnd(SpdyStreamId)129 void OnStreamEnd(SpdyStreamId /*stream_id*/) override {
130 // The framer invokes OnStreamEnd after processing a frame that had the fin
131 // bit set.
132 }
133
OnStreamPadding(SpdyStreamId,size_t)134 void OnStreamPadding(SpdyStreamId /*stream_id*/, size_t /*len*/) override {
135 CloseConnection("SPDY frame padding received.",
136 QUIC_INVALID_HEADERS_STREAM_DATA);
137 }
138
OnError(Http2DecoderAdapter::SpdyFramerError error,std::string detailed_error)139 void OnError(Http2DecoderAdapter::SpdyFramerError error,
140 std::string detailed_error) override {
141 QuicErrorCode code;
142 switch (error) {
143 case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_INDEX_VARINT_ERROR:
144 code = QUIC_HPACK_INDEX_VARINT_ERROR;
145 break;
146 case Http2DecoderAdapter::SpdyFramerError::
147 SPDY_HPACK_NAME_LENGTH_VARINT_ERROR:
148 code = QUIC_HPACK_NAME_LENGTH_VARINT_ERROR;
149 break;
150 case Http2DecoderAdapter::SpdyFramerError::
151 SPDY_HPACK_VALUE_LENGTH_VARINT_ERROR:
152 code = QUIC_HPACK_VALUE_LENGTH_VARINT_ERROR;
153 break;
154 case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_NAME_TOO_LONG:
155 code = QUIC_HPACK_NAME_TOO_LONG;
156 break;
157 case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_VALUE_TOO_LONG:
158 code = QUIC_HPACK_VALUE_TOO_LONG;
159 break;
160 case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_NAME_HUFFMAN_ERROR:
161 code = QUIC_HPACK_NAME_HUFFMAN_ERROR;
162 break;
163 case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_VALUE_HUFFMAN_ERROR:
164 code = QUIC_HPACK_VALUE_HUFFMAN_ERROR;
165 break;
166 case Http2DecoderAdapter::SpdyFramerError::
167 SPDY_HPACK_MISSING_DYNAMIC_TABLE_SIZE_UPDATE:
168 code = QUIC_HPACK_MISSING_DYNAMIC_TABLE_SIZE_UPDATE;
169 break;
170 case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_INVALID_INDEX:
171 code = QUIC_HPACK_INVALID_INDEX;
172 break;
173 case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_INVALID_NAME_INDEX:
174 code = QUIC_HPACK_INVALID_NAME_INDEX;
175 break;
176 case Http2DecoderAdapter::SpdyFramerError::
177 SPDY_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_NOT_ALLOWED:
178 code = QUIC_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_NOT_ALLOWED;
179 break;
180 case Http2DecoderAdapter::SpdyFramerError::
181 SPDY_HPACK_INITIAL_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_LOW_WATER_MARK:
182 code = QUIC_HPACK_INITIAL_TABLE_SIZE_UPDATE_IS_ABOVE_LOW_WATER_MARK;
183 break;
184 case Http2DecoderAdapter::SpdyFramerError::
185 SPDY_HPACK_DYNAMIC_TABLE_SIZE_UPDATE_IS_ABOVE_ACKNOWLEDGED_SETTING:
186 code = QUIC_HPACK_TABLE_SIZE_UPDATE_IS_ABOVE_ACKNOWLEDGED_SETTING;
187 break;
188 case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_TRUNCATED_BLOCK:
189 code = QUIC_HPACK_TRUNCATED_BLOCK;
190 break;
191 case Http2DecoderAdapter::SpdyFramerError::SPDY_HPACK_FRAGMENT_TOO_LONG:
192 code = QUIC_HPACK_FRAGMENT_TOO_LONG;
193 break;
194 case Http2DecoderAdapter::SpdyFramerError::
195 SPDY_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT:
196 code = QUIC_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT;
197 break;
198 case Http2DecoderAdapter::SpdyFramerError::SPDY_DECOMPRESS_FAILURE:
199 code = QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE;
200 break;
201 default:
202 code = QUIC_INVALID_HEADERS_STREAM_DATA;
203 }
204 CloseConnection(quiche::QuicheStrCat(
205 "SPDY framing error: ", detailed_error,
206 Http2DecoderAdapter::SpdyFramerErrorToString(error)),
207 code);
208 }
209
OnDataFrameHeader(SpdyStreamId,size_t,bool)210 void OnDataFrameHeader(SpdyStreamId /*stream_id*/,
211 size_t /*length*/,
212 bool /*fin*/) override {
213 DCHECK(!VersionUsesHttp3(session_->transport_version()));
214 CloseConnection("SPDY DATA frame received.",
215 QUIC_INVALID_HEADERS_STREAM_DATA);
216 }
217
OnRstStream(SpdyStreamId,SpdyErrorCode)218 void OnRstStream(SpdyStreamId /*stream_id*/,
219 SpdyErrorCode /*error_code*/) override {
220 CloseConnection("SPDY RST_STREAM frame received.",
221 QUIC_INVALID_HEADERS_STREAM_DATA);
222 }
223
OnSetting(SpdySettingsId id,uint32_t value)224 void OnSetting(SpdySettingsId id, uint32_t value) override {
225 DCHECK(!VersionUsesHttp3(session_->transport_version()));
226 session_->OnSetting(id, value);
227 }
228
OnSettingsEnd()229 void OnSettingsEnd() override {
230 DCHECK(!VersionUsesHttp3(session_->transport_version()));
231 }
232
OnPing(SpdyPingId,bool)233 void OnPing(SpdyPingId /*unique_id*/, bool /*is_ack*/) override {
234 CloseConnection("SPDY PING frame received.",
235 QUIC_INVALID_HEADERS_STREAM_DATA);
236 }
237
OnGoAway(SpdyStreamId,SpdyErrorCode)238 void OnGoAway(SpdyStreamId /*last_accepted_stream_id*/,
239 SpdyErrorCode /*error_code*/) override {
240 CloseConnection("SPDY GOAWAY frame received.",
241 QUIC_INVALID_HEADERS_STREAM_DATA);
242 }
243
OnHeaders(SpdyStreamId stream_id,bool has_priority,int weight,SpdyStreamId parent_stream_id,bool exclusive,bool fin,bool)244 void OnHeaders(SpdyStreamId stream_id,
245 bool has_priority,
246 int weight,
247 SpdyStreamId parent_stream_id,
248 bool exclusive,
249 bool fin,
250 bool /*end*/) override {
251 if (!session_->IsConnected()) {
252 return;
253 }
254
255 if (VersionUsesHttp3(session_->transport_version())) {
256 CloseConnection("HEADERS frame not allowed on headers stream.",
257 QUIC_INVALID_HEADERS_STREAM_DATA);
258 return;
259 }
260
261 QUIC_BUG_IF(session_->destruction_indicator() != 123456789)
262 << "QuicSpdyStream use after free. "
263 << session_->destruction_indicator() << QuicStackTrace();
264
265 if (session_->use_http2_priority_write_scheduler()) {
266 session_->OnHeaders(
267 stream_id, has_priority,
268 spdy::SpdyStreamPrecedence(parent_stream_id, weight, exclusive), fin);
269 return;
270 }
271
272 SpdyPriority priority =
273 has_priority ? Http2WeightToSpdy3Priority(weight) : 0;
274 session_->OnHeaders(stream_id, has_priority,
275 spdy::SpdyStreamPrecedence(priority), fin);
276 }
277
OnWindowUpdate(SpdyStreamId,int)278 void OnWindowUpdate(SpdyStreamId /*stream_id*/,
279 int /*delta_window_size*/) override {
280 CloseConnection("SPDY WINDOW_UPDATE frame received.",
281 QUIC_INVALID_HEADERS_STREAM_DATA);
282 }
283
OnPushPromise(SpdyStreamId stream_id,SpdyStreamId promised_stream_id,bool)284 void OnPushPromise(SpdyStreamId stream_id,
285 SpdyStreamId promised_stream_id,
286 bool /*end*/) override {
287 DCHECK(!VersionUsesHttp3(session_->transport_version()));
288 if (session_->perspective() != Perspective::IS_CLIENT) {
289 CloseConnection("PUSH_PROMISE not supported.",
290 QUIC_INVALID_HEADERS_STREAM_DATA);
291 return;
292 }
293 if (!session_->IsConnected()) {
294 return;
295 }
296 session_->OnPushPromise(stream_id, promised_stream_id);
297 }
298
OnContinuation(SpdyStreamId,bool)299 void OnContinuation(SpdyStreamId /*stream_id*/, bool /*end*/) override {}
300
OnPriority(SpdyStreamId stream_id,SpdyStreamId parent_id,int weight,bool exclusive)301 void OnPriority(SpdyStreamId stream_id,
302 SpdyStreamId parent_id,
303 int weight,
304 bool exclusive) override {
305 DCHECK(!VersionUsesHttp3(session_->transport_version()));
306 if (!session_->IsConnected()) {
307 return;
308 }
309 if (session_->use_http2_priority_write_scheduler()) {
310 session_->OnPriority(
311 stream_id, spdy::SpdyStreamPrecedence(parent_id, weight, exclusive));
312 return;
313 }
314 SpdyPriority priority = Http2WeightToSpdy3Priority(weight);
315 session_->OnPriority(stream_id, spdy::SpdyStreamPrecedence(priority));
316 }
317
OnUnknownFrame(SpdyStreamId,uint8_t)318 bool OnUnknownFrame(SpdyStreamId /*stream_id*/,
319 uint8_t /*frame_type*/) override {
320 CloseConnection("Unknown frame type received.",
321 QUIC_INVALID_HEADERS_STREAM_DATA);
322 return false;
323 }
324
325 // SpdyFramerDebugVisitorInterface implementation
OnSendCompressedFrame(SpdyStreamId,SpdyFrameType,size_t payload_len,size_t frame_len)326 void OnSendCompressedFrame(SpdyStreamId /*stream_id*/,
327 SpdyFrameType /*type*/,
328 size_t payload_len,
329 size_t frame_len) override {
330 if (payload_len == 0) {
331 QUIC_BUG << "Zero payload length.";
332 return;
333 }
334 int compression_pct = 100 - (100 * frame_len) / payload_len;
335 QUIC_DVLOG(1) << "Net.QuicHpackCompressionPercentage: " << compression_pct;
336 }
337
OnReceiveCompressedFrame(SpdyStreamId,SpdyFrameType,size_t frame_len)338 void OnReceiveCompressedFrame(SpdyStreamId /*stream_id*/,
339 SpdyFrameType /*type*/,
340 size_t frame_len) override {
341 if (session_->IsConnected()) {
342 session_->OnCompressedFrameSize(frame_len);
343 }
344 }
345
set_max_header_list_size(size_t max_header_list_size)346 void set_max_header_list_size(size_t max_header_list_size) {
347 header_list_.set_max_header_list_size(max_header_list_size);
348 }
349
350 private:
CloseConnection(const std::string & details,QuicErrorCode code)351 void CloseConnection(const std::string& details, QuicErrorCode code) {
352 if (session_->IsConnected()) {
353 session_->CloseConnectionWithDetails(code, details);
354 }
355 }
356
357 QuicSpdySession* session_;
358 QuicHeaderList header_list_;
359 };
360
QuicHpackDebugVisitor()361 QuicHpackDebugVisitor::QuicHpackDebugVisitor() {}
362
~QuicHpackDebugVisitor()363 QuicHpackDebugVisitor::~QuicHpackDebugVisitor() {}
364
Http3DebugVisitor()365 Http3DebugVisitor::Http3DebugVisitor() {}
366
~Http3DebugVisitor()367 Http3DebugVisitor::~Http3DebugVisitor() {}
368
369 // Expected unidirectional static streams Requirement can be found at
370 // 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)371 QuicSpdySession::QuicSpdySession(
372 QuicConnection* connection,
373 QuicSession::Visitor* visitor,
374 const QuicConfig& config,
375 const ParsedQuicVersionVector& supported_versions)
376 : QuicSession(connection,
377 visitor,
378 config,
379 supported_versions,
380 /*num_expected_unidirectional_static_streams = */
381 VersionUsesHttp3(connection->transport_version())
382 ? kHttp3StaticUnidirectionalStreamCount
383 : 0),
384 send_control_stream_(nullptr),
385 receive_control_stream_(nullptr),
386 qpack_encoder_receive_stream_(nullptr),
387 qpack_decoder_receive_stream_(nullptr),
388 qpack_encoder_send_stream_(nullptr),
389 qpack_decoder_send_stream_(nullptr),
390 qpack_maximum_dynamic_table_capacity_(
391 kDefaultQpackMaxDynamicTableCapacity),
392 qpack_maximum_blocked_streams_(kDefaultMaximumBlockedStreams),
393 max_inbound_header_list_size_(kDefaultMaxUncompressedHeaderSize),
394 max_outbound_header_list_size_(std::numeric_limits<size_t>::max()),
395 stream_id_(
396 QuicUtils::GetInvalidStreamId(connection->transport_version())),
397 promised_stream_id_(
398 QuicUtils::GetInvalidStreamId(connection->transport_version())),
399 frame_len_(0),
400 fin_(false),
401 spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
402 spdy_framer_visitor_(new SpdyFramerVisitor(this)),
403 debug_visitor_(nullptr),
404 destruction_indicator_(123456789),
405 server_push_enabled_(true),
406 ietf_server_push_enabled_(
407 GetQuicFlag(FLAGS_quic_enable_http3_server_push)),
408 http3_max_push_id_sent_(false),
409 reject_spdy_settings_(GetQuicReloadableFlag(quic_reject_spdy_settings)),
410 goaway_with_max_stream_id_(
411 GetQuicReloadableFlag(quic_goaway_with_max_stream_id)) {
412 if (goaway_with_max_stream_id_) {
413 QUIC_RELOADABLE_FLAG_COUNT_N(quic_goaway_with_max_stream_id, 1, 2);
414 }
415 h2_deframer_.set_visitor(spdy_framer_visitor_.get());
416 h2_deframer_.set_debug_visitor(spdy_framer_visitor_.get());
417 spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
418 }
419
~QuicSpdySession()420 QuicSpdySession::~QuicSpdySession() {
421 QUIC_BUG_IF(destruction_indicator_ != 123456789)
422 << "QuicSpdyStream use after free. " << destruction_indicator_
423 << QuicStackTrace();
424 destruction_indicator_ = 987654321;
425
426 if (GetQuicReloadableFlag(quic_clean_up_spdy_session_destructor)) {
427 QUIC_RELOADABLE_FLAG_COUNT(quic_clean_up_spdy_session_destructor);
428 return;
429 }
430 // Set the streams' session pointers in closed and dynamic stream lists
431 // to null to avoid subsequent use of this session.
432 for (auto& stream : *closed_streams()) {
433 static_cast<QuicSpdyStream*>(stream.get())->ClearSession();
434 }
435 for (auto const& kv : stream_map()) {
436 if (!kv.second->is_static()) {
437 static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession();
438 }
439 }
440 }
441
Initialize()442 void QuicSpdySession::Initialize() {
443 QuicSession::Initialize();
444
445 FillSettingsFrame();
446 if (!VersionUsesHttp3(transport_version())) {
447 if (perspective() == Perspective::IS_SERVER) {
448 set_largest_peer_created_stream_id(
449 QuicUtils::GetHeadersStreamId(transport_version()));
450 } else {
451 QuicStreamId headers_stream_id = GetNextOutgoingBidirectionalStreamId();
452 DCHECK_EQ(headers_stream_id,
453 QuicUtils::GetHeadersStreamId(transport_version()));
454 }
455 auto headers_stream = std::make_unique<QuicHeadersStream>((this));
456 DCHECK_EQ(QuicUtils::GetHeadersStreamId(transport_version()),
457 headers_stream->id());
458
459 headers_stream_ = headers_stream.get();
460 ActivateStream(std::move(headers_stream));
461 } else {
462 qpack_encoder_ = std::make_unique<QpackEncoder>(this);
463 qpack_decoder_ =
464 std::make_unique<QpackDecoder>(qpack_maximum_dynamic_table_capacity_,
465 qpack_maximum_blocked_streams_, this);
466 MaybeInitializeHttp3UnidirectionalStreams();
467 }
468
469 spdy_framer_visitor_->set_max_header_list_size(max_inbound_header_list_size_);
470
471 // Limit HPACK buffering to 2x header list size limit.
472 h2_deframer_.GetHpackDecoder()->set_max_decode_buffer_size_bytes(
473 2 * max_inbound_header_list_size_);
474 }
475
FillSettingsFrame()476 void QuicSpdySession::FillSettingsFrame() {
477 settings_.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] =
478 qpack_maximum_dynamic_table_capacity_;
479 settings_.values[SETTINGS_QPACK_BLOCKED_STREAMS] =
480 qpack_maximum_blocked_streams_;
481 settings_.values[SETTINGS_MAX_FIELD_SECTION_SIZE] =
482 max_inbound_header_list_size_;
483 }
484
OnDecoderStreamError(QuicErrorCode error_code,absl::string_view error_message)485 void QuicSpdySession::OnDecoderStreamError(QuicErrorCode error_code,
486 absl::string_view error_message) {
487 DCHECK(VersionUsesHttp3(transport_version()));
488
489 CloseConnectionWithDetails(
490 error_code,
491 quiche::QuicheStrCat("Decoder stream error: ", error_message));
492 }
493
OnEncoderStreamError(QuicErrorCode error_code,absl::string_view error_message)494 void QuicSpdySession::OnEncoderStreamError(QuicErrorCode error_code,
495 absl::string_view error_message) {
496 DCHECK(VersionUsesHttp3(transport_version()));
497
498 CloseConnectionWithDetails(
499 error_code,
500 quiche::QuicheStrCat("Encoder stream error: ", error_message));
501 }
502
OnStreamHeadersPriority(QuicStreamId stream_id,const spdy::SpdyStreamPrecedence & precedence)503 void QuicSpdySession::OnStreamHeadersPriority(
504 QuicStreamId stream_id,
505 const spdy::SpdyStreamPrecedence& precedence) {
506 QuicSpdyStream* stream = GetOrCreateSpdyDataStream(stream_id);
507 if (!stream) {
508 // It's quite possible to receive headers after a stream has been reset.
509 return;
510 }
511 stream->OnStreamHeadersPriority(precedence);
512 }
513
OnStreamHeaderList(QuicStreamId stream_id,bool fin,size_t frame_len,const QuicHeaderList & header_list)514 void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id,
515 bool fin,
516 size_t frame_len,
517 const QuicHeaderList& header_list) {
518 if (IsStaticStream(stream_id)) {
519 connection()->CloseConnection(
520 QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
521 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
522 return;
523 }
524 QuicSpdyStream* stream = GetOrCreateSpdyDataStream(stream_id);
525 if (stream == nullptr) {
526 // The stream no longer exists, but trailing headers may contain the final
527 // byte offset necessary for flow control and open stream accounting.
528 size_t final_byte_offset = 0;
529 for (const auto& header : header_list) {
530 const std::string& header_key = header.first;
531 const std::string& header_value = header.second;
532 if (header_key == kFinalOffsetHeaderKey) {
533 if (!absl::SimpleAtoi(header_value, &final_byte_offset)) {
534 connection()->CloseConnection(
535 QUIC_INVALID_HEADERS_STREAM_DATA,
536 "Trailers are malformed (no final offset)",
537 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
538 return;
539 }
540 QUIC_DVLOG(1) << ENDPOINT
541 << "Received final byte offset in trailers for stream "
542 << stream_id << ", which no longer exists.";
543 OnFinalByteOffsetReceived(stream_id, final_byte_offset);
544 }
545 }
546
547 // It's quite possible to receive headers after a stream has been reset.
548 return;
549 }
550 stream->OnStreamHeaderList(fin, frame_len, header_list);
551 }
552
OnPriorityFrame(QuicStreamId stream_id,const spdy::SpdyStreamPrecedence & precedence)553 void QuicSpdySession::OnPriorityFrame(
554 QuicStreamId stream_id,
555 const spdy::SpdyStreamPrecedence& precedence) {
556 QuicSpdyStream* stream = GetOrCreateSpdyDataStream(stream_id);
557 if (!stream) {
558 // It's quite possible to receive a PRIORITY frame after a stream has been
559 // reset.
560 return;
561 }
562 stream->OnPriorityFrame(precedence);
563 }
564
OnPriorityUpdateForRequestStream(QuicStreamId stream_id,int urgency)565 bool QuicSpdySession::OnPriorityUpdateForRequestStream(QuicStreamId stream_id,
566 int urgency) {
567 if (perspective() == Perspective::IS_CLIENT ||
568 !QuicUtils::IsBidirectionalStreamId(stream_id, version()) ||
569 !QuicUtils::IsClientInitiatedStreamId(transport_version(), stream_id)) {
570 return true;
571 }
572
573 QuicStreamCount advertised_max_incoming_bidirectional_streams =
574 GetAdvertisedMaxIncomingBidirectionalStreams();
575 if (advertised_max_incoming_bidirectional_streams == 0 ||
576 stream_id > QuicUtils::GetFirstBidirectionalStreamId(
577 transport_version(), Perspective::IS_CLIENT) +
578 QuicUtils::StreamIdDelta(transport_version()) *
579 (advertised_max_incoming_bidirectional_streams - 1)) {
580 connection()->CloseConnection(
581 QUIC_INVALID_STREAM_ID,
582 "PRIORITY_UPDATE frame received for invalid stream.",
583 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
584 return false;
585 }
586
587 if (MaybeSetStreamPriority(stream_id, spdy::SpdyStreamPrecedence(urgency))) {
588 return true;
589 }
590
591 if (IsClosedStream(stream_id)) {
592 return true;
593 }
594
595 buffered_stream_priorities_[stream_id] = urgency;
596
597 if (buffered_stream_priorities_.size() >
598 10 * max_open_incoming_bidirectional_streams()) {
599 // This should never happen, because |buffered_stream_priorities_| should
600 // only contain entries for streams that are allowed to be open by the peer
601 // but have not been opened yet.
602 std::string error_message = quiche::QuicheStrCat(
603 "Too many stream priority values buffered: ",
604 buffered_stream_priorities_.size(),
605 ", which should not exceed the incoming stream limit of ",
606 max_open_incoming_bidirectional_streams());
607 QUIC_BUG << error_message;
608 connection()->CloseConnection(
609 QUIC_INTERNAL_ERROR, error_message,
610 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
611 return false;
612 }
613
614 return true;
615 }
616
OnPriorityUpdateForPushStream(QuicStreamId,int)617 bool QuicSpdySession::OnPriorityUpdateForPushStream(QuicStreamId /*push_id*/,
618 int /*urgency*/) {
619 // TODO(b/147306124): Implement PRIORITY_UPDATE frames for pushed streams.
620 return true;
621 }
622
ProcessHeaderData(const struct iovec & iov)623 size_t QuicSpdySession::ProcessHeaderData(const struct iovec& iov) {
624 QUIC_BUG_IF(destruction_indicator_ != 123456789)
625 << "QuicSpdyStream use after free. " << destruction_indicator_
626 << QuicStackTrace();
627 return h2_deframer_.ProcessInput(static_cast<char*>(iov.iov_base),
628 iov.iov_len);
629 }
630
WriteHeadersOnHeadersStream(QuicStreamId id,SpdyHeaderBlock headers,bool fin,const spdy::SpdyStreamPrecedence & precedence,QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener)631 size_t QuicSpdySession::WriteHeadersOnHeadersStream(
632 QuicStreamId id,
633 SpdyHeaderBlock headers,
634 bool fin,
635 const spdy::SpdyStreamPrecedence& precedence,
636 QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
637 DCHECK(!VersionUsesHttp3(transport_version()));
638
639 return WriteHeadersOnHeadersStreamImpl(
640 id, std::move(headers), fin,
641 /* parent_stream_id = */ 0,
642 Spdy3PriorityToHttp2Weight(precedence.spdy3_priority()),
643 /* exclusive = */ false, std::move(ack_listener));
644 }
645
WritePriority(QuicStreamId id,QuicStreamId parent_stream_id,int weight,bool exclusive)646 size_t QuicSpdySession::WritePriority(QuicStreamId id,
647 QuicStreamId parent_stream_id,
648 int weight,
649 bool exclusive) {
650 DCHECK(!VersionUsesHttp3(transport_version()));
651 SpdyPriorityIR priority_frame(id, parent_stream_id, weight, exclusive);
652 SpdySerializedFrame frame(spdy_framer_.SerializeFrame(priority_frame));
653 headers_stream()->WriteOrBufferData(
654 absl::string_view(frame.data(), frame.size()), false, nullptr);
655 return frame.size();
656 }
657
WriteHttp3PriorityUpdate(const PriorityUpdateFrame & priority_update)658 void QuicSpdySession::WriteHttp3PriorityUpdate(
659 const PriorityUpdateFrame& priority_update) {
660 DCHECK(VersionUsesHttp3(transport_version()));
661
662 send_control_stream_->WritePriorityUpdate(priority_update);
663 }
664
OnHttp3GoAway(uint64_t id)665 void QuicSpdySession::OnHttp3GoAway(uint64_t id) {
666 QUIC_BUG_IF(!version().UsesHttp3())
667 << "HTTP/3 GOAWAY received on version " << version();
668
669 if (last_received_http3_goaway_id_.has_value() &&
670 id > last_received_http3_goaway_id_.value()) {
671 CloseConnectionWithDetails(
672 QUIC_HTTP_GOAWAY_ID_LARGER_THAN_PREVIOUS,
673 quiche::QuicheStrCat("GOAWAY received with ID ", id,
674 " greater than previously received ID ",
675 last_received_http3_goaway_id_.value()));
676 return;
677 }
678 last_received_http3_goaway_id_ = id;
679
680 if (perspective() == Perspective::IS_SERVER) {
681 // TODO(b/151749109): Cancel server pushes with push ID larger than |id|.
682 return;
683 }
684
685 // QuicStreamId is uint32_t. Casting to this narrower type is well-defined
686 // and preserves the lower 32 bits. Both IsBidirectionalStreamId() and
687 // IsIncomingStream() give correct results, because their return value is
688 // determined by the least significant two bits.
689 QuicStreamId stream_id = static_cast<QuicStreamId>(id);
690 if (!QuicUtils::IsBidirectionalStreamId(stream_id, version()) ||
691 IsIncomingStream(stream_id)) {
692 CloseConnectionWithDetails(QUIC_HTTP_GOAWAY_INVALID_STREAM_ID,
693 "GOAWAY with invalid stream ID");
694 return;
695 }
696
697 // TODO(b/161252736): Cancel client requests with ID larger than |id|.
698 // If |id| is larger than numeric_limits<QuicStreamId>::max(), then use
699 // max() instead of downcast value.
700 }
701
OnStreamsBlockedFrame(const QuicStreamsBlockedFrame & frame)702 bool QuicSpdySession::OnStreamsBlockedFrame(
703 const QuicStreamsBlockedFrame& frame) {
704 if (!QuicSession::OnStreamsBlockedFrame(frame)) {
705 return false;
706 }
707
708 // The peer asked for stream space more than this implementation has. Send
709 // goaway.
710 if (perspective() == Perspective::IS_SERVER &&
711 frame.stream_count >= QuicUtils::GetMaxStreamCount()) {
712 DCHECK_EQ(frame.stream_count, QuicUtils::GetMaxStreamCount());
713 SendHttp3GoAway();
714 }
715 return true;
716 }
717
SendHttp3GoAway()718 void QuicSpdySession::SendHttp3GoAway() {
719 DCHECK_EQ(perspective(), Perspective::IS_SERVER);
720 DCHECK(VersionUsesHttp3(transport_version()));
721
722 QuicStreamId stream_id;
723
724 if (goaway_with_max_stream_id_) {
725 stream_id = QuicUtils::GetMaxClientInitiatedBidirectionalStreamId(
726 transport_version());
727 if (last_sent_http3_goaway_id_.has_value()) {
728 if (last_sent_http3_goaway_id_.value() == stream_id) {
729 // Do not send GOAWAY twice.
730 return;
731 }
732 if (last_sent_http3_goaway_id_.value() < stream_id) {
733 // A previous GOAWAY frame was sent with smaller stream ID. This is not
734 // possible, because the only time a GOAWAY frame with non-maximal
735 // stream ID is sent is right before closing connection.
736 QUIC_BUG << "GOAWAY frame with smaller ID already sent.";
737 return;
738 }
739 }
740 } else {
741 stream_id = GetLargestPeerCreatedStreamId(/*unidirectional = */ false);
742
743 if (GetQuicReloadableFlag(quic_fix_http3_goaway_stream_id)) {
744 QUIC_RELOADABLE_FLAG_COUNT(quic_fix_http3_goaway_stream_id);
745 if (stream_id == QuicUtils::GetInvalidStreamId(transport_version())) {
746 // No client-initiated bidirectional streams received yet.
747 // Send 0 to let client know that all requests can be retried.
748 stream_id = 0;
749 } else {
750 // Tell client that streams starting with the next after the largest
751 // received one can be retried.
752 stream_id += QuicUtils::StreamIdDelta(transport_version());
753 }
754 if (last_sent_http3_goaway_id_.has_value() &&
755 last_sent_http3_goaway_id_.value() <= stream_id) {
756 // MUST not send GOAWAY with identifier larger than previously sent.
757 // Do not bother sending one with same identifier as before, since
758 // GOAWAY frames on the control stream are guaranteed to be processed in
759 // order.
760 return;
761 }
762 }
763 }
764
765 send_control_stream_->SendGoAway(stream_id);
766 last_sent_http3_goaway_id_ = stream_id;
767 }
768
SendHttp3Shutdown()769 void QuicSpdySession::SendHttp3Shutdown() {
770 if (goaway_with_max_stream_id_) {
771 SendHttp3GoAway();
772 return;
773 }
774
775 DCHECK_EQ(perspective(), Perspective::IS_SERVER);
776 DCHECK(VersionUsesHttp3(transport_version()));
777 QuicStreamCount advertised_max_incoming_bidirectional_streams =
778 GetAdvertisedMaxIncomingBidirectionalStreams();
779 const QuicStreamId stream_id =
780 QuicUtils::GetFirstBidirectionalStreamId(transport_version(),
781 Perspective::IS_CLIENT) +
782 QuicUtils::StreamIdDelta(transport_version()) *
783 advertised_max_incoming_bidirectional_streams;
784 if (last_sent_http3_goaway_id_.has_value() &&
785 last_sent_http3_goaway_id_.value() < stream_id) {
786 send_control_stream_->SendGoAway(last_sent_http3_goaway_id_.value());
787 return;
788 }
789 send_control_stream_->SendGoAway(stream_id);
790 last_sent_http3_goaway_id_ = stream_id;
791 }
792
WritePushPromise(QuicStreamId original_stream_id,QuicStreamId promised_stream_id,SpdyHeaderBlock headers)793 void QuicSpdySession::WritePushPromise(QuicStreamId original_stream_id,
794 QuicStreamId promised_stream_id,
795 SpdyHeaderBlock headers) {
796 if (perspective() == Perspective::IS_CLIENT) {
797 QUIC_BUG << "Client shouldn't send PUSH_PROMISE";
798 return;
799 }
800
801 if (!VersionUsesHttp3(transport_version())) {
802 SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id,
803 std::move(headers));
804 // PUSH_PROMISE must not be the last frame sent out, at least followed by
805 // response headers.
806 push_promise.set_fin(false);
807
808 SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise));
809 headers_stream()->WriteOrBufferData(
810 absl::string_view(frame.data(), frame.size()), false, nullptr);
811 return;
812 }
813
814 if (!max_push_id_.has_value() || promised_stream_id > max_push_id_.value()) {
815 QUIC_BUG
816 << "Server shouldn't send push id higher than client's MAX_PUSH_ID.";
817 return;
818 }
819
820 // Encode header list.
821 std::string encoded_headers =
822 qpack_encoder_->EncodeHeaderList(original_stream_id, headers, nullptr);
823
824 if (debug_visitor_) {
825 debug_visitor_->OnPushPromiseFrameSent(original_stream_id,
826 promised_stream_id, headers);
827 }
828
829 PushPromiseFrame frame;
830 frame.push_id = promised_stream_id;
831 frame.headers = encoded_headers;
832 QuicSpdyStream* stream = GetOrCreateSpdyDataStream(original_stream_id);
833 stream->WritePushPromise(frame);
834 }
835
server_push_enabled() const836 bool QuicSpdySession::server_push_enabled() const {
837 return VersionUsesHttp3(transport_version())
838 ? ietf_server_push_enabled_ && max_push_id_.has_value()
839 : server_push_enabled_;
840 }
841
SendInitialData()842 void QuicSpdySession::SendInitialData() {
843 if (!VersionUsesHttp3(transport_version())) {
844 return;
845 }
846 QuicConnection::ScopedPacketFlusher flusher(connection());
847 send_control_stream_->MaybeSendSettingsFrame();
848 if (perspective() == Perspective::IS_CLIENT && max_push_id_.has_value() &&
849 !http3_max_push_id_sent_) {
850 SendMaxPushId();
851 }
852 }
853
qpack_encoder()854 QpackEncoder* QuicSpdySession::qpack_encoder() {
855 DCHECK(VersionUsesHttp3(transport_version()));
856
857 return qpack_encoder_.get();
858 }
859
qpack_decoder()860 QpackDecoder* QuicSpdySession::qpack_decoder() {
861 DCHECK(VersionUsesHttp3(transport_version()));
862
863 return qpack_decoder_.get();
864 }
865
OnStreamCreated(QuicSpdyStream * stream)866 void QuicSpdySession::OnStreamCreated(QuicSpdyStream* stream) {
867 auto it = buffered_stream_priorities_.find(stream->id());
868 if (it == buffered_stream_priorities_.end()) {
869 return;
870 }
871
872 stream->SetPriority(spdy::SpdyStreamPrecedence(it->second));
873 buffered_stream_priorities_.erase(it);
874 }
875
GetOrCreateSpdyDataStream(const QuicStreamId stream_id)876 QuicSpdyStream* QuicSpdySession::GetOrCreateSpdyDataStream(
877 const QuicStreamId stream_id) {
878 QuicStream* stream = GetOrCreateStream(stream_id);
879 if (stream && stream->is_static()) {
880 QUIC_BUG << "GetOrCreateSpdyDataStream returns static stream " << stream_id
881 << " in version " << transport_version() << "\n"
882 << QuicStackTrace();
883 connection()->CloseConnection(
884 QUIC_INVALID_STREAM_ID,
885 quiche::QuicheStrCat("stream ", stream_id, " is static"),
886 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
887 return nullptr;
888 }
889 return static_cast<QuicSpdyStream*>(stream);
890 }
891
OnNewEncryptionKeyAvailable(EncryptionLevel level,std::unique_ptr<QuicEncrypter> encrypter)892 void QuicSpdySession::OnNewEncryptionKeyAvailable(
893 EncryptionLevel level,
894 std::unique_ptr<QuicEncrypter> encrypter) {
895 QuicSession::OnNewEncryptionKeyAvailable(level, std::move(encrypter));
896 if (IsEncryptionEstablished()) {
897 // Send H3 SETTINGs once encryption is established.
898 SendInitialData();
899 }
900 }
901
902 // True if there are open HTTP requests.
ShouldKeepConnectionAlive() const903 bool QuicSpdySession::ShouldKeepConnectionAlive() const {
904 DCHECK(VersionUsesHttp3(transport_version()) || 0u == pending_streams_size());
905 return GetNumActiveStreams() + pending_streams_size() > 0;
906 }
907
UsesPendingStreams() const908 bool QuicSpdySession::UsesPendingStreams() const {
909 // QuicSpdySession supports PendingStreams, therefore this method should
910 // eventually just return true. However, pending streams can only be used if
911 // unidirectional stream type is supported.
912 return VersionUsesHttp3(transport_version());
913 }
914
WriteHeadersOnHeadersStreamImpl(QuicStreamId id,spdy::SpdyHeaderBlock headers,bool fin,QuicStreamId parent_stream_id,int weight,bool exclusive,QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener)915 size_t QuicSpdySession::WriteHeadersOnHeadersStreamImpl(
916 QuicStreamId id,
917 spdy::SpdyHeaderBlock headers,
918 bool fin,
919 QuicStreamId parent_stream_id,
920 int weight,
921 bool exclusive,
922 QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
923 DCHECK(!VersionUsesHttp3(transport_version()));
924
925 const QuicByteCount uncompressed_size = headers.TotalBytesUsed();
926 SpdyHeadersIR headers_frame(id, std::move(headers));
927 headers_frame.set_fin(fin);
928 if (perspective() == Perspective::IS_CLIENT) {
929 headers_frame.set_has_priority(true);
930 headers_frame.set_parent_stream_id(parent_stream_id);
931 headers_frame.set_weight(weight);
932 headers_frame.set_exclusive(exclusive);
933 }
934 SpdySerializedFrame frame(spdy_framer_.SerializeFrame(headers_frame));
935 headers_stream()->WriteOrBufferData(
936 absl::string_view(frame.data(), frame.size()), false,
937 std::move(ack_listener));
938
939 // Calculate compressed header block size without framing overhead.
940 QuicByteCount compressed_size = frame.size();
941 compressed_size -= spdy::kFrameHeaderSize;
942 if (perspective() == Perspective::IS_CLIENT) {
943 // Exclusive bit and Stream Dependency are four bytes, weight is one more.
944 compressed_size -= 5;
945 }
946
947 LogHeaderCompressionRatioHistogram(
948 /* using_qpack = */ false,
949 /* is_sent = */ true, compressed_size, uncompressed_size);
950
951 return frame.size();
952 }
953
OnPromiseHeaderList(QuicStreamId,QuicStreamId,size_t,const QuicHeaderList &)954 void QuicSpdySession::OnPromiseHeaderList(
955 QuicStreamId /*stream_id*/,
956 QuicStreamId /*promised_stream_id*/,
957 size_t /*frame_len*/,
958 const QuicHeaderList& /*header_list*/) {
959 std::string error =
960 "OnPromiseHeaderList should be overridden in client code.";
961 QUIC_BUG << error;
962 connection()->CloseConnection(QUIC_INTERNAL_ERROR, error,
963 ConnectionCloseBehavior::SILENT_CLOSE);
964 }
965
ResumeApplicationState(ApplicationState * cached_state)966 bool QuicSpdySession::ResumeApplicationState(ApplicationState* cached_state) {
967 DCHECK_EQ(perspective(), Perspective::IS_CLIENT);
968 DCHECK(VersionUsesHttp3(transport_version()));
969
970 SettingsFrame out;
971 if (!HttpDecoder::DecodeSettings(
972 reinterpret_cast<char*>(cached_state->data()), cached_state->size(),
973 &out)) {
974 return false;
975 }
976
977 if (debug_visitor_ != nullptr) {
978 debug_visitor_->OnSettingsFrameResumed(out);
979 }
980 for (const auto& setting : out.values) {
981 OnSetting(setting.first, setting.second);
982 }
983 return true;
984 }
985
OnSettingsFrame(const SettingsFrame & frame)986 bool QuicSpdySession::OnSettingsFrame(const SettingsFrame& frame) {
987 DCHECK(VersionUsesHttp3(transport_version()));
988 if (debug_visitor_ != nullptr) {
989 debug_visitor_->OnSettingsFrameReceived(frame);
990 }
991 for (const auto& setting : frame.values) {
992 if (!OnSetting(setting.first, setting.second)) {
993 return false;
994 }
995 }
996 return true;
997 }
998
OnSetting(uint64_t id,uint64_t value)999 bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) {
1000 if (VersionUsesHttp3(transport_version())) {
1001 if (reject_spdy_settings_) {
1002 QUIC_RELOADABLE_FLAG_COUNT(quic_reject_spdy_settings);
1003 }
1004 // SETTINGS frame received on the control stream.
1005 switch (id) {
1006 case SETTINGS_QPACK_MAX_TABLE_CAPACITY: {
1007 QUIC_DVLOG(1)
1008 << ENDPOINT
1009 << "SETTINGS_QPACK_MAX_TABLE_CAPACITY received with value "
1010 << value;
1011 // Communicate |value| to encoder, because it is used for encoding
1012 // Required Insert Count.
1013 bool success = qpack_encoder_->SetMaximumDynamicTableCapacity(value);
1014 if (GetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2) && !success) {
1015 CloseConnectionWithDetails(
1016 was_zero_rtt_rejected()
1017 ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH
1018 : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
1019 quiche::QuicheStrCat(
1020 was_zero_rtt_rejected()
1021 ? "Server rejected 0-RTT, aborting because "
1022 : "",
1023 "Server sent an SETTINGS_QPACK_MAX_TABLE_CAPACITY: ", value,
1024 "while current value is: ",
1025 qpack_encoder_->MaximumDynamicTableCapacity()));
1026 return false;
1027 }
1028 // However, limit the dynamic table capacity to
1029 // |qpack_maximum_dynamic_table_capacity_|.
1030 qpack_encoder_->SetDynamicTableCapacity(
1031 std::min(value, qpack_maximum_dynamic_table_capacity_));
1032 break;
1033 }
1034 case SETTINGS_MAX_FIELD_SECTION_SIZE:
1035 QUIC_DVLOG(1) << ENDPOINT
1036 << "SETTINGS_MAX_FIELD_SECTION_SIZE received with value "
1037 << value;
1038 if (GetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2) &&
1039 max_outbound_header_list_size_ !=
1040 std::numeric_limits<size_t>::max() &&
1041 max_outbound_header_list_size_ > value) {
1042 CloseConnectionWithDetails(
1043 was_zero_rtt_rejected()
1044 ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH
1045 : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
1046 quiche::QuicheStrCat(
1047 was_zero_rtt_rejected()
1048 ? "Server rejected 0-RTT, aborting because "
1049 : "",
1050 "Server sent an SETTINGS_MAX_FIELD_SECTION_SIZE: ", value,
1051 "which reduces current value: ",
1052 max_outbound_header_list_size_));
1053 return false;
1054 }
1055 max_outbound_header_list_size_ = value;
1056 break;
1057 case SETTINGS_QPACK_BLOCKED_STREAMS: {
1058 QUIC_DVLOG(1) << ENDPOINT
1059 << "SETTINGS_QPACK_BLOCKED_STREAMS received with value "
1060 << value;
1061 bool success = qpack_encoder_->SetMaximumBlockedStreams(value);
1062 if (GetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2) && !success) {
1063 CloseConnectionWithDetails(
1064 was_zero_rtt_rejected()
1065 ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH
1066 : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
1067 quiche::QuicheStrCat(
1068 was_zero_rtt_rejected()
1069 ? "Server rejected 0-RTT, aborting because "
1070 : "",
1071 "Server sent an SETTINGS_QPACK_BLOCKED_STREAMS: ", value,
1072 "which reduces current value: ",
1073 qpack_encoder_->maximum_blocked_streams()));
1074 return false;
1075 }
1076 break;
1077 }
1078 case spdy::SETTINGS_ENABLE_PUSH:
1079 ABSL_FALLTHROUGH_INTENDED;
1080 case spdy::SETTINGS_MAX_CONCURRENT_STREAMS:
1081 ABSL_FALLTHROUGH_INTENDED;
1082 case spdy::SETTINGS_INITIAL_WINDOW_SIZE:
1083 ABSL_FALLTHROUGH_INTENDED;
1084 case spdy::SETTINGS_MAX_FRAME_SIZE:
1085 if (reject_spdy_settings_) {
1086 CloseConnectionWithDetails(
1087 QUIC_HTTP_RECEIVE_SPDY_SETTING,
1088 quiche::QuicheStrCat(
1089 "received HTTP/2 specific setting in HTTP/3 session: ", id));
1090 return false;
1091 }
1092 break;
1093 default:
1094 QUIC_DVLOG(1) << ENDPOINT << "Unknown setting identifier " << id
1095 << " received with value " << value;
1096 // Ignore unknown settings.
1097 break;
1098 }
1099 return true;
1100 }
1101
1102 // SETTINGS frame received on the headers stream.
1103 switch (id) {
1104 case spdy::SETTINGS_HEADER_TABLE_SIZE:
1105 QUIC_DVLOG(1) << ENDPOINT
1106 << "SETTINGS_HEADER_TABLE_SIZE received with value "
1107 << value;
1108 spdy_framer_.UpdateHeaderEncoderTableSize(value);
1109 break;
1110 case spdy::SETTINGS_ENABLE_PUSH:
1111 if (perspective() == Perspective::IS_SERVER) {
1112 // See rfc7540, Section 6.5.2.
1113 if (value > 1) {
1114 QUIC_DLOG(ERROR) << ENDPOINT << "Invalid value " << value
1115 << " received for SETTINGS_ENABLE_PUSH.";
1116 if (IsConnected()) {
1117 CloseConnectionWithDetails(
1118 QUIC_INVALID_HEADERS_STREAM_DATA,
1119 quiche::QuicheStrCat("Invalid value for SETTINGS_ENABLE_PUSH: ",
1120 value));
1121 }
1122 return true;
1123 }
1124 QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_ENABLE_PUSH received with value "
1125 << value;
1126 server_push_enabled_ = value;
1127 break;
1128 } else {
1129 QUIC_DLOG(ERROR)
1130 << ENDPOINT
1131 << "Invalid SETTINGS_ENABLE_PUSH received by client with value "
1132 << value;
1133 if (IsConnected()) {
1134 CloseConnectionWithDetails(
1135 QUIC_INVALID_HEADERS_STREAM_DATA,
1136 quiche::QuicheStrCat(
1137 "Unsupported field of HTTP/2 SETTINGS frame: ", id));
1138 }
1139 }
1140 break;
1141 case spdy::SETTINGS_MAX_HEADER_LIST_SIZE:
1142 QUIC_DVLOG(1) << ENDPOINT
1143 << "SETTINGS_MAX_HEADER_LIST_SIZE received with value "
1144 << value;
1145 max_outbound_header_list_size_ = value;
1146 break;
1147 default:
1148 QUIC_DLOG(ERROR) << ENDPOINT << "Unknown setting identifier " << id
1149 << " received with value " << value;
1150 if (IsConnected()) {
1151 CloseConnectionWithDetails(
1152 QUIC_INVALID_HEADERS_STREAM_DATA,
1153 quiche::QuicheStrCat("Unsupported field of HTTP/2 SETTINGS frame: ",
1154 id));
1155 }
1156 }
1157 return true;
1158 }
1159
ShouldReleaseHeadersStreamSequencerBuffer()1160 bool QuicSpdySession::ShouldReleaseHeadersStreamSequencerBuffer() {
1161 return false;
1162 }
1163
OnHeaders(SpdyStreamId stream_id,bool has_priority,const spdy::SpdyStreamPrecedence & precedence,bool fin)1164 void QuicSpdySession::OnHeaders(SpdyStreamId stream_id,
1165 bool has_priority,
1166 const spdy::SpdyStreamPrecedence& precedence,
1167 bool fin) {
1168 if (has_priority) {
1169 if (perspective() == Perspective::IS_CLIENT) {
1170 CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
1171 "Server must not send priorities.");
1172 return;
1173 }
1174 OnStreamHeadersPriority(stream_id, precedence);
1175 } else {
1176 if (perspective() == Perspective::IS_SERVER) {
1177 CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
1178 "Client must send priorities.");
1179 return;
1180 }
1181 }
1182 DCHECK_EQ(QuicUtils::GetInvalidStreamId(transport_version()), stream_id_);
1183 DCHECK_EQ(QuicUtils::GetInvalidStreamId(transport_version()),
1184 promised_stream_id_);
1185 stream_id_ = stream_id;
1186 fin_ = fin;
1187 }
1188
OnPushPromise(SpdyStreamId stream_id,SpdyStreamId promised_stream_id)1189 void QuicSpdySession::OnPushPromise(SpdyStreamId stream_id,
1190 SpdyStreamId promised_stream_id) {
1191 DCHECK_EQ(QuicUtils::GetInvalidStreamId(transport_version()), stream_id_);
1192 DCHECK_EQ(QuicUtils::GetInvalidStreamId(transport_version()),
1193 promised_stream_id_);
1194 stream_id_ = stream_id;
1195 promised_stream_id_ = promised_stream_id;
1196 }
1197
1198 // TODO (wangyix): Why is SpdyStreamId used instead of QuicStreamId?
1199 // This occurs in many places in this file.
OnPriority(SpdyStreamId stream_id,const spdy::SpdyStreamPrecedence & precedence)1200 void QuicSpdySession::OnPriority(SpdyStreamId stream_id,
1201 const spdy::SpdyStreamPrecedence& precedence) {
1202 if (perspective() == Perspective::IS_CLIENT) {
1203 CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
1204 "Server must not send PRIORITY frames.");
1205 return;
1206 }
1207 OnPriorityFrame(stream_id, precedence);
1208 }
1209
OnHeaderList(const QuicHeaderList & header_list)1210 void QuicSpdySession::OnHeaderList(const QuicHeaderList& header_list) {
1211 QUIC_DVLOG(1) << ENDPOINT << "Received header list for stream " << stream_id_
1212 << ": " << header_list.DebugString();
1213 // This code path is only executed for push promise in IETF QUIC.
1214 if (VersionUsesHttp3(transport_version())) {
1215 DCHECK(promised_stream_id_ !=
1216 QuicUtils::GetInvalidStreamId(transport_version()));
1217 }
1218 if (promised_stream_id_ ==
1219 QuicUtils::GetInvalidStreamId(transport_version())) {
1220 OnStreamHeaderList(stream_id_, fin_, frame_len_, header_list);
1221 } else {
1222 OnPromiseHeaderList(stream_id_, promised_stream_id_, frame_len_,
1223 header_list);
1224 }
1225 // Reset state for the next frame.
1226 promised_stream_id_ = QuicUtils::GetInvalidStreamId(transport_version());
1227 stream_id_ = QuicUtils::GetInvalidStreamId(transport_version());
1228 fin_ = false;
1229 frame_len_ = 0;
1230 }
1231
OnCompressedFrameSize(size_t frame_len)1232 void QuicSpdySession::OnCompressedFrameSize(size_t frame_len) {
1233 frame_len_ += frame_len;
1234 }
1235
SetHpackEncoderDebugVisitor(std::unique_ptr<QuicHpackDebugVisitor> visitor)1236 void QuicSpdySession::SetHpackEncoderDebugVisitor(
1237 std::unique_ptr<QuicHpackDebugVisitor> visitor) {
1238 spdy_framer_.SetEncoderHeaderTableDebugVisitor(
1239 std::unique_ptr<HeaderTableDebugVisitor>(new HeaderTableDebugVisitor(
1240 connection()->helper()->GetClock(), std::move(visitor))));
1241 }
1242
SetHpackDecoderDebugVisitor(std::unique_ptr<QuicHpackDebugVisitor> visitor)1243 void QuicSpdySession::SetHpackDecoderDebugVisitor(
1244 std::unique_ptr<QuicHpackDebugVisitor> visitor) {
1245 h2_deframer_.SetDecoderHeaderTableDebugVisitor(
1246 std::make_unique<HeaderTableDebugVisitor>(
1247 connection()->helper()->GetClock(), std::move(visitor)));
1248 }
1249
CloseConnectionWithDetails(QuicErrorCode error,const std::string & details)1250 void QuicSpdySession::CloseConnectionWithDetails(QuicErrorCode error,
1251 const std::string& details) {
1252 connection()->CloseConnection(
1253 error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
1254 }
1255
HasActiveRequestStreams() const1256 bool QuicSpdySession::HasActiveRequestStreams() const {
1257 return GetNumActiveStreams() + num_draining_streams() > 0;
1258 }
1259
ProcessPendingStream(PendingStream * pending)1260 bool QuicSpdySession::ProcessPendingStream(PendingStream* pending) {
1261 DCHECK(VersionUsesHttp3(transport_version()));
1262 DCHECK(connection()->connected());
1263 struct iovec iov;
1264 if (!pending->sequencer()->GetReadableRegion(&iov)) {
1265 // The first byte hasn't been received yet.
1266 return false;
1267 }
1268
1269 QuicDataReader reader(static_cast<char*>(iov.iov_base), iov.iov_len);
1270 uint8_t stream_type_length = reader.PeekVarInt62Length();
1271 uint64_t stream_type = 0;
1272 if (!reader.ReadVarInt62(&stream_type)) {
1273 if (pending->sequencer()->NumBytesBuffered() ==
1274 pending->sequencer()->close_offset()) {
1275 // Stream received FIN but there are not enough bytes for stream type.
1276 // Mark all bytes consumed in order to close stream.
1277 pending->MarkConsumed(pending->sequencer()->close_offset());
1278 }
1279 return false;
1280 }
1281 pending->MarkConsumed(stream_type_length);
1282
1283 switch (stream_type) {
1284 case kControlStream: { // HTTP/3 control stream.
1285 if (receive_control_stream_) {
1286 CloseConnectionOnDuplicateHttp3UnidirectionalStreams("Control");
1287 return false;
1288 }
1289 auto receive_stream =
1290 std::make_unique<QuicReceiveControlStream>(pending, this);
1291 receive_control_stream_ = receive_stream.get();
1292 ActivateStream(std::move(receive_stream));
1293 receive_control_stream_->SetUnblocked();
1294 QUIC_DVLOG(1) << ENDPOINT << "Receive Control stream is created";
1295 if (debug_visitor_ != nullptr) {
1296 debug_visitor_->OnPeerControlStreamCreated(
1297 receive_control_stream_->id());
1298 }
1299 return true;
1300 }
1301 case kServerPushStream: { // Push Stream.
1302 QuicSpdyStream* stream = CreateIncomingStream(pending);
1303 stream->SetUnblocked();
1304 return true;
1305 }
1306 case kQpackEncoderStream: { // QPACK encoder stream.
1307 if (qpack_encoder_receive_stream_) {
1308 CloseConnectionOnDuplicateHttp3UnidirectionalStreams("QPACK encoder");
1309 return false;
1310 }
1311 auto encoder_receive = std::make_unique<QpackReceiveStream>(
1312 pending, this, qpack_decoder_->encoder_stream_receiver());
1313 qpack_encoder_receive_stream_ = encoder_receive.get();
1314 ActivateStream(std::move(encoder_receive));
1315 qpack_encoder_receive_stream_->SetUnblocked();
1316 QUIC_DVLOG(1) << ENDPOINT << "Receive QPACK Encoder stream is created";
1317 if (debug_visitor_ != nullptr) {
1318 debug_visitor_->OnPeerQpackEncoderStreamCreated(
1319 qpack_encoder_receive_stream_->id());
1320 }
1321 return true;
1322 }
1323 case kQpackDecoderStream: { // QPACK decoder stream.
1324 if (qpack_decoder_receive_stream_) {
1325 CloseConnectionOnDuplicateHttp3UnidirectionalStreams("QPACK decoder");
1326 return false;
1327 }
1328 auto decoder_receive = std::make_unique<QpackReceiveStream>(
1329 pending, this, qpack_encoder_->decoder_stream_receiver());
1330 qpack_decoder_receive_stream_ = decoder_receive.get();
1331 ActivateStream(std::move(decoder_receive));
1332 qpack_decoder_receive_stream_->SetUnblocked();
1333 QUIC_DVLOG(1) << ENDPOINT << "Receive QPACK Decoder stream is created";
1334 if (debug_visitor_ != nullptr) {
1335 debug_visitor_->OnPeerQpackDecoderStreamCreated(
1336 qpack_decoder_receive_stream_->id());
1337 }
1338 return true;
1339 }
1340 default:
1341 if (GetQuicReloadableFlag(quic_stop_sending_uses_ietf_error_code)) {
1342 SendStopSending(QUIC_STREAM_STREAM_CREATION_ERROR, pending->id());
1343 } else {
1344 SendStopSending(static_cast<QuicRstStreamErrorCode>(
1345 QuicHttp3ErrorCode::STREAM_CREATION_ERROR),
1346 pending->id());
1347 }
1348 pending->StopReading();
1349 }
1350 return false;
1351 }
1352
MaybeInitializeHttp3UnidirectionalStreams()1353 void QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams() {
1354 DCHECK(VersionUsesHttp3(transport_version()));
1355 if (!send_control_stream_ && CanOpenNextOutgoingUnidirectionalStream()) {
1356 auto send_control = std::make_unique<QuicSendControlStream>(
1357 GetNextOutgoingUnidirectionalStreamId(), this, settings_);
1358 send_control_stream_ = send_control.get();
1359 ActivateStream(std::move(send_control));
1360 if (debug_visitor_) {
1361 debug_visitor_->OnControlStreamCreated(send_control_stream_->id());
1362 }
1363 }
1364
1365 if (!qpack_decoder_send_stream_ &&
1366 CanOpenNextOutgoingUnidirectionalStream()) {
1367 auto decoder_send = std::make_unique<QpackSendStream>(
1368 GetNextOutgoingUnidirectionalStreamId(), this, kQpackDecoderStream);
1369 qpack_decoder_send_stream_ = decoder_send.get();
1370 ActivateStream(std::move(decoder_send));
1371 qpack_decoder_->set_qpack_stream_sender_delegate(
1372 qpack_decoder_send_stream_);
1373 if (debug_visitor_) {
1374 debug_visitor_->OnQpackDecoderStreamCreated(
1375 qpack_decoder_send_stream_->id());
1376 }
1377 }
1378
1379 if (!qpack_encoder_send_stream_ &&
1380 CanOpenNextOutgoingUnidirectionalStream()) {
1381 auto encoder_send = std::make_unique<QpackSendStream>(
1382 GetNextOutgoingUnidirectionalStreamId(), this, kQpackEncoderStream);
1383 qpack_encoder_send_stream_ = encoder_send.get();
1384 ActivateStream(std::move(encoder_send));
1385 qpack_encoder_->set_qpack_stream_sender_delegate(
1386 qpack_encoder_send_stream_);
1387 if (debug_visitor_) {
1388 debug_visitor_->OnQpackEncoderStreamCreated(
1389 qpack_encoder_send_stream_->id());
1390 }
1391 }
1392 }
1393
BeforeConnectionCloseSent()1394 void QuicSpdySession::BeforeConnectionCloseSent() {
1395 if (!GetQuicReloadableFlag(quic_send_goaway_with_connection_close) ||
1396 !VersionUsesHttp3(transport_version()) || !IsEncryptionEstablished()) {
1397 return;
1398 }
1399
1400 DCHECK_EQ(perspective(), Perspective::IS_SERVER);
1401
1402 QUIC_CODE_COUNT(quic_send_goaway_with_connection_close);
1403
1404 QuicStreamId stream_id =
1405 GetLargestPeerCreatedStreamId(/*unidirectional = */ false);
1406
1407 if (GetQuicReloadableFlag(quic_fix_http3_goaway_stream_id)) {
1408 QUIC_RELOADABLE_FLAG_COUNT(quic_fix_http3_goaway_stream_id);
1409 if (stream_id == QuicUtils::GetInvalidStreamId(transport_version())) {
1410 // No client-initiated bidirectional streams received yet.
1411 // Send 0 to let client know that all requests can be retried.
1412 stream_id = 0;
1413 } else {
1414 // Tell client that streams starting with the next after the largest
1415 // received one can be retried.
1416 stream_id += QuicUtils::StreamIdDelta(transport_version());
1417 }
1418 if (last_sent_http3_goaway_id_.has_value() &&
1419 last_sent_http3_goaway_id_.value() <= stream_id) {
1420 if (goaway_with_max_stream_id_) {
1421 // A previous GOAWAY frame was sent with smaller stream ID. This is not
1422 // possible, because this is the only method sending a GOAWAY frame with
1423 // non-maximal stream ID, and this must only be called once, right
1424 // before closing connection.
1425 QUIC_BUG << "GOAWAY frame with smaller ID already sent.";
1426 }
1427 // MUST not send GOAWAY with identifier larger than previously sent.
1428 // Do not bother sending one with same identifier as before, since GOAWAY
1429 // frames on the control stream are guaranteed to be processed in order.
1430 return;
1431 }
1432 }
1433
1434 send_control_stream_->SendGoAway(stream_id);
1435 last_sent_http3_goaway_id_ = stream_id;
1436 }
1437
OnCanCreateNewOutgoingStream(bool unidirectional)1438 void QuicSpdySession::OnCanCreateNewOutgoingStream(bool unidirectional) {
1439 if (unidirectional && VersionUsesHttp3(transport_version())) {
1440 MaybeInitializeHttp3UnidirectionalStreams();
1441 }
1442 }
1443
SetMaxPushId(PushId max_push_id)1444 void QuicSpdySession::SetMaxPushId(PushId max_push_id) {
1445 DCHECK(VersionUsesHttp3(transport_version()));
1446 DCHECK_EQ(Perspective::IS_CLIENT, perspective());
1447 if (max_push_id_.has_value()) {
1448 DCHECK_GE(max_push_id, max_push_id_.value());
1449 }
1450
1451 if (!max_push_id_.has_value() && max_push_id == 0) {
1452 // The default max_push_id is 0. So no need to send out MaxPushId frame.
1453 return;
1454 }
1455
1456 ietf_server_push_enabled_ = true;
1457
1458 if (max_push_id_.has_value()) {
1459 if (max_push_id == max_push_id_.value()) {
1460 QUIC_DVLOG(1) << "Not changing max_push_id: " << max_push_id;
1461 return;
1462 }
1463
1464 QUIC_DVLOG(1) << "Setting max_push_id to: " << max_push_id
1465 << " from: " << max_push_id_.value();
1466 } else {
1467 QUIC_DVLOG(1) << "Setting max_push_id to: " << max_push_id << " from unset";
1468 }
1469 max_push_id_ = max_push_id;
1470
1471 if (IsEncryptionEstablished()) {
1472 SendMaxPushId();
1473 }
1474 }
1475
OnMaxPushIdFrame(PushId max_push_id)1476 bool QuicSpdySession::OnMaxPushIdFrame(PushId max_push_id) {
1477 DCHECK(VersionUsesHttp3(transport_version()));
1478 DCHECK_EQ(Perspective::IS_SERVER, perspective());
1479
1480 if (max_push_id_.has_value()) {
1481 QUIC_DVLOG(1) << "Setting max_push_id to: " << max_push_id
1482 << " from: " << max_push_id_.value();
1483 } else {
1484 QUIC_DVLOG(1) << "Setting max_push_id to: " << max_push_id
1485 << " from unset";
1486 }
1487 absl::optional<PushId> old_max_push_id = max_push_id_;
1488 max_push_id_ = max_push_id;
1489
1490 if (!old_max_push_id.has_value() || max_push_id > old_max_push_id.value()) {
1491 OnCanCreateNewOutgoingStream(true);
1492 return true;
1493 }
1494
1495 // Equal value is not considered an error.
1496 if (max_push_id < old_max_push_id.value()) {
1497 CloseConnectionWithDetails(
1498 QUIC_HTTP_INVALID_MAX_PUSH_ID,
1499 quiche::QuicheStrCat(
1500 "MAX_PUSH_ID received with value ", max_push_id,
1501 " which is smaller that previously received value ",
1502 old_max_push_id.value()));
1503 return false;
1504 }
1505
1506 return true;
1507 }
1508
SendMaxPushId()1509 void QuicSpdySession::SendMaxPushId() {
1510 DCHECK(VersionUsesHttp3(transport_version()));
1511 DCHECK_EQ(Perspective::IS_CLIENT, perspective());
1512
1513 send_control_stream_->SendMaxPushIdFrame(max_push_id_.value());
1514 http3_max_push_id_sent_ = true;
1515 }
1516
EnableServerPush()1517 void QuicSpdySession::EnableServerPush() {
1518 DCHECK(VersionUsesHttp3(transport_version()));
1519 DCHECK_EQ(perspective(), Perspective::IS_SERVER);
1520
1521 ietf_server_push_enabled_ = true;
1522 }
1523
goaway_received() const1524 bool QuicSpdySession::goaway_received() const {
1525 return VersionUsesHttp3(transport_version())
1526 ? last_received_http3_goaway_id_.has_value()
1527 : transport_goaway_received();
1528 }
1529
goaway_sent() const1530 bool QuicSpdySession::goaway_sent() const {
1531 return VersionUsesHttp3(transport_version())
1532 ? last_sent_http3_goaway_id_.has_value()
1533 : transport_goaway_sent();
1534 }
1535
CanCreatePushStreamWithId(PushId push_id)1536 bool QuicSpdySession::CanCreatePushStreamWithId(PushId push_id) {
1537 DCHECK(VersionUsesHttp3(transport_version()));
1538
1539 return ietf_server_push_enabled_ && max_push_id_.has_value() &&
1540 max_push_id_.value() >= push_id;
1541 }
1542
CloseConnectionOnDuplicateHttp3UnidirectionalStreams(absl::string_view type)1543 void QuicSpdySession::CloseConnectionOnDuplicateHttp3UnidirectionalStreams(
1544 absl::string_view type) {
1545 QUIC_PEER_BUG << quiche::QuicheStrCat("Received a duplicate ", type,
1546 " stream: Closing connection.");
1547 CloseConnectionWithDetails(
1548 QUIC_HTTP_DUPLICATE_UNIDIRECTIONAL_STREAM,
1549 quiche::QuicheStrCat(type, " stream is received twice."));
1550 }
1551
1552 // static
LogHeaderCompressionRatioHistogram(bool using_qpack,bool is_sent,QuicByteCount compressed,QuicByteCount uncompressed)1553 void QuicSpdySession::LogHeaderCompressionRatioHistogram(
1554 bool using_qpack,
1555 bool is_sent,
1556 QuicByteCount compressed,
1557 QuicByteCount uncompressed) {
1558 if (compressed <= 0 || uncompressed <= 0) {
1559 return;
1560 }
1561
1562 int ratio = 100 * (compressed) / (uncompressed);
1563 if (ratio < 1) {
1564 ratio = 1;
1565 } else if (ratio > 200) {
1566 ratio = 200;
1567 }
1568
1569 // Note that when using histogram macros in Chromium, the histogram name must
1570 // be the same across calls for any given call site.
1571 if (using_qpack) {
1572 if (is_sent) {
1573 QUIC_HISTOGRAM_COUNTS("QuicSession.HeaderCompressionRatioQpackSent",
1574 ratio, 1, 200, 200,
1575 "Header compression ratio as percentage for sent "
1576 "headers using QPACK.");
1577 } else {
1578 QUIC_HISTOGRAM_COUNTS("QuicSession.HeaderCompressionRatioQpackReceived",
1579 ratio, 1, 200, 200,
1580 "Header compression ratio as percentage for "
1581 "received headers using QPACK.");
1582 }
1583 } else {
1584 if (is_sent) {
1585 QUIC_HISTOGRAM_COUNTS("QuicSession.HeaderCompressionRatioHpackSent",
1586 ratio, 1, 200, 200,
1587 "Header compression ratio as percentage for sent "
1588 "headers using HPACK.");
1589 } else {
1590 QUIC_HISTOGRAM_COUNTS("QuicSession.HeaderCompressionRatioHpackReceived",
1591 ratio, 1, 200, 200,
1592 "Header compression ratio as percentage for "
1593 "received headers using HPACK.");
1594 }
1595 }
1596 }
1597
1598 #undef ENDPOINT // undef for jumbo builds
1599
1600 } // namespace quic
1601