blob: 8b8ff90fbc4d03e78faac174183aa08bbbd31d52 [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() {
fkastenholz305e1732019-06-18 05:01:22 -0700232 DCHECK(VersionHasIetfQuicFrames(transport_version()));
fkastenholz3c4eabf2019-04-22 07:49:59 -0700233 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
renjietang3a1bb802019-06-11 10:42:41 -0700242bool QuicStreamIdManager::RegisterStaticStream(QuicStreamId stream_id,
243 bool stream_already_counted) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700244 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_);
245 if (IsIncomingStream(stream_id)) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500246 // This code is predicated on static stream ids being allocated densely, in
247 // order, and starting with the first stream allowed. QUIC_BUG if this is
248 // not so.
QUICHE teama6ef0a62019-03-07 20:34:33 -0500249 // This is a stream id for a stream that is started by the peer, deal with
250 // the incoming stream ids. Increase the floor and adjust everything
251 // accordingly.
fkastenholz3c4eabf2019-04-22 07:49:59 -0700252
253 QUIC_BUG_IF(incoming_actual_max_streams_ >
254 QuicUtils::GetMaxStreamCount(unidirectional_, perspective()));
255
256 // If we have reached the limit on stream creation, do not create
257 // the static stream; return false.
258 if (incoming_stream_count_ >=
259 QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) {
260 return false;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500261 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700262
263 if (incoming_actual_max_streams_ <
264 QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) {
265 incoming_actual_max_streams_++;
266 }
267 if (incoming_advertised_max_streams_ <
268 QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) {
269 incoming_advertised_max_streams_++;
270 }
renjietang3a1bb802019-06-11 10:42:41 -0700271
272 if (!stream_already_counted) {
273 incoming_stream_count_++;
274 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700275 incoming_static_stream_count_++;
276 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500277 }
278
fkastenholz3c4eabf2019-04-22 07:49:59 -0700279 QUIC_BUG_IF(!using_default_max_streams_)
280 << "Attempted to allocate static stream (id " << stream_id
281 << ") after receiving a MAX_STREAMS frame";
282
283 // If we have reached the limit on stream creation, do not create
284 // the static stream; return false.
285 if (outgoing_max_streams_ >=
286 QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) {
287 return false;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500288 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700289
fkastenholz3c4eabf2019-04-22 07:49:59 -0700290 // Increase the outgoing_max_streams_ limit to reflect the semantic that
291 // outgoing_max_streams_ was inialized to a "maximum request/response" count
292 // and only becomes a maximum stream count when we receive the first
293 // MAX_STREAMS.
294 outgoing_max_streams_++;
295 outgoing_static_stream_count_++;
296 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500297}
298
fkastenholz3c4eabf2019-04-22 07:49:59 -0700299// Stream_id is the id of a new incoming stream. Check if it can be
300// created (doesn't violate limits, etc).
QUICHE teama6ef0a62019-03-07 20:34:33 -0500301bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
302 const QuicStreamId stream_id) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700303 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_);
304
QUICHE teama6ef0a62019-03-07 20:34:33 -0500305 available_streams_.erase(stream_id);
306
307 if (largest_peer_created_stream_id_ !=
fkastenholz3c4eabf2019-04-22 07:49:59 -0700308 QuicUtils::GetInvalidStreamId(transport_version()) &&
QUICHE teama6ef0a62019-03-07 20:34:33 -0500309 stream_id <= largest_peer_created_stream_id_) {
310 return true;
311 }
312
fkastenholz3c4eabf2019-04-22 07:49:59 -0700313 QuicStreamCount stream_count_increment;
314 if (largest_peer_created_stream_id_ !=
315 QuicUtils::GetInvalidStreamId(transport_version())) {
316 stream_count_increment = (stream_id - largest_peer_created_stream_id_) /
317 QuicUtils::StreamIdDelta(transport_version());
318 } else {
319 // Largest_peer_created_stream_id is the invalid ID,
320 // which means that the peer has not created any stream IDs.
321 // The "+1" is because the first stream ID has not yet
322 // been used. For example, if the FirstIncoming ID is 1
323 // and stream_id is 1, then we want the increment to be 1.
324 stream_count_increment = ((stream_id - GetFirstIncomingStreamId()) /
325 QuicUtils::StreamIdDelta(transport_version())) +
326 1;
327 }
328
329 // If already at, or over, the limit, close the connection/etc.
330 if (((incoming_stream_count_ + stream_count_increment) >
331 incoming_advertised_max_streams_) ||
332 ((incoming_stream_count_ + stream_count_increment) <
333 incoming_stream_count_)) {
334 // This stream would exceed the limit. do not increase.
QUICHE teama6ef0a62019-03-07 20:34:33 -0500335 QUIC_DLOG(INFO) << ENDPOINT
336 << "Failed to create a new incoming stream with id:"
fkastenholz3c4eabf2019-04-22 07:49:59 -0700337 << stream_id << ", reaching MAX_STREAMS limit: "
338 << incoming_advertised_max_streams_ << ".";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500339 session_->connection()->CloseConnection(
340 QUIC_INVALID_STREAM_ID,
fkastenholz3c4eabf2019-04-22 07:49:59 -0700341 QuicStrCat("Stream id ", stream_id, " would exceed stream count limit ",
342 incoming_advertised_max_streams_),
QUICHE teama6ef0a62019-03-07 20:34:33 -0500343 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
344 return false;
345 }
346
fkastenholz3c4eabf2019-04-22 07:49:59 -0700347 QuicStreamId id = GetFirstIncomingStreamId();
348 if (largest_peer_created_stream_id_ !=
349 QuicUtils::GetInvalidStreamId(transport_version())) {
350 id = largest_peer_created_stream_id_ +
351 QuicUtils::StreamIdDelta(transport_version());
QUICHE teama6ef0a62019-03-07 20:34:33 -0500352 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700353
354 for (; id < stream_id; id += QuicUtils::StreamIdDelta(transport_version())) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500355 available_streams_.insert(id);
356 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700357 incoming_stream_count_ += stream_count_increment;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500358 largest_peer_created_stream_id_ = stream_id;
359 return true;
360}
361
362bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700363 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id), unidirectional_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500364 if (!IsIncomingStream(id)) {
365 // Stream IDs under next_ougoing_stream_id_ are either open or previously
366 // open but now closed.
367 return id >= next_outgoing_stream_id_;
368 }
369 // For peer created streams, we also need to consider available streams.
370 return largest_peer_created_stream_id_ ==
fkastenholz3c4eabf2019-04-22 07:49:59 -0700371 QuicUtils::GetInvalidStreamId(transport_version()) ||
QUICHE teama6ef0a62019-03-07 20:34:33 -0500372 id > largest_peer_created_stream_id_ ||
373 QuicContainsKey(available_streams_, id);
374}
375
376bool QuicStreamIdManager::IsIncomingStream(QuicStreamId id) const {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700377 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id), unidirectional_);
378 // The 0x1 bit in the stream id indicates whether the stream id is
379 // server- or client- initiated. Next_OUTGOING_stream_id_ has that bit
380 // set based on whether this node is a server or client. Thus, if the stream
381 // id in question has the 0x1 bit set opposite of next_OUTGOING_stream_id_,
382 // then that stream id is incoming -- it is for streams initiated by the peer.
383 return (id & 0x1) != (next_outgoing_stream_id_ & 0x1);
384}
385
386QuicStreamId QuicStreamIdManager::GetFirstOutgoingStreamId() const {
387 return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
388 transport_version(), perspective())
389 : QuicUtils::GetFirstBidirectionalStreamId(
390 transport_version(), perspective());
391}
392
393QuicStreamId QuicStreamIdManager::GetFirstIncomingStreamId() const {
394 return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
395 transport_version(), peer_perspective())
396 : QuicUtils::GetFirstBidirectionalStreamId(
397 transport_version(), peer_perspective());
398}
399
400Perspective QuicStreamIdManager::perspective() const {
401 return session_->perspective();
402}
403
404Perspective QuicStreamIdManager::peer_perspective() const {
nharper4eba09b2019-06-26 20:17:25 -0700405 return QuicUtils::InvertPerspective(perspective());
fkastenholz3c4eabf2019-04-22 07:49:59 -0700406}
407
408QuicTransportVersion QuicStreamIdManager::transport_version() const {
409 return session_->connection()->transport_version();
410}
411
412size_t QuicStreamIdManager::available_incoming_streams() {
413 return incoming_advertised_max_streams_ - incoming_stream_count_;
414}
415
416void QuicStreamIdManager::CalculateIncomingMaxStreamsWindow() {
417 max_streams_window_ = incoming_actual_max_streams_ / kMaxStreamsWindowDivisor;
418 if (max_streams_window_ == 0) {
419 max_streams_window_ = 1;
420 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500421}
422
423} // namespace quic