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