QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1 | // 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 | |
vasilvv | 872e7a3 | 2019-03-12 16:42:44 -0700 | [diff] [blame] | 6 | #include <string> |
| 7 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 8 | #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" |
dmcardle | cf0bfcf | 2019-12-13 08:08:21 -0800 | [diff] [blame] | 15 | #include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 16 | |
| 17 | namespace quic { |
| 18 | |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 19 | #define ENDPOINT \ |
| 20 | (perspective_ == Perspective::IS_SERVER ? " Server: " : " Client: ") |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 21 | |
| 22 | QuicStreamIdManager::QuicStreamIdManager( |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 23 | DelegateInterface* delegate, |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 24 | bool unidirectional, |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 25 | Perspective perspective, |
| 26 | QuicTransportVersion transport_version, |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 27 | QuicStreamCount max_allowed_outgoing_streams, |
| 28 | QuicStreamCount max_allowed_incoming_streams) |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 29 | : delegate_(delegate), |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 30 | unidirectional_(unidirectional), |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 31 | perspective_(perspective), |
| 32 | transport_version_(transport_version), |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 33 | is_config_negotiated_(false), |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 34 | outgoing_max_streams_(max_allowed_outgoing_streams), |
| 35 | next_outgoing_stream_id_(GetFirstOutgoingStreamId()), |
| 36 | outgoing_stream_count_(0), |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 37 | 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), |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 43 | incoming_stream_count_(0), |
| 44 | largest_peer_created_stream_id_( |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 45 | QuicUtils::GetInvalidStreamId(transport_version)), |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 46 | max_streams_window_(0), |
| 47 | pending_max_streams_(false), |
| 48 | pending_streams_blocked_( |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 49 | QuicUtils::GetInvalidStreamId(transport_version)) { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 50 | CalculateIncomingMaxStreamsWindow(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 51 | } |
| 52 | |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 53 | QuicStreamIdManager::~QuicStreamIdManager() {} |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 54 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 55 | bool 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); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 59 | |
| 60 | // Set the limit to be exactly the stream count in the frame. |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 61 | // Also informs the higher layers that they can create more |
| 62 | // streams if the limit is increased. |
| 63 | return SetMaxOpenOutgoingStreams(frame.stream_count); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 64 | } |
| 65 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 66 | // The peer sends a streams blocked frame when it can not open any more |
| 67 | // streams because it has runs into the limit. |
| 68 | bool QuicStreamIdManager::OnStreamsBlockedFrame( |
| 69 | const QuicStreamsBlockedFrame& frame) { |
| 70 | // Ensure that the frame has the correct directionality. |
| 71 | DCHECK_EQ(frame.unidirectional, unidirectional_); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 72 | if (frame.stream_count > incoming_advertised_max_streams_) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 73 | // Peer thinks it can send more streams that we've told it. |
| 74 | // This is a protocol error. |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 75 | QUIC_CODE_COUNT(quic_streams_blocked_too_big); |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 76 | delegate_->OnError(QUIC_STREAMS_BLOCKED_ERROR, |
| 77 | "Invalid stream count specified"); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 78 | return false; |
| 79 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 80 | 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 85 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 86 | QUIC_CODE_COUNT(quic_streams_blocked_id_correct); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 87 | return true; |
| 88 | } |
| 89 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 90 | // Used when configuration has been done and we have an initial |
| 91 | // maximum stream count from the peer. |
renjietang | 52e1338 | 2019-12-16 15:58:04 -0800 | [diff] [blame] | 92 | bool QuicStreamIdManager::SetMaxOpenOutgoingStreams( |
| 93 | QuicStreamCount max_open_streams) { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 94 | 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_) { |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 103 | delegate_->OnError(QUIC_MAX_STREAMS_ERROR, |
| 104 | "Stream limit less than existing stream count"); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 105 | 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 114 | } |
| 115 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 116 | // This implementation only supports 32 bit Stream IDs, so limit max streams |
| 117 | // if it would exceed the max 32 bits can express. |
renjietang | 52e1338 | 2019-12-16 15:58:04 -0800 | [diff] [blame] | 118 | outgoing_max_streams_ = |
| 119 | std::min(max_open_streams, |
| 120 | QuicUtils::GetMaxStreamCount(unidirectional_, perspective_)); |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 121 | |
| 122 | // Inform the higher layers that the stream limit has increased and that |
| 123 | // new streams may be created. |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 124 | delegate_->OnCanCreateNewOutgoingStream(unidirectional_); |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 125 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 126 | return true; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 127 | } |
| 128 | |
renjietang | 52e1338 | 2019-12-16 15:58:04 -0800 | [diff] [blame] | 129 | void QuicStreamIdManager::SetMaxOpenIncomingStreams( |
| 130 | QuicStreamCount max_open_streams) { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 131 | QuicStreamCount implementation_max = |
| 132 | QuicUtils::GetMaxStreamCount(unidirectional_, perspective()); |
renjietang | 52e1338 | 2019-12-16 15:58:04 -0800 | [diff] [blame] | 133 | QuicStreamCount new_max = std::min(implementation_max, max_open_streams); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 134 | if (new_max < incoming_stream_count_) { |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 135 | delegate_->OnError(QUIC_MAX_STREAMS_ERROR, |
| 136 | "Stream limit less than existing stream count"); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 137 | return; |
| 138 | } |
| 139 | incoming_actual_max_streams_ = new_max; |
| 140 | incoming_advertised_max_streams_ = new_max; |
| 141 | incoming_initial_max_open_streams_ = |
renjietang | 52e1338 | 2019-12-16 15:58:04 -0800 | [diff] [blame] | 142 | std::min(max_open_streams, implementation_max); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 143 | CalculateIncomingMaxStreamsWindow(); |
| 144 | } |
| 145 | |
| 146 | void QuicStreamIdManager::MaybeSendMaxStreamsFrame() { |
| 147 | if ((incoming_advertised_max_streams_ - incoming_stream_count_) > |
| 148 | max_streams_window_) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 149 | // window too large, no advertisement |
| 150 | return; |
| 151 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 152 | SendMaxStreamsFrame(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 153 | } |
| 154 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 155 | void QuicStreamIdManager::SendMaxStreamsFrame() { |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 156 | 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. |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 161 | pending_max_streams_ = true; |
| 162 | return; |
| 163 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 164 | incoming_advertised_max_streams_ = incoming_actual_max_streams_; |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 165 | delegate_->SendMaxStreams(incoming_advertised_max_streams_, unidirectional_); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 169 | DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 170 | if (!IsIncomingStream(stream_id)) { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 171 | // Nothing to do for outgoing streams. |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 172 | return; |
| 173 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 174 | // 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 179 | // Reached the maximum stream id value that the implementation |
| 180 | // supports. Nothing can be done here. |
| 181 | return; |
| 182 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 183 | // One stream closed ... another can be opened. |
| 184 | incoming_actual_max_streams_++; |
| 185 | MaybeSendMaxStreamsFrame(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() { |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 189 | // Applications should always consult CanOpenNextOutgoingStream() first. |
| 190 | // If they ask for stream ids that violate the limit, it's an implementation |
| 191 | // bug. |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 192 | QUIC_BUG_IF(outgoing_stream_count_ >= outgoing_max_streams_) |
| 193 | << "Attempt to allocate a new outgoing stream that would exceed the " |
fkastenholz | 9b4b0ad | 2019-08-20 05:10:40 -0700 | [diff] [blame] | 194 | "limit (" |
| 195 | << outgoing_max_streams_ << ")"; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 196 | QuicStreamId id = next_outgoing_stream_id_; |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 197 | next_outgoing_stream_id_ += QuicUtils::StreamIdDelta(transport_version_); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 198 | outgoing_stream_count_++; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 199 | return id; |
| 200 | } |
| 201 | |
| 202 | bool QuicStreamIdManager::CanOpenNextOutgoingStream() { |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 203 | DCHECK(VersionHasIetfQuicFrames(transport_version_)); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 204 | if (outgoing_stream_count_ < outgoing_max_streams_) { |
| 205 | return true; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 206 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 207 | // Next stream ID would exceed the limit, need to inform the peer. |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 208 | |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 209 | 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. |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 213 | pending_streams_blocked_ = outgoing_max_streams_; |
| 214 | return false; |
| 215 | } |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 216 | delegate_->SendStreamsBlocked(outgoing_max_streams_, unidirectional_); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 217 | QUIC_CODE_COUNT(quic_reached_outgoing_stream_id_limit); |
| 218 | return false; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 219 | } |
| 220 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 221 | // Stream_id is the id of a new incoming stream. Check if it can be |
| 222 | // created (doesn't violate limits, etc). |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 223 | bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( |
| 224 | const QuicStreamId stream_id) { |
rch | 9301d3c | 2019-09-20 14:30:48 -0700 | [diff] [blame] | 225 | // |stream_id| must be an incoming stream of the right directionality. |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 226 | DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_); |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 227 | DCHECK_NE(QuicUtils::IsServerInitiatedStreamId(transport_version_, stream_id), |
| 228 | perspective() == Perspective::IS_SERVER); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 229 | available_streams_.erase(stream_id); |
| 230 | |
| 231 | if (largest_peer_created_stream_id_ != |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 232 | QuicUtils::GetInvalidStreamId(transport_version_) && |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 233 | stream_id <= largest_peer_created_stream_id_) { |
| 234 | return true; |
| 235 | } |
| 236 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 237 | QuicStreamCount stream_count_increment; |
| 238 | if (largest_peer_created_stream_id_ != |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 239 | QuicUtils::GetInvalidStreamId(transport_version_)) { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 240 | stream_count_increment = (stream_id - largest_peer_created_stream_id_) / |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 241 | QuicUtils::StreamIdDelta(transport_version_); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 242 | } 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()) / |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 249 | QuicUtils::StreamIdDelta(transport_version_)) + |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 250 | 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 259 | QUIC_DLOG(INFO) << ENDPOINT |
| 260 | << "Failed to create a new incoming stream with id:" |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 261 | << stream_id << ", reaching MAX_STREAMS limit: " |
| 262 | << incoming_advertised_max_streams_ << "."; |
dmcardle | cf0bfcf | 2019-12-13 08:08:21 -0800 | [diff] [blame] | 263 | 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 267 | return false; |
| 268 | } |
| 269 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 270 | QuicStreamId id = GetFirstIncomingStreamId(); |
| 271 | if (largest_peer_created_stream_id_ != |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 272 | QuicUtils::GetInvalidStreamId(transport_version_)) { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 273 | id = largest_peer_created_stream_id_ + |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 274 | QuicUtils::StreamIdDelta(transport_version_); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 275 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 276 | |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 277 | for (; id < stream_id; id += QuicUtils::StreamIdDelta(transport_version_)) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 278 | available_streams_.insert(id); |
| 279 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 280 | incoming_stream_count_ += stream_count_increment; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 281 | largest_peer_created_stream_id_ = stream_id; |
| 282 | return true; |
| 283 | } |
| 284 | |
| 285 | bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 286 | DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id), unidirectional_); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 287 | 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_ == |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 294 | QuicUtils::GetInvalidStreamId(transport_version_) || |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 295 | id > largest_peer_created_stream_id_ || |
| 296 | QuicContainsKey(available_streams_, id); |
| 297 | } |
| 298 | |
| 299 | bool QuicStreamIdManager::IsIncomingStream(QuicStreamId id) const { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 300 | 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 | |
| 309 | QuicStreamId QuicStreamIdManager::GetFirstOutgoingStreamId() const { |
| 310 | return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId( |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 311 | transport_version_, perspective()) |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 312 | : QuicUtils::GetFirstBidirectionalStreamId( |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 313 | transport_version_, perspective()); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 314 | } |
| 315 | |
| 316 | QuicStreamId QuicStreamIdManager::GetFirstIncomingStreamId() const { |
| 317 | return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId( |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 318 | transport_version_, peer_perspective()) |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 319 | : QuicUtils::GetFirstBidirectionalStreamId( |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 320 | transport_version_, peer_perspective()); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 321 | } |
| 322 | |
| 323 | Perspective QuicStreamIdManager::perspective() const { |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 324 | return perspective_; |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 325 | } |
| 326 | |
| 327 | Perspective QuicStreamIdManager::peer_perspective() const { |
nharper | 4eba09b | 2019-06-26 20:17:25 -0700 | [diff] [blame] | 328 | return QuicUtils::InvertPerspective(perspective()); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 329 | } |
| 330 | |
renjietang | 52e1338 | 2019-12-16 15:58:04 -0800 | [diff] [blame] | 331 | QuicStreamCount QuicStreamIdManager::available_incoming_streams() { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 332 | return incoming_advertised_max_streams_ - incoming_stream_count_; |
| 333 | } |
| 334 | |
| 335 | void QuicStreamIdManager::CalculateIncomingMaxStreamsWindow() { |
| 336 | max_streams_window_ = incoming_actual_max_streams_ / kMaxStreamsWindowDivisor; |
| 337 | if (max_streams_window_ == 0) { |
| 338 | max_streams_window_ = 1; |
| 339 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 340 | } |
| 341 | |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 342 | void QuicStreamIdManager::OnConfigNegotiated() { |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 343 | is_config_negotiated_ = true; |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 344 | // If a STREAMS_BLOCKED or MAX_STREAMS is pending, send it and clear |
| 345 | // the pending state. |
| 346 | if (pending_streams_blocked_ != |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 347 | QuicUtils::GetInvalidStreamId(transport_version_)) { |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 348 | 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. |
rch | a8b56e4 | 2019-09-20 10:41:48 -0700 | [diff] [blame] | 351 | delegate_->SendStreamsBlocked(outgoing_max_streams_, unidirectional_); |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 352 | } |
| 353 | pending_streams_blocked_ = |
renjietang | ab93f58 | 2019-12-13 10:29:31 -0800 | [diff] [blame] | 354 | QuicUtils::GetInvalidStreamId(transport_version_); |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 355 | } |
| 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 363 | } // namespace quic |