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/quic_stream_id_manager.h"
5
6 #include <cstdint>
7 #include <string>
8
9 #include "absl/strings/str_cat.h"
10 #include "quiche/quic/core/quic_connection.h"
11 #include "quiche/quic/core/quic_constants.h"
12 #include "quiche/quic/core/quic_session.h"
13 #include "quiche/quic/core/quic_utils.h"
14 #include "quiche/quic/core/quic_versions.h"
15 #include "quiche/quic/platform/api/quic_flag_utils.h"
16 #include "quiche/quic/platform/api/quic_flags.h"
17 #include "quiche/quic/platform/api/quic_logging.h"
18
19 namespace quic {
20
21 #define ENDPOINT \
22 (perspective_ == Perspective::IS_SERVER ? " Server: " : " Client: ")
23
QuicStreamIdManager(DelegateInterface * delegate,bool unidirectional,Perspective perspective,ParsedQuicVersion version,QuicStreamCount max_allowed_outgoing_streams,QuicStreamCount max_allowed_incoming_streams)24 QuicStreamIdManager::QuicStreamIdManager(
25 DelegateInterface* delegate, bool unidirectional, Perspective perspective,
26 ParsedQuicVersion version, QuicStreamCount max_allowed_outgoing_streams,
27 QuicStreamCount max_allowed_incoming_streams)
28 : delegate_(delegate),
29 unidirectional_(unidirectional),
30 perspective_(perspective),
31 version_(version),
32 outgoing_max_streams_(max_allowed_outgoing_streams),
33 next_outgoing_stream_id_(GetFirstOutgoingStreamId()),
34 outgoing_stream_count_(0),
35 incoming_actual_max_streams_(max_allowed_incoming_streams),
36 incoming_advertised_max_streams_(max_allowed_incoming_streams),
37 incoming_initial_max_open_streams_(max_allowed_incoming_streams),
38 incoming_stream_count_(0),
39 largest_peer_created_stream_id_(
40 QuicUtils::GetInvalidStreamId(version.transport_version)) {}
41
~QuicStreamIdManager()42 QuicStreamIdManager::~QuicStreamIdManager() {}
43
OnStreamsBlockedFrame(const QuicStreamsBlockedFrame & frame,std::string * error_details)44 bool QuicStreamIdManager::OnStreamsBlockedFrame(
45 const QuicStreamsBlockedFrame& frame, std::string* error_details) {
46 QUICHE_DCHECK_EQ(frame.unidirectional, unidirectional_);
47 if (frame.stream_count > incoming_advertised_max_streams_) {
48 // Peer thinks it can send more streams that we've told it.
49 *error_details = absl::StrCat(
50 "StreamsBlockedFrame's stream count ", frame.stream_count,
51 " exceeds incoming max stream ", incoming_advertised_max_streams_);
52 return false;
53 }
54 QUICHE_DCHECK_LE(incoming_advertised_max_streams_,
55 incoming_actual_max_streams_);
56 if (incoming_advertised_max_streams_ == incoming_actual_max_streams_) {
57 // We have told peer about current max.
58 return true;
59 }
60 if (frame.stream_count < incoming_actual_max_streams_) {
61 // Peer thinks it's blocked on a stream count that is less than our current
62 // max. Inform the peer of the correct stream count.
63 SendMaxStreamsFrame();
64 }
65 return true;
66 }
67
MaybeAllowNewOutgoingStreams(QuicStreamCount max_open_streams)68 bool QuicStreamIdManager::MaybeAllowNewOutgoingStreams(
69 QuicStreamCount max_open_streams) {
70 if (max_open_streams <= outgoing_max_streams_) {
71 // Only update the stream count if it would increase the limit.
72 return false;
73 }
74
75 // This implementation only supports 32 bit Stream IDs, so limit max streams
76 // if it would exceed the max 32 bits can express.
77 outgoing_max_streams_ =
78 std::min(max_open_streams, QuicUtils::GetMaxStreamCount());
79
80 return true;
81 }
82
SetMaxOpenIncomingStreams(QuicStreamCount max_open_streams)83 void QuicStreamIdManager::SetMaxOpenIncomingStreams(
84 QuicStreamCount max_open_streams) {
85 QUIC_BUG_IF(quic_bug_12413_1, incoming_stream_count_ > 0)
86 << "non-zero incoming stream count " << incoming_stream_count_
87 << " when setting max incoming stream to " << max_open_streams;
88 QUIC_DLOG_IF(WARNING, incoming_initial_max_open_streams_ != max_open_streams)
89 << absl::StrCat(unidirectional_ ? "unidirectional " : "bidirectional: ",
90 "incoming stream limit changed from ",
91 incoming_initial_max_open_streams_, " to ",
92 max_open_streams);
93 incoming_actual_max_streams_ = max_open_streams;
94 incoming_advertised_max_streams_ = max_open_streams;
95 incoming_initial_max_open_streams_ = max_open_streams;
96 }
97
MaybeSendMaxStreamsFrame()98 void QuicStreamIdManager::MaybeSendMaxStreamsFrame() {
99 int divisor = GetQuicFlag(quic_max_streams_window_divisor);
100
101 if (divisor > 0) {
102 if ((incoming_advertised_max_streams_ - incoming_stream_count_) >
103 (incoming_initial_max_open_streams_ / divisor)) {
104 // window too large, no advertisement
105 return;
106 }
107 }
108 SendMaxStreamsFrame();
109 }
110
SendMaxStreamsFrame()111 void QuicStreamIdManager::SendMaxStreamsFrame() {
112 QUIC_BUG_IF(quic_bug_12413_2,
113 incoming_advertised_max_streams_ >= incoming_actual_max_streams_);
114 incoming_advertised_max_streams_ = incoming_actual_max_streams_;
115 delegate_->SendMaxStreams(incoming_advertised_max_streams_, unidirectional_);
116 }
117
OnStreamClosed(QuicStreamId stream_id)118 void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) {
119 QUICHE_DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id, version_),
120 unidirectional_);
121 if (QuicUtils::IsOutgoingStreamId(version_, stream_id, perspective_)) {
122 // Nothing to do for outgoing streams.
123 return;
124 }
125 // If the stream is inbound, we can increase the actual stream limit and maybe
126 // advertise the new limit to the peer.
127 if (incoming_actual_max_streams_ == QuicUtils::GetMaxStreamCount()) {
128 // Reached the maximum stream id value that the implementation
129 // supports. Nothing can be done here.
130 return;
131 }
132 // One stream closed, and another one can be opened.
133 incoming_actual_max_streams_++;
134 MaybeSendMaxStreamsFrame();
135 }
136
GetNextOutgoingStreamId()137 QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() {
138 QUIC_BUG_IF(quic_bug_12413_3, outgoing_stream_count_ >= outgoing_max_streams_)
139 << "Attempt to allocate a new outgoing stream that would exceed the "
140 "limit ("
141 << outgoing_max_streams_ << ")";
142 QuicStreamId id = next_outgoing_stream_id_;
143 next_outgoing_stream_id_ +=
144 QuicUtils::StreamIdDelta(version_.transport_version);
145 outgoing_stream_count_++;
146 return id;
147 }
148
CanOpenNextOutgoingStream() const149 bool QuicStreamIdManager::CanOpenNextOutgoingStream() const {
150 QUICHE_DCHECK(VersionHasIetfQuicFrames(version_.transport_version));
151 return outgoing_stream_count_ < outgoing_max_streams_;
152 }
153
MaybeIncreaseLargestPeerStreamId(const QuicStreamId stream_id,std::string * error_details)154 bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
155 const QuicStreamId stream_id, std::string* error_details) {
156 // |stream_id| must be an incoming stream of the right directionality.
157 QUICHE_DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id, version_),
158 unidirectional_);
159 QUICHE_DCHECK_NE(QuicUtils::IsServerInitiatedStreamId(
160 version_.transport_version, stream_id),
161 perspective_ == Perspective::IS_SERVER);
162 if (available_streams_.erase(stream_id) == 1) {
163 // stream_id is available.
164 return true;
165 }
166
167 if (largest_peer_created_stream_id_ !=
168 QuicUtils::GetInvalidStreamId(version_.transport_version)) {
169 QUICHE_DCHECK_GT(stream_id, largest_peer_created_stream_id_);
170 }
171
172 // Calculate increment of incoming_stream_count_ by creating stream_id.
173 const QuicStreamCount delta =
174 QuicUtils::StreamIdDelta(version_.transport_version);
175 const QuicStreamId least_new_stream_id =
176 largest_peer_created_stream_id_ ==
177 QuicUtils::GetInvalidStreamId(version_.transport_version)
178 ? GetFirstIncomingStreamId()
179 : largest_peer_created_stream_id_ + delta;
180 const QuicStreamCount stream_count_increment =
181 (stream_id - least_new_stream_id) / delta + 1;
182
183 if (incoming_stream_count_ + stream_count_increment >
184 incoming_advertised_max_streams_) {
185 QUIC_DLOG(INFO) << ENDPOINT
186 << "Failed to create a new incoming stream with id:"
187 << stream_id << ", reaching MAX_STREAMS limit: "
188 << incoming_advertised_max_streams_ << ".";
189 *error_details = absl::StrCat("Stream id ", stream_id,
190 " would exceed stream count limit ",
191 incoming_advertised_max_streams_);
192 return false;
193 }
194
195 for (QuicStreamId id = least_new_stream_id; id < stream_id; id += delta) {
196 available_streams_.insert(id);
197 }
198 incoming_stream_count_ += stream_count_increment;
199 largest_peer_created_stream_id_ = stream_id;
200 return true;
201 }
202
IsAvailableStream(QuicStreamId id) const203 bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
204 QUICHE_DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id, version_),
205 unidirectional_);
206 if (QuicUtils::IsOutgoingStreamId(version_, id, perspective_)) {
207 // Stream IDs under next_ougoing_stream_id_ are either open or previously
208 // open but now closed.
209 return id >= next_outgoing_stream_id_;
210 }
211 // For peer created streams, we also need to consider available streams.
212 return largest_peer_created_stream_id_ ==
213 QuicUtils::GetInvalidStreamId(version_.transport_version) ||
214 id > largest_peer_created_stream_id_ ||
215 available_streams_.contains(id);
216 }
217
GetFirstOutgoingStreamId() const218 QuicStreamId QuicStreamIdManager::GetFirstOutgoingStreamId() const {
219 return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
220 version_.transport_version, perspective_)
221 : QuicUtils::GetFirstBidirectionalStreamId(
222 version_.transport_version, perspective_);
223 }
224
GetFirstIncomingStreamId() const225 QuicStreamId QuicStreamIdManager::GetFirstIncomingStreamId() const {
226 return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
227 version_.transport_version,
228 QuicUtils::InvertPerspective(perspective_))
229 : QuicUtils::GetFirstBidirectionalStreamId(
230 version_.transport_version,
231 QuicUtils::InvertPerspective(perspective_));
232 }
233
available_incoming_streams() const234 QuicStreamCount QuicStreamIdManager::available_incoming_streams() const {
235 return incoming_advertised_max_streams_ - incoming_stream_count_;
236 }
237
238 } // namespace quic
239