blob: 14aa8e90ac56a83b156749ccad9ed8f951958378 [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 using_default_max_streams_(true),
38 incoming_actual_max_streams_(max_allowed_incoming_streams),
39 // Advertised max starts at actual because it's communicated in the
40 // handshake.
41 incoming_advertised_max_streams_(max_allowed_incoming_streams),
42 incoming_initial_max_open_streams_(max_allowed_incoming_streams),
fkastenholz3c4eabf2019-04-22 07:49:59 -070043 incoming_stream_count_(0),
44 largest_peer_created_stream_id_(
rcha8b56e42019-09-20 10:41:48 -070045 QuicUtils::GetInvalidStreamId(transport_version)),
fkastenholz56055be2019-09-17 11:17:37 -070046 max_streams_window_(0),
47 pending_max_streams_(false),
48 pending_streams_blocked_(
rcha8b56e42019-09-20 10:41:48 -070049 QuicUtils::GetInvalidStreamId(transport_version)) {
fkastenholz3c4eabf2019-04-22 07:49:59 -070050 CalculateIncomingMaxStreamsWindow();
QUICHE teama6ef0a62019-03-07 20:34:33 -050051}
52
rcha8b56e42019-09-20 10:41:48 -070053QuicStreamIdManager::~QuicStreamIdManager() {}
QUICHE teama6ef0a62019-03-07 20:34:33 -050054
fkastenholz3c4eabf2019-04-22 07:49:59 -070055bool QuicStreamIdManager::OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) {
56 // Ensure that the frame has the correct directionality.
57 DCHECK_EQ(frame.unidirectional, unidirectional_);
58 QUIC_CODE_COUNT_N(quic_max_streams_received, 2, 2);
fkastenholz3c4eabf2019-04-22 07:49:59 -070059
60 // Set the limit to be exactly the stream count in the frame.
fkastenholz56055be2019-09-17 11:17:37 -070061 // Also informs the higher layers that they can create more
62 // streams if the limit is increased.
63 return SetMaxOpenOutgoingStreams(frame.stream_count);
QUICHE teama6ef0a62019-03-07 20:34:33 -050064}
65
fkastenholz3c4eabf2019-04-22 07:49:59 -070066// The peer sends a streams blocked frame when it can not open any more
67// streams because it has runs into the limit.
68bool QuicStreamIdManager::OnStreamsBlockedFrame(
69 const QuicStreamsBlockedFrame& frame) {
70 // Ensure that the frame has the correct directionality.
71 DCHECK_EQ(frame.unidirectional, unidirectional_);
fkastenholz3c4eabf2019-04-22 07:49:59 -070072 if (frame.stream_count > incoming_advertised_max_streams_) {
QUICHE teama6ef0a62019-03-07 20:34:33 -050073 // Peer thinks it can send more streams that we've told it.
74 // This is a protocol error.
fkastenholz3c4eabf2019-04-22 07:49:59 -070075 QUIC_CODE_COUNT(quic_streams_blocked_too_big);
rcha8b56e42019-09-20 10:41:48 -070076 delegate_->OnError(QUIC_STREAMS_BLOCKED_ERROR,
77 "Invalid stream count specified");
QUICHE teama6ef0a62019-03-07 20:34:33 -050078 return false;
79 }
fkastenholz3c4eabf2019-04-22 07:49:59 -070080 if (frame.stream_count < incoming_actual_max_streams_) {
81 // Peer thinks it's blocked on a stream count that is less than our current
82 // max. Inform the peer of the correct stream count. Sending a MAX_STREAMS
83 // frame in this case is not controlled by the window.
84 SendMaxStreamsFrame();
QUICHE teama6ef0a62019-03-07 20:34:33 -050085 }
fkastenholz3c4eabf2019-04-22 07:49:59 -070086 QUIC_CODE_COUNT(quic_streams_blocked_id_correct);
QUICHE teama6ef0a62019-03-07 20:34:33 -050087 return true;
88}
89
fkastenholz3c4eabf2019-04-22 07:49:59 -070090// Used when configuration has been done and we have an initial
91// maximum stream count from the peer.
renjietang52e13382019-12-16 15:58:04 -080092bool QuicStreamIdManager::SetMaxOpenOutgoingStreams(
93 QuicStreamCount max_open_streams) {
fkastenholz3c4eabf2019-04-22 07:49:59 -070094 if (using_default_max_streams_) {
95 // This is the first MAX_STREAMS/transport negotiation we've received. Treat
96 // this a bit differently than later ones. The difference is that
97 // outgoing_max_streams_ is currently an estimate. The MAX_STREAMS frame or
98 // transport negotiation is authoritative and can reduce
99 // outgoing_max_streams_ -- so long as outgoing_max_streams_ is not set to
100 // be less than the number of existing outgoing streams. If that happens,
101 // close the connection.
102 if (max_open_streams < outgoing_stream_count_) {
rcha8b56e42019-09-20 10:41:48 -0700103 delegate_->OnError(QUIC_MAX_STREAMS_ERROR,
104 "Stream limit less than existing stream count");
fkastenholz3c4eabf2019-04-22 07:49:59 -0700105 return false;
106 }
107 using_default_max_streams_ = false;
108 } else if (max_open_streams <= outgoing_max_streams_) {
109 // Is not the 1st MAX_STREAMS or negotiation.
110 // Only update the stream count if it would increase the limit.
111 // If it decreases the limit, or doesn't change it, then do not update.
112 // Note that this handles the case of receiving a count of 0 in the frame
113 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500114 }
115
fkastenholz3c4eabf2019-04-22 07:49:59 -0700116 // This implementation only supports 32 bit Stream IDs, so limit max streams
117 // if it would exceed the max 32 bits can express.
renjietang52e13382019-12-16 15:58:04 -0800118 outgoing_max_streams_ =
119 std::min(max_open_streams,
120 QuicUtils::GetMaxStreamCount(unidirectional_, perspective_));
fkastenholz56055be2019-09-17 11:17:37 -0700121
122 // Inform the higher layers that the stream limit has increased and that
123 // new streams may be created.
rcha8b56e42019-09-20 10:41:48 -0700124 delegate_->OnCanCreateNewOutgoingStream(unidirectional_);
fkastenholz56055be2019-09-17 11:17:37 -0700125
fkastenholz3c4eabf2019-04-22 07:49:59 -0700126 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500127}
128
renjietang52e13382019-12-16 15:58:04 -0800129void QuicStreamIdManager::SetMaxOpenIncomingStreams(
130 QuicStreamCount max_open_streams) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700131 QuicStreamCount implementation_max =
132 QuicUtils::GetMaxStreamCount(unidirectional_, perspective());
renjietang52e13382019-12-16 15:58:04 -0800133 QuicStreamCount new_max = std::min(implementation_max, max_open_streams);
fkastenholz3c4eabf2019-04-22 07:49:59 -0700134 if (new_max < incoming_stream_count_) {
rcha8b56e42019-09-20 10:41:48 -0700135 delegate_->OnError(QUIC_MAX_STREAMS_ERROR,
136 "Stream limit less than existing stream count");
fkastenholz3c4eabf2019-04-22 07:49:59 -0700137 return;
138 }
139 incoming_actual_max_streams_ = new_max;
140 incoming_advertised_max_streams_ = new_max;
141 incoming_initial_max_open_streams_ =
renjietang52e13382019-12-16 15:58:04 -0800142 std::min(max_open_streams, implementation_max);
fkastenholz3c4eabf2019-04-22 07:49:59 -0700143 CalculateIncomingMaxStreamsWindow();
144}
145
146void QuicStreamIdManager::MaybeSendMaxStreamsFrame() {
147 if ((incoming_advertised_max_streams_ - incoming_stream_count_) >
148 max_streams_window_) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500149 // window too large, no advertisement
150 return;
151 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700152 SendMaxStreamsFrame();
QUICHE teama6ef0a62019-03-07 20:34:33 -0500153}
154
fkastenholz3c4eabf2019-04-22 07:49:59 -0700155void QuicStreamIdManager::SendMaxStreamsFrame() {
rcha8b56e42019-09-20 10:41:48 -0700156 if (!is_config_negotiated_) {
157 // The config has not yet been negotiated, so we can not send the
158 // MAX STREAMS frame yet. Record that we would have sent one and then
159 // return. A new frame will be generated once the configuration is
160 // received.
fkastenholz56055be2019-09-17 11:17:37 -0700161 pending_max_streams_ = true;
162 return;
163 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700164 incoming_advertised_max_streams_ = incoming_actual_max_streams_;
rcha8b56e42019-09-20 10:41:48 -0700165 delegate_->SendMaxStreams(incoming_advertised_max_streams_, unidirectional_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500166}
167
168void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700169 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500170 if (!IsIncomingStream(stream_id)) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700171 // Nothing to do for outgoing streams.
QUICHE teama6ef0a62019-03-07 20:34:33 -0500172 return;
173 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700174 // If the stream is inbound, we can increase the actual stream limit and maybe
175 // advertise the new limit to the peer. Have to check to make sure that we do
176 // not exceed the maximum.
177 if (incoming_actual_max_streams_ ==
178 QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500179 // Reached the maximum stream id value that the implementation
180 // supports. Nothing can be done here.
181 return;
182 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700183 // One stream closed ... another can be opened.
184 incoming_actual_max_streams_++;
185 MaybeSendMaxStreamsFrame();
QUICHE teama6ef0a62019-03-07 20:34:33 -0500186}
187
188QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() {
renjietangab93f582019-12-13 10:29:31 -0800189 // Applications should always consult CanOpenNextOutgoingStream() first.
190 // If they ask for stream ids that violate the limit, it's an implementation
191 // bug.
fkastenholz3c4eabf2019-04-22 07:49:59 -0700192 QUIC_BUG_IF(outgoing_stream_count_ >= outgoing_max_streams_)
193 << "Attempt to allocate a new outgoing stream that would exceed the "
fkastenholz9b4b0ad2019-08-20 05:10:40 -0700194 "limit ("
195 << outgoing_max_streams_ << ")";
QUICHE teama6ef0a62019-03-07 20:34:33 -0500196 QuicStreamId id = next_outgoing_stream_id_;
renjietangab93f582019-12-13 10:29:31 -0800197 next_outgoing_stream_id_ += QuicUtils::StreamIdDelta(transport_version_);
fkastenholz3c4eabf2019-04-22 07:49:59 -0700198 outgoing_stream_count_++;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500199 return id;
200}
201
202bool QuicStreamIdManager::CanOpenNextOutgoingStream() {
renjietangab93f582019-12-13 10:29:31 -0800203 DCHECK(VersionHasIetfQuicFrames(transport_version_));
fkastenholz3c4eabf2019-04-22 07:49:59 -0700204 if (outgoing_stream_count_ < outgoing_max_streams_) {
205 return true;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500206 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700207 // Next stream ID would exceed the limit, need to inform the peer.
fkastenholz56055be2019-09-17 11:17:37 -0700208
rcha8b56e42019-09-20 10:41:48 -0700209 if (!is_config_negotiated_) {
210 // The config is not negotiated, so we can not send the STREAMS_BLOCKED
211 // frame yet. Record that we would have sent one, and what the limit was
212 // when we were blocked, and return.
fkastenholz56055be2019-09-17 11:17:37 -0700213 pending_streams_blocked_ = outgoing_max_streams_;
214 return false;
215 }
rcha8b56e42019-09-20 10:41:48 -0700216 delegate_->SendStreamsBlocked(outgoing_max_streams_, unidirectional_);
fkastenholz3c4eabf2019-04-22 07:49:59 -0700217 QUIC_CODE_COUNT(quic_reached_outgoing_stream_id_limit);
218 return false;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500219}
220
fkastenholz3c4eabf2019-04-22 07:49:59 -0700221// Stream_id is the id of a new incoming stream. Check if it can be
222// created (doesn't violate limits, etc).
QUICHE teama6ef0a62019-03-07 20:34:33 -0500223bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
224 const QuicStreamId stream_id) {
rch9301d3c2019-09-20 14:30:48 -0700225 // |stream_id| must be an incoming stream of the right directionality.
fkastenholz3c4eabf2019-04-22 07:49:59 -0700226 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_);
renjietangab93f582019-12-13 10:29:31 -0800227 DCHECK_NE(QuicUtils::IsServerInitiatedStreamId(transport_version_, stream_id),
228 perspective() == Perspective::IS_SERVER);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500229 available_streams_.erase(stream_id);
230
231 if (largest_peer_created_stream_id_ !=
renjietangab93f582019-12-13 10:29:31 -0800232 QuicUtils::GetInvalidStreamId(transport_version_) &&
QUICHE teama6ef0a62019-03-07 20:34:33 -0500233 stream_id <= largest_peer_created_stream_id_) {
234 return true;
235 }
236
fkastenholz3c4eabf2019-04-22 07:49:59 -0700237 QuicStreamCount stream_count_increment;
238 if (largest_peer_created_stream_id_ !=
renjietangab93f582019-12-13 10:29:31 -0800239 QuicUtils::GetInvalidStreamId(transport_version_)) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700240 stream_count_increment = (stream_id - largest_peer_created_stream_id_) /
renjietangab93f582019-12-13 10:29:31 -0800241 QuicUtils::StreamIdDelta(transport_version_);
fkastenholz3c4eabf2019-04-22 07:49:59 -0700242 } else {
243 // Largest_peer_created_stream_id is the invalid ID,
244 // which means that the peer has not created any stream IDs.
245 // The "+1" is because the first stream ID has not yet
246 // been used. For example, if the FirstIncoming ID is 1
247 // and stream_id is 1, then we want the increment to be 1.
248 stream_count_increment = ((stream_id - GetFirstIncomingStreamId()) /
renjietangab93f582019-12-13 10:29:31 -0800249 QuicUtils::StreamIdDelta(transport_version_)) +
fkastenholz3c4eabf2019-04-22 07:49:59 -0700250 1;
251 }
252
253 // If already at, or over, the limit, close the connection/etc.
254 if (((incoming_stream_count_ + stream_count_increment) >
255 incoming_advertised_max_streams_) ||
256 ((incoming_stream_count_ + stream_count_increment) <
257 incoming_stream_count_)) {
258 // This stream would exceed the limit. do not increase.
QUICHE teama6ef0a62019-03-07 20:34:33 -0500259 QUIC_DLOG(INFO) << ENDPOINT
260 << "Failed to create a new incoming stream with id:"
fkastenholz3c4eabf2019-04-22 07:49:59 -0700261 << stream_id << ", reaching MAX_STREAMS limit: "
262 << incoming_advertised_max_streams_ << ".";
dmcardlecf0bfcf2019-12-13 08:08:21 -0800263 delegate_->OnError(QUIC_INVALID_STREAM_ID,
264 quiche::QuicheStrCat("Stream id ", stream_id,
265 " would exceed stream count limit ",
266 incoming_advertised_max_streams_));
QUICHE teama6ef0a62019-03-07 20:34:33 -0500267 return false;
268 }
269
fkastenholz3c4eabf2019-04-22 07:49:59 -0700270 QuicStreamId id = GetFirstIncomingStreamId();
271 if (largest_peer_created_stream_id_ !=
renjietangab93f582019-12-13 10:29:31 -0800272 QuicUtils::GetInvalidStreamId(transport_version_)) {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700273 id = largest_peer_created_stream_id_ +
renjietangab93f582019-12-13 10:29:31 -0800274 QuicUtils::StreamIdDelta(transport_version_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500275 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700276
renjietangab93f582019-12-13 10:29:31 -0800277 for (; id < stream_id; id += QuicUtils::StreamIdDelta(transport_version_)) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500278 available_streams_.insert(id);
279 }
fkastenholz3c4eabf2019-04-22 07:49:59 -0700280 incoming_stream_count_ += stream_count_increment;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500281 largest_peer_created_stream_id_ = stream_id;
282 return true;
283}
284
285bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700286 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id), unidirectional_);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500287 if (!IsIncomingStream(id)) {
288 // Stream IDs under next_ougoing_stream_id_ are either open or previously
289 // open but now closed.
290 return id >= next_outgoing_stream_id_;
291 }
292 // For peer created streams, we also need to consider available streams.
293 return largest_peer_created_stream_id_ ==
renjietangab93f582019-12-13 10:29:31 -0800294 QuicUtils::GetInvalidStreamId(transport_version_) ||
QUICHE teama6ef0a62019-03-07 20:34:33 -0500295 id > largest_peer_created_stream_id_ ||
296 QuicContainsKey(available_streams_, id);
297}
298
299bool QuicStreamIdManager::IsIncomingStream(QuicStreamId id) const {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700300 DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id), unidirectional_);
301 // The 0x1 bit in the stream id indicates whether the stream id is
302 // server- or client- initiated. Next_OUTGOING_stream_id_ has that bit
303 // set based on whether this node is a server or client. Thus, if the stream
304 // id in question has the 0x1 bit set opposite of next_OUTGOING_stream_id_,
305 // then that stream id is incoming -- it is for streams initiated by the peer.
306 return (id & 0x1) != (next_outgoing_stream_id_ & 0x1);
307}
308
309QuicStreamId QuicStreamIdManager::GetFirstOutgoingStreamId() const {
310 return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
renjietangab93f582019-12-13 10:29:31 -0800311 transport_version_, perspective())
fkastenholz3c4eabf2019-04-22 07:49:59 -0700312 : QuicUtils::GetFirstBidirectionalStreamId(
renjietangab93f582019-12-13 10:29:31 -0800313 transport_version_, perspective());
fkastenholz3c4eabf2019-04-22 07:49:59 -0700314}
315
316QuicStreamId QuicStreamIdManager::GetFirstIncomingStreamId() const {
317 return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
renjietangab93f582019-12-13 10:29:31 -0800318 transport_version_, peer_perspective())
fkastenholz3c4eabf2019-04-22 07:49:59 -0700319 : QuicUtils::GetFirstBidirectionalStreamId(
renjietangab93f582019-12-13 10:29:31 -0800320 transport_version_, peer_perspective());
fkastenholz3c4eabf2019-04-22 07:49:59 -0700321}
322
323Perspective QuicStreamIdManager::perspective() const {
rcha8b56e42019-09-20 10:41:48 -0700324 return perspective_;
fkastenholz3c4eabf2019-04-22 07:49:59 -0700325}
326
327Perspective QuicStreamIdManager::peer_perspective() const {
nharper4eba09b2019-06-26 20:17:25 -0700328 return QuicUtils::InvertPerspective(perspective());
fkastenholz3c4eabf2019-04-22 07:49:59 -0700329}
330
renjietang52e13382019-12-16 15:58:04 -0800331QuicStreamCount QuicStreamIdManager::available_incoming_streams() {
fkastenholz3c4eabf2019-04-22 07:49:59 -0700332 return incoming_advertised_max_streams_ - incoming_stream_count_;
333}
334
335void QuicStreamIdManager::CalculateIncomingMaxStreamsWindow() {
336 max_streams_window_ = incoming_actual_max_streams_ / kMaxStreamsWindowDivisor;
337 if (max_streams_window_ == 0) {
338 max_streams_window_ = 1;
339 }
QUICHE teama6ef0a62019-03-07 20:34:33 -0500340}
341
fkastenholz56055be2019-09-17 11:17:37 -0700342void QuicStreamIdManager::OnConfigNegotiated() {
rcha8b56e42019-09-20 10:41:48 -0700343 is_config_negotiated_ = true;
fkastenholz56055be2019-09-17 11:17:37 -0700344 // If a STREAMS_BLOCKED or MAX_STREAMS is pending, send it and clear
345 // the pending state.
346 if (pending_streams_blocked_ !=
renjietangab93f582019-12-13 10:29:31 -0800347 QuicUtils::GetInvalidStreamId(transport_version_)) {
fkastenholz56055be2019-09-17 11:17:37 -0700348 if (pending_streams_blocked_ >= outgoing_max_streams_) {
349 // There is a pending STREAMS_BLOCKED frame and the current limit does not
350 // let new streams be formed. Regenerate and send the frame.
rcha8b56e42019-09-20 10:41:48 -0700351 delegate_->SendStreamsBlocked(outgoing_max_streams_, unidirectional_);
fkastenholz56055be2019-09-17 11:17:37 -0700352 }
353 pending_streams_blocked_ =
renjietangab93f582019-12-13 10:29:31 -0800354 QuicUtils::GetInvalidStreamId(transport_version_);
fkastenholz56055be2019-09-17 11:17:37 -0700355 }
356 if (pending_max_streams_) {
357 // Generate a MAX_STREAMS using the current stream limits.
358 SendMaxStreamsFrame();
359 pending_max_streams_ = false;
360 }
361}
362
QUICHE teama6ef0a62019-03-07 20:34:33 -0500363} // namespace quic