blob: 2921268b5a2d46924ef4fd03d6008419e8bf8b2f [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"
15#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050016
17namespace quic {
18
19#define ENDPOINT \
20 (session_->perspective() == Perspective::IS_SERVER ? " Server: " \
21 : " Client: ")
22
23QuicStreamIdManager::QuicStreamIdManager(
24 QuicSession* session,
fkastenholz3c4eabf2019-04-22 07:49:59 -070025 bool unidirectional,
26 QuicStreamCount max_allowed_outgoing_streams,
27 QuicStreamCount max_allowed_incoming_streams)
QUICHE teama6ef0a62019-03-07 20:34:33 -050028 : session_(session),
fkastenholz3c4eabf2019-04-22 07:49:59 -070029 unidirectional_(unidirectional),
30 outgoing_max_streams_(max_allowed_outgoing_streams),
31 next_outgoing_stream_id_(GetFirstOutgoingStreamId()),
32 outgoing_stream_count_(0),
33 outgoing_static_stream_count_(0),
34 using_default_max_streams_(true),
35 incoming_actual_max_streams_(max_allowed_incoming_streams),
36 // Advertised max starts at actual because it's communicated in the
37 // handshake.
38 incoming_advertised_max_streams_(max_allowed_incoming_streams),
39 incoming_initial_max_open_streams_(max_allowed_incoming_streams),
40 incoming_static_stream_count_(0),
41 incoming_stream_count_(0),
42 largest_peer_created_stream_id_(
43 QuicUtils::GetInvalidStreamId(transport_version())),
44 max_streams_window_(0) {
45 CalculateIncomingMaxStreamsWindow();
QUICHE teama6ef0a62019-03-07 20:34:33 -050046}
47
48QuicStreamIdManager::~QuicStreamIdManager() {
QUICHE teama6ef0a62019-03-07 20:34:33 -050049}
50
fkastenholz3c4eabf2019-04-22 07:49:59 -070051bool QuicStreamIdManager::OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) {
52 // Ensure that the frame has the correct directionality.
53 DCHECK_EQ(frame.unidirectional, unidirectional_);
54 QUIC_CODE_COUNT_N(quic_max_streams_received, 2, 2);
55 const QuicStreamCount current_outgoing_max_streams = outgoing_max_streams_;
56
57 // Set the limit to be exactly the stream count in the frame.
58 if (!ConfigureMaxOpenOutgoingStreams(frame.stream_count)) {
QUICHE teama6ef0a62019-03-07 20:34:33 -050059 return false;
60 }
fkastenholz3c4eabf2019-04-22 07:49:59 -070061 // If we were at the previous limit and this MAX_STREAMS frame
62 // increased the limit, inform the application that new streams are
63 // available.
64 if (outgoing_stream_count_ == current_outgoing_max_streams &&
65 current_outgoing_max_streams < outgoing_max_streams_) {
66 session_->OnCanCreateNewOutgoingStream();
QUICHE teama6ef0a62019-03-07 20:34:33 -050067 }
QUICHE teama6ef0a62019-03-07 20:34:33 -050068 return true;
69}
70
fkastenholz3c4eabf2019-04-22 07:49:59 -070071// The peer sends a streams blocked frame when it can not open any more
72// streams because it has runs into the limit.
73bool QuicStreamIdManager::OnStreamsBlockedFrame(
74 const QuicStreamsBlockedFrame& frame) {
75 // Ensure that the frame has the correct directionality.
76 DCHECK_EQ(frame.unidirectional, unidirectional_);
77 QUIC_CODE_COUNT_N(quic_streams_blocked_received, 2, 2);
QUICHE teama6ef0a62019-03-07 20:34:33 -050078
fkastenholz3c4eabf2019-04-22 07:49:59 -070079 if (frame.stream_count > incoming_advertised_max_streams_) {
QUICHE teama6ef0a62019-03-07 20:34:33 -050080 // Peer thinks it can send more streams that we've told it.
81 // This is a protocol error.
82 // TODO(fkastenholz): revise when proper IETF Connection Close support is
83 // done.
fkastenholz3c4eabf2019-04-22 07:49:59 -070084 QUIC_CODE_COUNT(quic_streams_blocked_too_big);
QUICHE teama6ef0a62019-03-07 20:34:33 -050085 session_->connection()->CloseConnection(
fkastenholz3c4eabf2019-04-22 07:49:59 -070086 QUIC_STREAMS_BLOCKED_ERROR, "Invalid stream count specified",
QUICHE teama6ef0a62019-03-07 20:34:33 -050087 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
88 return false;
89 }
fkastenholz3c4eabf2019-04-22 07:49:59 -070090 if (frame.stream_count < incoming_actual_max_streams_) {
91 // Peer thinks it's blocked on a stream count that is less than our current
92 // max. Inform the peer of the correct stream count. Sending a MAX_STREAMS
93 // frame in this case is not controlled by the window.
94 SendMaxStreamsFrame();
QUICHE teama6ef0a62019-03-07 20:34:33 -050095 }
fkastenholz3c4eabf2019-04-22 07:49:59 -070096 QUIC_CODE_COUNT(quic_streams_blocked_id_correct);
QUICHE teama6ef0a62019-03-07 20:34:33 -050097 return true;
98}
99
fkastenholz3c4eabf2019-04-22 07:49:59 -0700100// Used when configuration has been done and we have an initial
101// maximum stream count from the peer.
102bool QuicStreamIdManager::ConfigureMaxOpenOutgoingStreams(
103 size_t max_open_streams) {
104 if (using_default_max_streams_) {
105 // This is the first MAX_STREAMS/transport negotiation we've received. Treat
106 // this a bit differently than later ones. The difference is that
107 // outgoing_max_streams_ is currently an estimate. The MAX_STREAMS frame or
108 // transport negotiation is authoritative and can reduce
109 // outgoing_max_streams_ -- so long as outgoing_max_streams_ is not set to
110 // be less than the number of existing outgoing streams. If that happens,
111 // close the connection.
112 if (max_open_streams < outgoing_stream_count_) {
113 session_->connection()->CloseConnection(
114 QUIC_MAX_STREAMS_ERROR,
115 "Stream limit less than existing stream count",
116 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
117 return false;
118 }
119 using_default_max_streams_ = false;
120 } else if (max_open_streams <= outgoing_max_streams_) {
121 // Is not the 1st MAX_STREAMS or negotiation.
122 // Only update the stream count if it would increase the limit.
123 // If it decreases the limit, or doesn't change it, then do not update.
124 // Note that this handles the case of receiving a count of 0 in the frame
125 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500126 }
127
fkastenholz3c4eabf2019-04-22 07:49:59 -0700128 // This implementation only supports 32 bit Stream IDs, so limit max streams
129 // if it would exceed the max 32 bits can express.
nharperfc7c10e2019-05-21 03:44:53 -0700130 outgoing_max_streams_ = std::min<size_t>(
131 max_open_streams,
fkastenholz3c4eabf2019-04-22 07:49:59 -0700132 QuicUtils::GetMaxStreamCount(unidirectional_, session_->perspective()));
fkastenholz3c4eabf2019-04-22 07:49:59 -0700133 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500134}
135
fkastenholz3c4eabf2019-04-22 07:49:59 -0700136void QuicStreamIdManager::SetMaxOpenOutgoingStreams(size_t max_open_streams) {
137 QUIC_BUG_IF(!using_default_max_streams_);
fkastenholzd3a1de92019-05-15 07:00:07 -0700138 // TODO(fkastenholz): when static streams are removed from I-Quic, this
139 // should be revised to invoke ConfigureMaxOpen...
fkastenholz3c4eabf2019-04-22 07:49:59 -0700140 AdjustMaxOpenOutgoingStreams(max_open_streams);
141}
142
143// Adjust the outgoing stream limit - max_open_streams is the limit, not
144// including static streams. If the new stream limit wraps, will peg
145// the limit at the implementation max.
146// TODO(fkastenholz): AdjustMax is cognizant of the number of static streams and
fkastenholzd3a1de92019-05-15 07:00:07 -0700147// sets the maximum to be max_streams + number_of_statics. This should be
148// removed from IETF QUIC when static streams are gone.
fkastenholz3c4eabf2019-04-22 07:49:59 -0700149void QuicStreamIdManager::AdjustMaxOpenOutgoingStreams(
150 size_t max_open_streams) {
151 if ((outgoing_static_stream_count_ + max_open_streams) < max_open_streams) {
152 // New limit causes us to wrap, set limit to be the implementation maximum.
153 ConfigureMaxOpenOutgoingStreams(
154 QuicUtils::GetMaxStreamCount(unidirectional_, perspective()));
155 return;
156 }
157 // Does not wrap, set limit to what is requested.
158 ConfigureMaxOpenOutgoingStreams(outgoing_static_stream_count_ +
159 max_open_streams);
160}
161
162void QuicStreamIdManager::SetMaxOpenIncomingStreams(size_t max_open_streams) {
163 QuicStreamCount implementation_max =
164 QuicUtils::GetMaxStreamCount(unidirectional_, perspective());
165 QuicStreamCount new_max =
166 std::min(implementation_max,
167 static_cast<QuicStreamCount>(max_open_streams +
168 incoming_static_stream_count_));
169 if (new_max < max_open_streams) {
170 // wrapped around ...
171 new_max = implementation_max;
172 }
173 if (new_max < incoming_stream_count_) {
174 session_->connection()->CloseConnection(
175 QUIC_MAX_STREAMS_ERROR, "Stream limit less than existing stream count",
176 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
177 return;
178 }
179 incoming_actual_max_streams_ = new_max;
180 incoming_advertised_max_streams_ = new_max;
181 incoming_initial_max_open_streams_ =
182 std::min(max_open_streams, static_cast<size_t>(implementation_max));
183 CalculateIncomingMaxStreamsWindow();
184}
185
186void QuicStreamIdManager::MaybeSendMaxStreamsFrame() {
187 if ((incoming_advertised_max_streams_ - incoming_stream_count_) >
188 max_streams_window_) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500189 // window too large, no advertisement
190 return;
191 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700192 SendMaxStreamsFrame();
QUICHE teama6ef0a62019-03-07 20:34:33 -0500193}
194
fkastenholz3c4eabf2019-04-22 07:49:59 -0700195void QuicStreamIdManager::SendMaxStreamsFrame() {
196 incoming_advertised_max_streams_ = incoming_actual_max_streams_;
197 session_->SendMaxStreams(incoming_advertised_max_streams_, unidirectional_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500198}
199
200void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700201 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500202 if (!IsIncomingStream(stream_id)) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700203 // Nothing to do for outgoing streams.
QUICHE teama6ef0a62019-03-07 20:34:33 -0500204 return;
205 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700206 // If the stream is inbound, we can increase the actual stream limit and maybe
207 // advertise the new limit to the peer. Have to check to make sure that we do
208 // not exceed the maximum.
209 if (incoming_actual_max_streams_ ==
210 QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500211 // Reached the maximum stream id value that the implementation
212 // supports. Nothing can be done here.
213 return;
214 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700215 // One stream closed ... another can be opened.
216 incoming_actual_max_streams_++;
217 MaybeSendMaxStreamsFrame();
QUICHE teama6ef0a62019-03-07 20:34:33 -0500218}
219
220QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700221 // TODO(fkastenholz): Should we close the connection?
222 QUIC_BUG_IF(outgoing_stream_count_ >= outgoing_max_streams_)
223 << "Attempt to allocate a new outgoing stream that would exceed the "
224 "limit";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500225 QuicStreamId id = next_outgoing_stream_id_;
fkastenholz3c4eabf2019-04-22 07:49:59 -0700226 next_outgoing_stream_id_ += QuicUtils::StreamIdDelta(transport_version());
227 outgoing_stream_count_++;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500228 return id;
229}
230
231bool QuicStreamIdManager::CanOpenNextOutgoingStream() {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700232 DCHECK_EQ(QUIC_VERSION_99, transport_version());
233 if (outgoing_stream_count_ < outgoing_max_streams_) {
234 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500235 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700236 // Next stream ID would exceed the limit, need to inform the peer.
237 session_->SendStreamsBlocked(outgoing_max_streams_, unidirectional_);
238 QUIC_CODE_COUNT(quic_reached_outgoing_stream_id_limit);
239 return false;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500240}
241
fkastenholz3c4eabf2019-04-22 07:49:59 -0700242bool QuicStreamIdManager::RegisterStaticStream(QuicStreamId stream_id) {
243 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_);
244 if (IsIncomingStream(stream_id)) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500245 // This code is predicated on static stream ids being allocated densely, in
246 // order, and starting with the first stream allowed. QUIC_BUG if this is
247 // not so.
QUICHE teama6ef0a62019-03-07 20:34:33 -0500248 // This is a stream id for a stream that is started by the peer, deal with
249 // the incoming stream ids. Increase the floor and adjust everything
250 // accordingly.
fkastenholz3c4eabf2019-04-22 07:49:59 -0700251
252 QUIC_BUG_IF(incoming_actual_max_streams_ >
253 QuicUtils::GetMaxStreamCount(unidirectional_, perspective()));
254
255 // If we have reached the limit on stream creation, do not create
256 // the static stream; return false.
257 if (incoming_stream_count_ >=
258 QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) {
259 return false;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500260 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700261
262 if (incoming_actual_max_streams_ <
263 QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) {
264 incoming_actual_max_streams_++;
265 }
266 if (incoming_advertised_max_streams_ <
267 QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) {
268 incoming_advertised_max_streams_++;
269 }
270 incoming_stream_count_++;
271 incoming_static_stream_count_++;
272 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500273 }
274
fkastenholz3c4eabf2019-04-22 07:49:59 -0700275 QUIC_BUG_IF(!using_default_max_streams_)
276 << "Attempted to allocate static stream (id " << stream_id
277 << ") after receiving a MAX_STREAMS frame";
278
279 // If we have reached the limit on stream creation, do not create
280 // the static stream; return false.
281 if (outgoing_max_streams_ >=
282 QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) {
283 return false;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500284 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700285
fkastenholz3c4eabf2019-04-22 07:49:59 -0700286 // Increase the outgoing_max_streams_ limit to reflect the semantic that
287 // outgoing_max_streams_ was inialized to a "maximum request/response" count
288 // and only becomes a maximum stream count when we receive the first
289 // MAX_STREAMS.
290 outgoing_max_streams_++;
291 outgoing_static_stream_count_++;
292 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500293}
294
fkastenholz3c4eabf2019-04-22 07:49:59 -0700295// Stream_id is the id of a new incoming stream. Check if it can be
296// created (doesn't violate limits, etc).
QUICHE teama6ef0a62019-03-07 20:34:33 -0500297bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
298 const QuicStreamId stream_id) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700299 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_);
300
QUICHE teama6ef0a62019-03-07 20:34:33 -0500301 available_streams_.erase(stream_id);
302
303 if (largest_peer_created_stream_id_ !=
fkastenholz3c4eabf2019-04-22 07:49:59 -0700304 QuicUtils::GetInvalidStreamId(transport_version()) &&
QUICHE teama6ef0a62019-03-07 20:34:33 -0500305 stream_id <= largest_peer_created_stream_id_) {
306 return true;
307 }
308
fkastenholz3c4eabf2019-04-22 07:49:59 -0700309 QuicStreamCount stream_count_increment;
310 if (largest_peer_created_stream_id_ !=
311 QuicUtils::GetInvalidStreamId(transport_version())) {
312 stream_count_increment = (stream_id - largest_peer_created_stream_id_) /
313 QuicUtils::StreamIdDelta(transport_version());
314 } else {
315 // Largest_peer_created_stream_id is the invalid ID,
316 // which means that the peer has not created any stream IDs.
317 // The "+1" is because the first stream ID has not yet
318 // been used. For example, if the FirstIncoming ID is 1
319 // and stream_id is 1, then we want the increment to be 1.
320 stream_count_increment = ((stream_id - GetFirstIncomingStreamId()) /
321 QuicUtils::StreamIdDelta(transport_version())) +
322 1;
323 }
324
325 // If already at, or over, the limit, close the connection/etc.
326 if (((incoming_stream_count_ + stream_count_increment) >
327 incoming_advertised_max_streams_) ||
328 ((incoming_stream_count_ + stream_count_increment) <
329 incoming_stream_count_)) {
330 // This stream would exceed the limit. do not increase.
QUICHE teama6ef0a62019-03-07 20:34:33 -0500331 QUIC_DLOG(INFO) << ENDPOINT
332 << "Failed to create a new incoming stream with id:"
fkastenholz3c4eabf2019-04-22 07:49:59 -0700333 << stream_id << ", reaching MAX_STREAMS limit: "
334 << incoming_advertised_max_streams_ << ".";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500335 session_->connection()->CloseConnection(
336 QUIC_INVALID_STREAM_ID,
fkastenholz3c4eabf2019-04-22 07:49:59 -0700337 QuicStrCat("Stream id ", stream_id, " would exceed stream count limit ",
338 incoming_advertised_max_streams_),
QUICHE teama6ef0a62019-03-07 20:34:33 -0500339 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
340 return false;
341 }
342
fkastenholz3c4eabf2019-04-22 07:49:59 -0700343 QuicStreamId id = GetFirstIncomingStreamId();
344 if (largest_peer_created_stream_id_ !=
345 QuicUtils::GetInvalidStreamId(transport_version())) {
346 id = largest_peer_created_stream_id_ +
347 QuicUtils::StreamIdDelta(transport_version());
QUICHE teama6ef0a62019-03-07 20:34:33 -0500348 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700349
350 for (; id < stream_id; id += QuicUtils::StreamIdDelta(transport_version())) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500351 available_streams_.insert(id);
352 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700353 incoming_stream_count_ += stream_count_increment;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500354 largest_peer_created_stream_id_ = stream_id;
355 return true;
356}
357
358bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700359 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id), unidirectional_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500360 if (!IsIncomingStream(id)) {
361 // Stream IDs under next_ougoing_stream_id_ are either open or previously
362 // open but now closed.
363 return id >= next_outgoing_stream_id_;
364 }
365 // For peer created streams, we also need to consider available streams.
366 return largest_peer_created_stream_id_ ==
fkastenholz3c4eabf2019-04-22 07:49:59 -0700367 QuicUtils::GetInvalidStreamId(transport_version()) ||
QUICHE teama6ef0a62019-03-07 20:34:33 -0500368 id > largest_peer_created_stream_id_ ||
369 QuicContainsKey(available_streams_, id);
370}
371
372bool QuicStreamIdManager::IsIncomingStream(QuicStreamId id) const {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700373 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id), unidirectional_);
374 // The 0x1 bit in the stream id indicates whether the stream id is
375 // server- or client- initiated. Next_OUTGOING_stream_id_ has that bit
376 // set based on whether this node is a server or client. Thus, if the stream
377 // id in question has the 0x1 bit set opposite of next_OUTGOING_stream_id_,
378 // then that stream id is incoming -- it is for streams initiated by the peer.
379 return (id & 0x1) != (next_outgoing_stream_id_ & 0x1);
380}
381
382QuicStreamId QuicStreamIdManager::GetFirstOutgoingStreamId() const {
383 return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
384 transport_version(), perspective())
385 : QuicUtils::GetFirstBidirectionalStreamId(
386 transport_version(), perspective());
387}
388
389QuicStreamId QuicStreamIdManager::GetFirstIncomingStreamId() const {
390 return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
391 transport_version(), peer_perspective())
392 : QuicUtils::GetFirstBidirectionalStreamId(
393 transport_version(), peer_perspective());
394}
395
396Perspective QuicStreamIdManager::perspective() const {
397 return session_->perspective();
398}
399
400Perspective QuicStreamIdManager::peer_perspective() const {
401 return (perspective() == Perspective::IS_SERVER) ? Perspective::IS_CLIENT
402 : Perspective::IS_SERVER;
403}
404
405QuicTransportVersion QuicStreamIdManager::transport_version() const {
406 return session_->connection()->transport_version();
407}
408
409size_t QuicStreamIdManager::available_incoming_streams() {
410 return incoming_advertised_max_streams_ - incoming_stream_count_;
411}
412
413void QuicStreamIdManager::CalculateIncomingMaxStreamsWindow() {
414 max_streams_window_ = incoming_actual_max_streams_ / kMaxStreamsWindowDivisor;
415 if (max_streams_window_ == 0) {
416 max_streams_window_ = 1;
417 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500418}
419
420} // namespace quic