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" |
| 15 | #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 16 | |
| 17 | namespace quic { |
| 18 | |
| 19 | #define ENDPOINT \ |
| 20 | (session_->perspective() == Perspective::IS_SERVER ? " Server: " \ |
| 21 | : " Client: ") |
| 22 | |
| 23 | QuicStreamIdManager::QuicStreamIdManager( |
| 24 | QuicSession* session, |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 25 | bool unidirectional, |
| 26 | QuicStreamCount max_allowed_outgoing_streams, |
| 27 | QuicStreamCount max_allowed_incoming_streams) |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 28 | : session_(session), |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 29 | 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 46 | } |
| 47 | |
| 48 | QuicStreamIdManager::~QuicStreamIdManager() { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 49 | } |
| 50 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 51 | bool 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 59 | return false; |
| 60 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 61 | // 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 67 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 68 | return true; |
| 69 | } |
| 70 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 71 | // The peer sends a streams blocked frame when it can not open any more |
| 72 | // streams because it has runs into the limit. |
| 73 | bool 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 78 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 79 | if (frame.stream_count > incoming_advertised_max_streams_) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 80 | // 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. |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 84 | QUIC_CODE_COUNT(quic_streams_blocked_too_big); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 85 | session_->connection()->CloseConnection( |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 86 | QUIC_STREAMS_BLOCKED_ERROR, "Invalid stream count specified", |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 87 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 88 | return false; |
| 89 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 90 | 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 95 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 96 | QUIC_CODE_COUNT(quic_streams_blocked_id_correct); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 97 | return true; |
| 98 | } |
| 99 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 100 | // Used when configuration has been done and we have an initial |
| 101 | // maximum stream count from the peer. |
| 102 | bool 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 126 | } |
| 127 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 128 | // This implementation only supports 32 bit Stream IDs, so limit max streams |
| 129 | // if it would exceed the max 32 bits can express. |
nharper | fc7c10e | 2019-05-21 03:44:53 -0700 | [diff] [blame] | 130 | outgoing_max_streams_ = std::min<size_t>( |
| 131 | max_open_streams, |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 132 | QuicUtils::GetMaxStreamCount(unidirectional_, session_->perspective())); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 133 | return true; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 134 | } |
| 135 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 136 | void QuicStreamIdManager::SetMaxOpenOutgoingStreams(size_t max_open_streams) { |
| 137 | QUIC_BUG_IF(!using_default_max_streams_); |
fkastenholz | d3a1de9 | 2019-05-15 07:00:07 -0700 | [diff] [blame] | 138 | // TODO(fkastenholz): when static streams are removed from I-Quic, this |
| 139 | // should be revised to invoke ConfigureMaxOpen... |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 140 | 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 |
fkastenholz | d3a1de9 | 2019-05-15 07:00:07 -0700 | [diff] [blame] | 147 | // sets the maximum to be max_streams + number_of_statics. This should be |
| 148 | // removed from IETF QUIC when static streams are gone. |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 149 | void 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 | |
| 162 | void 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 | |
| 186 | void QuicStreamIdManager::MaybeSendMaxStreamsFrame() { |
| 187 | if ((incoming_advertised_max_streams_ - incoming_stream_count_) > |
| 188 | max_streams_window_) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 189 | // window too large, no advertisement |
| 190 | return; |
| 191 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 192 | SendMaxStreamsFrame(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 193 | } |
| 194 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 195 | void QuicStreamIdManager::SendMaxStreamsFrame() { |
| 196 | incoming_advertised_max_streams_ = incoming_actual_max_streams_; |
| 197 | session_->SendMaxStreams(incoming_advertised_max_streams_, unidirectional_); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 198 | } |
| 199 | |
| 200 | void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 201 | DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 202 | if (!IsIncomingStream(stream_id)) { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 203 | // Nothing to do for outgoing streams. |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 204 | return; |
| 205 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 206 | // 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 211 | // Reached the maximum stream id value that the implementation |
| 212 | // supports. Nothing can be done here. |
| 213 | return; |
| 214 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 215 | // One stream closed ... another can be opened. |
| 216 | incoming_actual_max_streams_++; |
| 217 | MaybeSendMaxStreamsFrame(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 218 | } |
| 219 | |
| 220 | QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 221 | // 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 225 | QuicStreamId id = next_outgoing_stream_id_; |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 226 | next_outgoing_stream_id_ += QuicUtils::StreamIdDelta(transport_version()); |
| 227 | outgoing_stream_count_++; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 228 | return id; |
| 229 | } |
| 230 | |
| 231 | bool QuicStreamIdManager::CanOpenNextOutgoingStream() { |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 232 | DCHECK(VersionHasIetfQuicFrames(transport_version())); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 233 | if (outgoing_stream_count_ < outgoing_max_streams_) { |
| 234 | return true; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 235 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 236 | // 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 240 | } |
| 241 | |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 242 | bool QuicStreamIdManager::RegisterStaticStream(QuicStreamId stream_id, |
| 243 | bool stream_already_counted) { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 244 | DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_); |
| 245 | if (IsIncomingStream(stream_id)) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 246 | // 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 249 | // 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. |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 252 | |
| 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 261 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 262 | |
| 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 | } |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 271 | |
| 272 | if (!stream_already_counted) { |
| 273 | incoming_stream_count_++; |
| 274 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 275 | incoming_static_stream_count_++; |
| 276 | return true; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 277 | } |
| 278 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 279 | 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 288 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 289 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 290 | // 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 297 | } |
| 298 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 299 | // Stream_id is the id of a new incoming stream. Check if it can be |
| 300 | // created (doesn't violate limits, etc). |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 301 | bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( |
| 302 | const QuicStreamId stream_id) { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 303 | DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_); |
| 304 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 305 | available_streams_.erase(stream_id); |
| 306 | |
| 307 | if (largest_peer_created_stream_id_ != |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 308 | QuicUtils::GetInvalidStreamId(transport_version()) && |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 309 | stream_id <= largest_peer_created_stream_id_) { |
| 310 | return true; |
| 311 | } |
| 312 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 313 | 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 335 | QUIC_DLOG(INFO) << ENDPOINT |
| 336 | << "Failed to create a new incoming stream with id:" |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 337 | << stream_id << ", reaching MAX_STREAMS limit: " |
| 338 | << incoming_advertised_max_streams_ << "."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 339 | session_->connection()->CloseConnection( |
| 340 | QUIC_INVALID_STREAM_ID, |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 341 | QuicStrCat("Stream id ", stream_id, " would exceed stream count limit ", |
| 342 | incoming_advertised_max_streams_), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 343 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 344 | return false; |
| 345 | } |
| 346 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 347 | 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 team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 352 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 353 | |
| 354 | for (; id < stream_id; id += QuicUtils::StreamIdDelta(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 355 | available_streams_.insert(id); |
| 356 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 357 | incoming_stream_count_ += stream_count_increment; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 358 | largest_peer_created_stream_id_ = stream_id; |
| 359 | return true; |
| 360 | } |
| 361 | |
| 362 | bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 363 | DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id), unidirectional_); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 364 | 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_ == |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 371 | QuicUtils::GetInvalidStreamId(transport_version()) || |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 372 | id > largest_peer_created_stream_id_ || |
| 373 | QuicContainsKey(available_streams_, id); |
| 374 | } |
| 375 | |
| 376 | bool QuicStreamIdManager::IsIncomingStream(QuicStreamId id) const { |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 377 | 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 | |
| 386 | QuicStreamId QuicStreamIdManager::GetFirstOutgoingStreamId() const { |
| 387 | return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId( |
| 388 | transport_version(), perspective()) |
| 389 | : QuicUtils::GetFirstBidirectionalStreamId( |
| 390 | transport_version(), perspective()); |
| 391 | } |
| 392 | |
| 393 | QuicStreamId QuicStreamIdManager::GetFirstIncomingStreamId() const { |
| 394 | return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId( |
| 395 | transport_version(), peer_perspective()) |
| 396 | : QuicUtils::GetFirstBidirectionalStreamId( |
| 397 | transport_version(), peer_perspective()); |
| 398 | } |
| 399 | |
| 400 | Perspective QuicStreamIdManager::perspective() const { |
| 401 | return session_->perspective(); |
| 402 | } |
| 403 | |
| 404 | Perspective QuicStreamIdManager::peer_perspective() const { |
nharper | 4eba09b | 2019-06-26 20:17:25 -0700 | [diff] [blame] | 405 | return QuicUtils::InvertPerspective(perspective()); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 406 | } |
| 407 | |
| 408 | QuicTransportVersion QuicStreamIdManager::transport_version() const { |
| 409 | return session_->connection()->transport_version(); |
| 410 | } |
| 411 | |
| 412 | size_t QuicStreamIdManager::available_incoming_streams() { |
| 413 | return incoming_advertised_max_streams_ - incoming_stream_count_; |
| 414 | } |
| 415 | |
| 416 | void QuicStreamIdManager::CalculateIncomingMaxStreamsWindow() { |
| 417 | max_streams_window_ = incoming_actual_max_streams_ / kMaxStreamsWindowDivisor; |
| 418 | if (max_streams_window_ == 0) { |
| 419 | max_streams_window_ = 1; |
| 420 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 421 | } |
| 422 | |
| 423 | } // namespace quic |