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 "quiche/quic/core/legacy_quic_stream_id_manager.h"
5
6 #include "quiche/quic/core/quic_session.h"
7 #include "quiche/quic/core/quic_types.h"
8 #include "quiche/quic/core/quic_utils.h"
9 #include "quiche/quic/core/quic_versions.h"
10
11 namespace quic {
12
LegacyQuicStreamIdManager(Perspective perspective,QuicTransportVersion transport_version,size_t max_open_outgoing_streams,size_t max_open_incoming_streams)13 LegacyQuicStreamIdManager::LegacyQuicStreamIdManager(
14 Perspective perspective, QuicTransportVersion transport_version,
15 size_t max_open_outgoing_streams, size_t max_open_incoming_streams)
16 : perspective_(perspective),
17 transport_version_(transport_version),
18 max_open_outgoing_streams_(max_open_outgoing_streams),
19 max_open_incoming_streams_(max_open_incoming_streams),
20 next_outgoing_stream_id_(QuicUtils::GetFirstBidirectionalStreamId(
21 transport_version_, perspective_)),
22 largest_peer_created_stream_id_(
23 perspective_ == Perspective::IS_SERVER
24 ? (QuicVersionUsesCryptoFrames(transport_version_)
25 ? QuicUtils::GetInvalidStreamId(transport_version_)
26 : QuicUtils::GetCryptoStreamId(transport_version_))
27 : QuicUtils::GetInvalidStreamId(transport_version_)),
28 num_open_incoming_streams_(0),
29 num_open_outgoing_streams_(0) {}
30
~LegacyQuicStreamIdManager()31 LegacyQuicStreamIdManager::~LegacyQuicStreamIdManager() {}
32
CanOpenNextOutgoingStream() const33 bool LegacyQuicStreamIdManager::CanOpenNextOutgoingStream() const {
34 QUICHE_DCHECK_LE(num_open_outgoing_streams_, max_open_outgoing_streams_);
35 QUIC_DLOG_IF(INFO, num_open_outgoing_streams_ == max_open_outgoing_streams_)
36 << "Failed to create a new outgoing stream. "
37 << "Already " << num_open_outgoing_streams_ << " open.";
38 return num_open_outgoing_streams_ < max_open_outgoing_streams_;
39 }
40
CanOpenIncomingStream() const41 bool LegacyQuicStreamIdManager::CanOpenIncomingStream() const {
42 return num_open_incoming_streams_ < max_open_incoming_streams_;
43 }
44
MaybeIncreaseLargestPeerStreamId(const QuicStreamId stream_id)45 bool LegacyQuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
46 const QuicStreamId stream_id) {
47 available_streams_.erase(stream_id);
48
49 if (largest_peer_created_stream_id_ !=
50 QuicUtils::GetInvalidStreamId(transport_version_) &&
51 stream_id <= largest_peer_created_stream_id_) {
52 return true;
53 }
54
55 // Check if the new number of available streams would cause the number of
56 // available streams to exceed the limit. Note that the peer can create
57 // only alternately-numbered streams.
58 size_t additional_available_streams =
59 (stream_id - largest_peer_created_stream_id_) / 2 - 1;
60 if (largest_peer_created_stream_id_ ==
61 QuicUtils::GetInvalidStreamId(transport_version_)) {
62 additional_available_streams = (stream_id + 1) / 2 - 1;
63 }
64 size_t new_num_available_streams =
65 GetNumAvailableStreams() + additional_available_streams;
66 if (new_num_available_streams > MaxAvailableStreams()) {
67 QUIC_DLOG(INFO) << perspective_
68 << "Failed to create a new incoming stream with id:"
69 << stream_id << ". There are already "
70 << GetNumAvailableStreams()
71 << " streams available, which would become "
72 << new_num_available_streams << ", which exceeds the limit "
73 << MaxAvailableStreams() << ".";
74 return false;
75 }
76 QuicStreamId first_available_stream = largest_peer_created_stream_id_ + 2;
77 if (largest_peer_created_stream_id_ ==
78 QuicUtils::GetInvalidStreamId(transport_version_)) {
79 first_available_stream = QuicUtils::GetFirstBidirectionalStreamId(
80 transport_version_, QuicUtils::InvertPerspective(perspective_));
81 }
82 for (QuicStreamId id = first_available_stream; id < stream_id; id += 2) {
83 available_streams_.insert(id);
84 }
85 largest_peer_created_stream_id_ = stream_id;
86
87 return true;
88 }
89
GetNextOutgoingStreamId()90 QuicStreamId LegacyQuicStreamIdManager::GetNextOutgoingStreamId() {
91 QuicStreamId id = next_outgoing_stream_id_;
92 next_outgoing_stream_id_ += 2;
93 return id;
94 }
95
ActivateStream(bool is_incoming)96 void LegacyQuicStreamIdManager::ActivateStream(bool is_incoming) {
97 if (is_incoming) {
98 ++num_open_incoming_streams_;
99 return;
100 }
101 ++num_open_outgoing_streams_;
102 }
103
OnStreamClosed(bool is_incoming)104 void LegacyQuicStreamIdManager::OnStreamClosed(bool is_incoming) {
105 if (is_incoming) {
106 QUIC_BUG_IF(quic_bug_12720_1, num_open_incoming_streams_ == 0);
107 --num_open_incoming_streams_;
108 return;
109 }
110 QUIC_BUG_IF(quic_bug_12720_2, num_open_outgoing_streams_ == 0);
111 --num_open_outgoing_streams_;
112 }
113
IsAvailableStream(QuicStreamId id) const114 bool LegacyQuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
115 if (!IsIncomingStream(id)) {
116 // Stream IDs under next_ougoing_stream_id_ are either open or previously
117 // open but now closed.
118 return id >= next_outgoing_stream_id_;
119 }
120 // For peer created streams, we also need to consider available streams.
121 return largest_peer_created_stream_id_ ==
122 QuicUtils::GetInvalidStreamId(transport_version_) ||
123 id > largest_peer_created_stream_id_ ||
124 available_streams_.contains(id);
125 }
126
IsIncomingStream(QuicStreamId id) const127 bool LegacyQuicStreamIdManager::IsIncomingStream(QuicStreamId id) const {
128 return id % 2 != next_outgoing_stream_id_ % 2;
129 }
130
GetNumAvailableStreams() const131 size_t LegacyQuicStreamIdManager::GetNumAvailableStreams() const {
132 return available_streams_.size();
133 }
134
MaxAvailableStreams() const135 size_t LegacyQuicStreamIdManager::MaxAvailableStreams() const {
136 return max_open_incoming_streams_ * kMaxAvailableStreamsMultiplier;
137 }
138
139 } // namespace quic
140