| // Copyright (c) 2018 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "quic/core/legacy_quic_stream_id_manager.h" | |
| #include "quic/core/quic_session.h" | |
| #include "quic/core/quic_types.h" | |
| #include "quic/core/quic_utils.h" | |
| #include "quic/core/quic_versions.h" | |
| #include "quic/platform/api/quic_map_util.h" | |
| #include "common/platform/api/quiche_str_cat.h" | |
| namespace quic { | |
| LegacyQuicStreamIdManager::LegacyQuicStreamIdManager( | |
| Perspective perspective, | |
| QuicTransportVersion transport_version, | |
| size_t max_open_outgoing_streams, | |
| size_t max_open_incoming_streams) | |
| : perspective_(perspective), | |
| transport_version_(transport_version), | |
| max_open_outgoing_streams_(max_open_outgoing_streams), | |
| max_open_incoming_streams_(max_open_incoming_streams), | |
| next_outgoing_stream_id_( | |
| QuicUtils::GetFirstBidirectionalStreamId(transport_version_, | |
| perspective_)), | |
| largest_peer_created_stream_id_( | |
| perspective_ == Perspective::IS_SERVER | |
| ? (QuicVersionUsesCryptoFrames(transport_version_) | |
| ? QuicUtils::GetInvalidStreamId(transport_version_) | |
| : QuicUtils::GetCryptoStreamId(transport_version_)) | |
| : QuicUtils::GetInvalidStreamId(transport_version_)), | |
| num_open_incoming_streams_(0), | |
| num_open_outgoing_streams_(0) {} | |
| LegacyQuicStreamIdManager::~LegacyQuicStreamIdManager() {} | |
| bool LegacyQuicStreamIdManager::CanOpenNextOutgoingStream() const { | |
| DCHECK_LE(num_open_outgoing_streams_, max_open_outgoing_streams_); | |
| QUIC_DLOG_IF(INFO, num_open_outgoing_streams_ == max_open_outgoing_streams_) | |
| << "Failed to create a new outgoing stream. " | |
| << "Already " << num_open_outgoing_streams_ << " open."; | |
| return num_open_outgoing_streams_ < max_open_outgoing_streams_; | |
| } | |
| bool LegacyQuicStreamIdManager::CanOpenIncomingStream() const { | |
| return num_open_incoming_streams_ < max_open_incoming_streams_; | |
| } | |
| bool LegacyQuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( | |
| const QuicStreamId stream_id) { | |
| available_streams_.erase(stream_id); | |
| if (largest_peer_created_stream_id_ != | |
| QuicUtils::GetInvalidStreamId(transport_version_) && | |
| stream_id <= largest_peer_created_stream_id_) { | |
| return true; | |
| } | |
| // Check if the new number of available streams would cause the number of | |
| // available streams to exceed the limit. Note that the peer can create | |
| // only alternately-numbered streams. | |
| size_t additional_available_streams = | |
| (stream_id - largest_peer_created_stream_id_) / 2 - 1; | |
| if (largest_peer_created_stream_id_ == | |
| QuicUtils::GetInvalidStreamId(transport_version_)) { | |
| additional_available_streams = (stream_id + 1) / 2 - 1; | |
| } | |
| size_t new_num_available_streams = | |
| GetNumAvailableStreams() + additional_available_streams; | |
| if (new_num_available_streams > MaxAvailableStreams()) { | |
| QUIC_DLOG(INFO) << perspective_ | |
| << "Failed to create a new incoming stream with id:" | |
| << stream_id << ". There are already " | |
| << GetNumAvailableStreams() | |
| << " streams available, which would become " | |
| << new_num_available_streams << ", which exceeds the limit " | |
| << MaxAvailableStreams() << "."; | |
| return false; | |
| } | |
| QuicStreamId first_available_stream = largest_peer_created_stream_id_ + 2; | |
| if (largest_peer_created_stream_id_ == | |
| QuicUtils::GetInvalidStreamId(transport_version_)) { | |
| first_available_stream = QuicUtils::GetFirstBidirectionalStreamId( | |
| transport_version_, QuicUtils::InvertPerspective(perspective_)); | |
| } | |
| for (QuicStreamId id = first_available_stream; id < stream_id; id += 2) { | |
| available_streams_.insert(id); | |
| } | |
| largest_peer_created_stream_id_ = stream_id; | |
| return true; | |
| } | |
| QuicStreamId LegacyQuicStreamIdManager::GetNextOutgoingStreamId() { | |
| QuicStreamId id = next_outgoing_stream_id_; | |
| next_outgoing_stream_id_ += 2; | |
| return id; | |
| } | |
| void LegacyQuicStreamIdManager::ActivateStream(bool is_incoming) { | |
| if (is_incoming) { | |
| ++num_open_incoming_streams_; | |
| return; | |
| } | |
| ++num_open_outgoing_streams_; | |
| } | |
| void LegacyQuicStreamIdManager::OnStreamClosed(bool is_incoming) { | |
| if (is_incoming) { | |
| QUIC_BUG_IF(num_open_incoming_streams_ == 0); | |
| --num_open_incoming_streams_; | |
| return; | |
| } | |
| QUIC_BUG_IF(num_open_outgoing_streams_ == 0); | |
| --num_open_outgoing_streams_; | |
| } | |
| bool LegacyQuicStreamIdManager::IsAvailableStream(QuicStreamId id) const { | |
| if (!IsIncomingStream(id)) { | |
| // Stream IDs under next_ougoing_stream_id_ are either open or previously | |
| // open but now closed. | |
| return id >= next_outgoing_stream_id_; | |
| } | |
| // For peer created streams, we also need to consider available streams. | |
| return largest_peer_created_stream_id_ == | |
| QuicUtils::GetInvalidStreamId(transport_version_) || | |
| id > largest_peer_created_stream_id_ || | |
| QuicContainsKey(available_streams_, id); | |
| } | |
| bool LegacyQuicStreamIdManager::IsIncomingStream(QuicStreamId id) const { | |
| return id % 2 != next_outgoing_stream_id_ % 2; | |
| } | |
| size_t LegacyQuicStreamIdManager::GetNumAvailableStreams() const { | |
| return available_streams_.size(); | |
| } | |
| size_t LegacyQuicStreamIdManager::MaxAvailableStreams() const { | |
| return max_open_incoming_streams_ * kMaxAvailableStreamsMultiplier; | |
| } | |
| } // namespace quic |