blob: 8f1b413108ba314ca3bedebfb6dfc7cc9b7c0732 [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// Copyright (c) 2018 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#include "net/third_party/quiche/src/quic/core/quic_stream_id_manager.h"
5
vasilvv872e7a32019-03-12 16:42:44 -07006#include <string>
7
QUICHE teama6ef0a62019-03-07 20:34:33 -05008#include "net/third_party/quiche/src/quic/core/quic_connection.h"
9#include "net/third_party/quiche/src/quic/core/quic_constants.h"
10#include "net/third_party/quiche/src/quic/core/quic_session.h"
11#include "net/third_party/quiche/src/quic/core/quic_utils.h"
12#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
13#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
14#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
dmcardlecf0bfcf2019-12-13 08:08:21 -080015#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050016
17namespace quic {
18
rcha8b56e42019-09-20 10:41:48 -070019#define ENDPOINT \
20 (perspective_ == Perspective::IS_SERVER ? " Server: " : " Client: ")
QUICHE teama6ef0a62019-03-07 20:34:33 -050021
22QuicStreamIdManager::QuicStreamIdManager(
rcha8b56e42019-09-20 10:41:48 -070023 DelegateInterface* delegate,
fkastenholz3c4eabf2019-04-22 07:49:59 -070024 bool unidirectional,
rcha8b56e42019-09-20 10:41:48 -070025 Perspective perspective,
26 QuicTransportVersion transport_version,
fkastenholz3c4eabf2019-04-22 07:49:59 -070027 QuicStreamCount max_allowed_outgoing_streams,
28 QuicStreamCount max_allowed_incoming_streams)
rcha8b56e42019-09-20 10:41:48 -070029 : delegate_(delegate),
fkastenholz3c4eabf2019-04-22 07:49:59 -070030 unidirectional_(unidirectional),
rcha8b56e42019-09-20 10:41:48 -070031 perspective_(perspective),
32 transport_version_(transport_version),
rcha8b56e42019-09-20 10:41:48 -070033 is_config_negotiated_(false),
fkastenholz3c4eabf2019-04-22 07:49:59 -070034 outgoing_max_streams_(max_allowed_outgoing_streams),
35 next_outgoing_stream_id_(GetFirstOutgoingStreamId()),
36 outgoing_stream_count_(0),
fkastenholz3c4eabf2019-04-22 07:49:59 -070037 incoming_actual_max_streams_(max_allowed_incoming_streams),
38 // Advertised max starts at actual because it's communicated in the
39 // handshake.
40 incoming_advertised_max_streams_(max_allowed_incoming_streams),
41 incoming_initial_max_open_streams_(max_allowed_incoming_streams),
fkastenholz3c4eabf2019-04-22 07:49:59 -070042 incoming_stream_count_(0),
43 largest_peer_created_stream_id_(
rcha8b56e42019-09-20 10:41:48 -070044 QuicUtils::GetInvalidStreamId(transport_version)),
fkastenholz56055be2019-09-17 11:17:37 -070045 max_streams_window_(0),
46 pending_max_streams_(false),
47 pending_streams_blocked_(
rcha8b56e42019-09-20 10:41:48 -070048 QuicUtils::GetInvalidStreamId(transport_version)) {
fkastenholz3c4eabf2019-04-22 07:49:59 -070049 CalculateIncomingMaxStreamsWindow();
QUICHE teama6ef0a62019-03-07 20:34:33 -050050}
51
rcha8b56e42019-09-20 10:41:48 -070052QuicStreamIdManager::~QuicStreamIdManager() {}
QUICHE teama6ef0a62019-03-07 20:34:33 -050053
fkastenholz3c4eabf2019-04-22 07:49:59 -070054bool QuicStreamIdManager::OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) {
55 // Ensure that the frame has the correct directionality.
56 DCHECK_EQ(frame.unidirectional, unidirectional_);
57 QUIC_CODE_COUNT_N(quic_max_streams_received, 2, 2);
fkastenholz3c4eabf2019-04-22 07:49:59 -070058
59 // Set the limit to be exactly the stream count in the frame.
fkastenholz56055be2019-09-17 11:17:37 -070060 // Also informs the higher layers that they can create more
61 // streams if the limit is increased.
62 return SetMaxOpenOutgoingStreams(frame.stream_count);
QUICHE teama6ef0a62019-03-07 20:34:33 -050063}
64
fkastenholz3c4eabf2019-04-22 07:49:59 -070065// The peer sends a streams blocked frame when it can not open any more
66// streams because it has runs into the limit.
67bool QuicStreamIdManager::OnStreamsBlockedFrame(
68 const QuicStreamsBlockedFrame& frame) {
69 // Ensure that the frame has the correct directionality.
70 DCHECK_EQ(frame.unidirectional, unidirectional_);
fkastenholz3c4eabf2019-04-22 07:49:59 -070071 if (frame.stream_count > incoming_advertised_max_streams_) {
QUICHE teama6ef0a62019-03-07 20:34:33 -050072 // Peer thinks it can send more streams that we've told it.
73 // This is a protocol error.
fkastenholz3c4eabf2019-04-22 07:49:59 -070074 QUIC_CODE_COUNT(quic_streams_blocked_too_big);
renjietangff4b2b62020-02-12 16:52:32 -080075 delegate_->OnStreamIdManagerError(QUIC_STREAMS_BLOCKED_ERROR,
76 "Invalid stream count specified");
QUICHE teama6ef0a62019-03-07 20:34:33 -050077 return false;
78 }
fkastenholz3c4eabf2019-04-22 07:49:59 -070079 if (frame.stream_count < incoming_actual_max_streams_) {
80 // Peer thinks it's blocked on a stream count that is less than our current
81 // max. Inform the peer of the correct stream count. Sending a MAX_STREAMS
82 // frame in this case is not controlled by the window.
83 SendMaxStreamsFrame();
QUICHE teama6ef0a62019-03-07 20:34:33 -050084 }
fkastenholz3c4eabf2019-04-22 07:49:59 -070085 QUIC_CODE_COUNT(quic_streams_blocked_id_correct);
QUICHE teama6ef0a62019-03-07 20:34:33 -050086 return true;
87}
88
fkastenholz3c4eabf2019-04-22 07:49:59 -070089// Used when configuration has been done and we have an initial
90// maximum stream count from the peer.
renjietang52e13382019-12-16 15:58:04 -080091bool QuicStreamIdManager::SetMaxOpenOutgoingStreams(
92 QuicStreamCount max_open_streams) {
renjietang315428e2020-01-27 10:18:12 -080093 if (max_open_streams <= outgoing_max_streams_) {
fkastenholz3c4eabf2019-04-22 07:49:59 -070094 // Only update the stream count if it would increase the limit.
95 // If it decreases the limit, or doesn't change it, then do not update.
96 // Note that this handles the case of receiving a count of 0 in the frame
97 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -050098 }
99
fkastenholz3c4eabf2019-04-22 07:49:59 -0700100 // This implementation only supports 32 bit Stream IDs, so limit max streams
101 // if it would exceed the max 32 bits can express.
renjietang52e13382019-12-16 15:58:04 -0800102 outgoing_max_streams_ =
103 std::min(max_open_streams,
104 QuicUtils::GetMaxStreamCount(unidirectional_, perspective_));
fkastenholz56055be2019-09-17 11:17:37 -0700105
106 // Inform the higher layers that the stream limit has increased and that
107 // new streams may be created.
rcha8b56e42019-09-20 10:41:48 -0700108 delegate_->OnCanCreateNewOutgoingStream(unidirectional_);
fkastenholz56055be2019-09-17 11:17:37 -0700109
fkastenholz3c4eabf2019-04-22 07:49:59 -0700110 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500111}
112
renjietang52e13382019-12-16 15:58:04 -0800113void QuicStreamIdManager::SetMaxOpenIncomingStreams(
114 QuicStreamCount max_open_streams) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700115 QuicStreamCount implementation_max =
116 QuicUtils::GetMaxStreamCount(unidirectional_, perspective());
renjietang52e13382019-12-16 15:58:04 -0800117 QuicStreamCount new_max = std::min(implementation_max, max_open_streams);
fkastenholz3c4eabf2019-04-22 07:49:59 -0700118 if (new_max < incoming_stream_count_) {
renjietangff4b2b62020-02-12 16:52:32 -0800119 delegate_->OnStreamIdManagerError(
120 QUIC_MAX_STREAMS_ERROR, "Stream limit less than existing stream count");
fkastenholz3c4eabf2019-04-22 07:49:59 -0700121 return;
122 }
123 incoming_actual_max_streams_ = new_max;
124 incoming_advertised_max_streams_ = new_max;
125 incoming_initial_max_open_streams_ =
renjietang52e13382019-12-16 15:58:04 -0800126 std::min(max_open_streams, implementation_max);
fkastenholz3c4eabf2019-04-22 07:49:59 -0700127 CalculateIncomingMaxStreamsWindow();
128}
129
130void QuicStreamIdManager::MaybeSendMaxStreamsFrame() {
131 if ((incoming_advertised_max_streams_ - incoming_stream_count_) >
132 max_streams_window_) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500133 // window too large, no advertisement
134 return;
135 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700136 SendMaxStreamsFrame();
QUICHE teama6ef0a62019-03-07 20:34:33 -0500137}
138
fkastenholz3c4eabf2019-04-22 07:49:59 -0700139void QuicStreamIdManager::SendMaxStreamsFrame() {
rcha8b56e42019-09-20 10:41:48 -0700140 if (!is_config_negotiated_) {
141 // The config has not yet been negotiated, so we can not send the
142 // MAX STREAMS frame yet. Record that we would have sent one and then
143 // return. A new frame will be generated once the configuration is
144 // received.
fkastenholz56055be2019-09-17 11:17:37 -0700145 pending_max_streams_ = true;
146 return;
147 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700148 incoming_advertised_max_streams_ = incoming_actual_max_streams_;
rcha8b56e42019-09-20 10:41:48 -0700149 delegate_->SendMaxStreams(incoming_advertised_max_streams_, unidirectional_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500150}
151
152void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700153 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500154 if (!IsIncomingStream(stream_id)) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700155 // Nothing to do for outgoing streams.
QUICHE teama6ef0a62019-03-07 20:34:33 -0500156 return;
157 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700158 // If the stream is inbound, we can increase the actual stream limit and maybe
159 // advertise the new limit to the peer. Have to check to make sure that we do
160 // not exceed the maximum.
161 if (incoming_actual_max_streams_ ==
162 QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500163 // Reached the maximum stream id value that the implementation
164 // supports. Nothing can be done here.
165 return;
166 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700167 // One stream closed ... another can be opened.
168 incoming_actual_max_streams_++;
169 MaybeSendMaxStreamsFrame();
QUICHE teama6ef0a62019-03-07 20:34:33 -0500170}
171
172QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() {
renjietangab93f582019-12-13 10:29:31 -0800173 // Applications should always consult CanOpenNextOutgoingStream() first.
174 // If they ask for stream ids that violate the limit, it's an implementation
175 // bug.
fkastenholz3c4eabf2019-04-22 07:49:59 -0700176 QUIC_BUG_IF(outgoing_stream_count_ >= outgoing_max_streams_)
177 << "Attempt to allocate a new outgoing stream that would exceed the "
fkastenholz9b4b0ad2019-08-20 05:10:40 -0700178 "limit ("
179 << outgoing_max_streams_ << ")";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500180 QuicStreamId id = next_outgoing_stream_id_;
renjietangab93f582019-12-13 10:29:31 -0800181 next_outgoing_stream_id_ += QuicUtils::StreamIdDelta(transport_version_);
fkastenholz3c4eabf2019-04-22 07:49:59 -0700182 outgoing_stream_count_++;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500183 return id;
184}
185
186bool QuicStreamIdManager::CanOpenNextOutgoingStream() {
renjietangab93f582019-12-13 10:29:31 -0800187 DCHECK(VersionHasIetfQuicFrames(transport_version_));
fkastenholz3c4eabf2019-04-22 07:49:59 -0700188 if (outgoing_stream_count_ < outgoing_max_streams_) {
189 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500190 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700191 // Next stream ID would exceed the limit, need to inform the peer.
fkastenholz56055be2019-09-17 11:17:37 -0700192
rcha8b56e42019-09-20 10:41:48 -0700193 if (!is_config_negotiated_) {
194 // The config is not negotiated, so we can not send the STREAMS_BLOCKED
195 // frame yet. Record that we would have sent one, and what the limit was
196 // when we were blocked, and return.
fkastenholz56055be2019-09-17 11:17:37 -0700197 pending_streams_blocked_ = outgoing_max_streams_;
198 return false;
199 }
rcha8b56e42019-09-20 10:41:48 -0700200 delegate_->SendStreamsBlocked(outgoing_max_streams_, unidirectional_);
fkastenholz3c4eabf2019-04-22 07:49:59 -0700201 QUIC_CODE_COUNT(quic_reached_outgoing_stream_id_limit);
202 return false;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500203}
204
fkastenholz3c4eabf2019-04-22 07:49:59 -0700205// Stream_id is the id of a new incoming stream. Check if it can be
206// created (doesn't violate limits, etc).
QUICHE teama6ef0a62019-03-07 20:34:33 -0500207bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
208 const QuicStreamId stream_id) {
rch9301d3c2019-09-20 14:30:48 -0700209 // |stream_id| must be an incoming stream of the right directionality.
fkastenholz3c4eabf2019-04-22 07:49:59 -0700210 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_);
renjietangab93f582019-12-13 10:29:31 -0800211 DCHECK_NE(QuicUtils::IsServerInitiatedStreamId(transport_version_, stream_id),
212 perspective() == Perspective::IS_SERVER);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500213 available_streams_.erase(stream_id);
214
215 if (largest_peer_created_stream_id_ !=
renjietangab93f582019-12-13 10:29:31 -0800216 QuicUtils::GetInvalidStreamId(transport_version_) &&
QUICHE teama6ef0a62019-03-07 20:34:33 -0500217 stream_id <= largest_peer_created_stream_id_) {
218 return true;
219 }
220
fkastenholz3c4eabf2019-04-22 07:49:59 -0700221 QuicStreamCount stream_count_increment;
222 if (largest_peer_created_stream_id_ !=
renjietangab93f582019-12-13 10:29:31 -0800223 QuicUtils::GetInvalidStreamId(transport_version_)) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700224 stream_count_increment = (stream_id - largest_peer_created_stream_id_) /
renjietangab93f582019-12-13 10:29:31 -0800225 QuicUtils::StreamIdDelta(transport_version_);
fkastenholz3c4eabf2019-04-22 07:49:59 -0700226 } else {
227 // Largest_peer_created_stream_id is the invalid ID,
228 // which means that the peer has not created any stream IDs.
229 // The "+1" is because the first stream ID has not yet
230 // been used. For example, if the FirstIncoming ID is 1
231 // and stream_id is 1, then we want the increment to be 1.
232 stream_count_increment = ((stream_id - GetFirstIncomingStreamId()) /
renjietangab93f582019-12-13 10:29:31 -0800233 QuicUtils::StreamIdDelta(transport_version_)) +
fkastenholz3c4eabf2019-04-22 07:49:59 -0700234 1;
235 }
236
237 // If already at, or over, the limit, close the connection/etc.
238 if (((incoming_stream_count_ + stream_count_increment) >
239 incoming_advertised_max_streams_) ||
240 ((incoming_stream_count_ + stream_count_increment) <
241 incoming_stream_count_)) {
242 // This stream would exceed the limit. do not increase.
QUICHE teama6ef0a62019-03-07 20:34:33 -0500243 QUIC_DLOG(INFO) << ENDPOINT
244 << "Failed to create a new incoming stream with id:"
fkastenholz3c4eabf2019-04-22 07:49:59 -0700245 << stream_id << ", reaching MAX_STREAMS limit: "
246 << incoming_advertised_max_streams_ << ".";
renjietangff4b2b62020-02-12 16:52:32 -0800247 delegate_->OnStreamIdManagerError(
248 QUIC_INVALID_STREAM_ID,
249 quiche::QuicheStrCat("Stream id ", stream_id,
250 " would exceed stream count limit ",
251 incoming_advertised_max_streams_));
QUICHE teama6ef0a62019-03-07 20:34:33 -0500252 return false;
253 }
254
fkastenholz3c4eabf2019-04-22 07:49:59 -0700255 QuicStreamId id = GetFirstIncomingStreamId();
256 if (largest_peer_created_stream_id_ !=
renjietangab93f582019-12-13 10:29:31 -0800257 QuicUtils::GetInvalidStreamId(transport_version_)) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700258 id = largest_peer_created_stream_id_ +
renjietangab93f582019-12-13 10:29:31 -0800259 QuicUtils::StreamIdDelta(transport_version_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500260 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700261
renjietangab93f582019-12-13 10:29:31 -0800262 for (; id < stream_id; id += QuicUtils::StreamIdDelta(transport_version_)) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500263 available_streams_.insert(id);
264 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700265 incoming_stream_count_ += stream_count_increment;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500266 largest_peer_created_stream_id_ = stream_id;
267 return true;
268}
269
270bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700271 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id), unidirectional_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500272 if (!IsIncomingStream(id)) {
273 // Stream IDs under next_ougoing_stream_id_ are either open or previously
274 // open but now closed.
275 return id >= next_outgoing_stream_id_;
276 }
277 // For peer created streams, we also need to consider available streams.
278 return largest_peer_created_stream_id_ ==
renjietangab93f582019-12-13 10:29:31 -0800279 QuicUtils::GetInvalidStreamId(transport_version_) ||
QUICHE teama6ef0a62019-03-07 20:34:33 -0500280 id > largest_peer_created_stream_id_ ||
281 QuicContainsKey(available_streams_, id);
282}
283
284bool QuicStreamIdManager::IsIncomingStream(QuicStreamId id) const {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700285 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id), unidirectional_);
286 // The 0x1 bit in the stream id indicates whether the stream id is
287 // server- or client- initiated. Next_OUTGOING_stream_id_ has that bit
288 // set based on whether this node is a server or client. Thus, if the stream
289 // id in question has the 0x1 bit set opposite of next_OUTGOING_stream_id_,
290 // then that stream id is incoming -- it is for streams initiated by the peer.
291 return (id & 0x1) != (next_outgoing_stream_id_ & 0x1);
292}
293
294QuicStreamId QuicStreamIdManager::GetFirstOutgoingStreamId() const {
295 return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
renjietangab93f582019-12-13 10:29:31 -0800296 transport_version_, perspective())
fkastenholz3c4eabf2019-04-22 07:49:59 -0700297 : QuicUtils::GetFirstBidirectionalStreamId(
renjietangab93f582019-12-13 10:29:31 -0800298 transport_version_, perspective());
fkastenholz3c4eabf2019-04-22 07:49:59 -0700299}
300
301QuicStreamId QuicStreamIdManager::GetFirstIncomingStreamId() const {
302 return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
renjietangab93f582019-12-13 10:29:31 -0800303 transport_version_, peer_perspective())
fkastenholz3c4eabf2019-04-22 07:49:59 -0700304 : QuicUtils::GetFirstBidirectionalStreamId(
renjietangab93f582019-12-13 10:29:31 -0800305 transport_version_, peer_perspective());
fkastenholz3c4eabf2019-04-22 07:49:59 -0700306}
307
308Perspective QuicStreamIdManager::perspective() const {
rcha8b56e42019-09-20 10:41:48 -0700309 return perspective_;
fkastenholz3c4eabf2019-04-22 07:49:59 -0700310}
311
312Perspective QuicStreamIdManager::peer_perspective() const {
nharper4eba09b2019-06-26 20:17:25 -0700313 return QuicUtils::InvertPerspective(perspective());
fkastenholz3c4eabf2019-04-22 07:49:59 -0700314}
315
renjietang52e13382019-12-16 15:58:04 -0800316QuicStreamCount QuicStreamIdManager::available_incoming_streams() {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700317 return incoming_advertised_max_streams_ - incoming_stream_count_;
318}
319
320void QuicStreamIdManager::CalculateIncomingMaxStreamsWindow() {
321 max_streams_window_ = incoming_actual_max_streams_ / kMaxStreamsWindowDivisor;
322 if (max_streams_window_ == 0) {
323 max_streams_window_ = 1;
324 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500325}
326
fkastenholz56055be2019-09-17 11:17:37 -0700327void QuicStreamIdManager::OnConfigNegotiated() {
rcha8b56e42019-09-20 10:41:48 -0700328 is_config_negotiated_ = true;
fkastenholz56055be2019-09-17 11:17:37 -0700329 // If a STREAMS_BLOCKED or MAX_STREAMS is pending, send it and clear
330 // the pending state.
331 if (pending_streams_blocked_ !=
renjietangab93f582019-12-13 10:29:31 -0800332 QuicUtils::GetInvalidStreamId(transport_version_)) {
fkastenholz56055be2019-09-17 11:17:37 -0700333 if (pending_streams_blocked_ >= outgoing_max_streams_) {
334 // There is a pending STREAMS_BLOCKED frame and the current limit does not
335 // let new streams be formed. Regenerate and send the frame.
rcha8b56e42019-09-20 10:41:48 -0700336 delegate_->SendStreamsBlocked(outgoing_max_streams_, unidirectional_);
fkastenholz56055be2019-09-17 11:17:37 -0700337 }
338 pending_streams_blocked_ =
renjietangab93f582019-12-13 10:29:31 -0800339 QuicUtils::GetInvalidStreamId(transport_version_);
fkastenholz56055be2019-09-17 11:17:37 -0700340 }
341 if (pending_max_streams_) {
342 // Generate a MAX_STREAMS using the current stream limits.
343 SendMaxStreamsFrame();
344 pending_max_streams_ = false;
345 }
346}
347
QUICHE teama6ef0a62019-03-07 20:34:33 -0500348} // namespace quic