// 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 "quiche/quic/core/legacy_quic_stream_id_manager.h" #include "quiche/quic/core/quic_session.h" #include "quiche/quic/core/quic_types.h" #include "quiche/quic/core/quic_utils.h" #include "quiche/quic/core/quic_versions.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 { QUICHE_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(quic_bug_12720_1, num_open_incoming_streams_ == 0); --num_open_incoming_streams_; return; } QUIC_BUG_IF(quic_bug_12720_2, 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_ || available_streams_.contains(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