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 
6 #include <cstdint>
7 #include <string>
8 
9 #include "net/third_party/quiche/src/quic/core/quic_connection.h"
10 #include "net/third_party/quiche/src/quic/core/quic_constants.h"
11 #include "net/third_party/quiche/src/quic/core/quic_session.h"
12 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
13 #include "net/third_party/quiche/src/quic/core/quic_versions.h"
14 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
15 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
16 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
17 #include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.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,
26     bool unidirectional,
27     Perspective perspective,
28     ParsedQuicVersion version,
29     QuicStreamCount max_allowed_outgoing_streams,
30     QuicStreamCount max_allowed_incoming_streams)
31     : delegate_(delegate),
32       unidirectional_(unidirectional),
33       perspective_(perspective),
34       version_(version),
35       outgoing_max_streams_(max_allowed_outgoing_streams),
36       next_outgoing_stream_id_(GetFirstOutgoingStreamId()),
37       outgoing_stream_count_(0),
38       incoming_actual_max_streams_(max_allowed_incoming_streams),
39       incoming_advertised_max_streams_(max_allowed_incoming_streams),
40       incoming_initial_max_open_streams_(max_allowed_incoming_streams),
41       incoming_stream_count_(0),
42       largest_peer_created_stream_id_(
43           QuicUtils::GetInvalidStreamId(version.transport_version)) {}
44 
~QuicStreamIdManager()45 QuicStreamIdManager::~QuicStreamIdManager() {}
46 
OnStreamsBlockedFrame(const QuicStreamsBlockedFrame & frame,std::string * error_details)47 bool QuicStreamIdManager::OnStreamsBlockedFrame(
48     const QuicStreamsBlockedFrame& frame,
49     std::string* error_details) {
50   DCHECK_EQ(frame.unidirectional, unidirectional_);
51   if (frame.stream_count > incoming_advertised_max_streams_) {
52     // Peer thinks it can send more streams that we've told it.
53     *error_details = quiche::QuicheStrCat(
54         "StreamsBlockedFrame's stream count ", frame.stream_count,
55         " exceeds incoming max stream ", incoming_advertised_max_streams_);
56     return false;
57   }
58   DCHECK_LE(incoming_advertised_max_streams_, incoming_actual_max_streams_);
59   if (incoming_advertised_max_streams_ == incoming_actual_max_streams_) {
60     // We have told peer about current max.
61     return true;
62   }
63   if (frame.stream_count < incoming_actual_max_streams_) {
64     // Peer thinks it's blocked on a stream count that is less than our current
65     // max. Inform the peer of the correct stream count.
66     SendMaxStreamsFrame();
67   }
68   return true;
69 }
70 
MaybeAllowNewOutgoingStreams(QuicStreamCount max_open_streams)71 bool QuicStreamIdManager::MaybeAllowNewOutgoingStreams(
72     QuicStreamCount max_open_streams) {
73   if (max_open_streams <= outgoing_max_streams_) {
74     // Only update the stream count if it would increase the limit.
75     return false;
76   }
77 
78   // This implementation only supports 32 bit Stream IDs, so limit max streams
79   // if it would exceed the max 32 bits can express.
80   outgoing_max_streams_ =
81       std::min(max_open_streams, QuicUtils::GetMaxStreamCount());
82 
83   return true;
84 }
85 
SetMaxOpenIncomingStreams(QuicStreamCount max_open_streams)86 void QuicStreamIdManager::SetMaxOpenIncomingStreams(
87     QuicStreamCount max_open_streams) {
88   QUIC_BUG_IF(incoming_stream_count_ > 0)
89       << "non-zero incoming stream count " << incoming_stream_count_
90       << " when setting max incoming stream to " << max_open_streams;
91   QUIC_DLOG_IF(WARNING, incoming_initial_max_open_streams_ != max_open_streams)
92       << quiche::QuicheStrCat(
93              unidirectional_ ? "unidirectional " : "bidirectional: ",
94              "incoming stream limit changed from ",
95              incoming_initial_max_open_streams_, " to ", max_open_streams);
96   incoming_actual_max_streams_ = max_open_streams;
97   incoming_advertised_max_streams_ = max_open_streams;
98   incoming_initial_max_open_streams_ = max_open_streams;
99 }
100 
MaybeSendMaxStreamsFrame()101 void QuicStreamIdManager::MaybeSendMaxStreamsFrame() {
102   if ((incoming_advertised_max_streams_ - incoming_stream_count_) >
103       (incoming_initial_max_open_streams_ /
104        GetQuicFlag(FLAGS_quic_max_streams_window_divisor))) {
105     // window too large, no advertisement
106     return;
107   }
108   SendMaxStreamsFrame();
109 }
110 
SendMaxStreamsFrame()111 void QuicStreamIdManager::SendMaxStreamsFrame() {
112   QUIC_BUG_IF(incoming_advertised_max_streams_ >= incoming_actual_max_streams_);
113   incoming_advertised_max_streams_ = incoming_actual_max_streams_;
114   delegate_->SendMaxStreams(incoming_advertised_max_streams_, unidirectional_);
115 }
116 
OnStreamClosed(QuicStreamId stream_id)117 void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) {
118   DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id, version_),
119             unidirectional_);
120   if (QuicUtils::IsOutgoingStreamId(version_, stream_id, perspective_)) {
121     // Nothing to do for outgoing streams.
122     return;
123   }
124   // If the stream is inbound, we can increase the actual stream limit and maybe
125   // advertise the new limit to the peer.
126   if (incoming_actual_max_streams_ == QuicUtils::GetMaxStreamCount()) {
127     // Reached the maximum stream id value that the implementation
128     // supports. Nothing can be done here.
129     return;
130   }
131   // One stream closed, and another one can be opened.
132   incoming_actual_max_streams_++;
133   MaybeSendMaxStreamsFrame();
134 }
135 
GetNextOutgoingStreamId()136 QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() {
137   QUIC_BUG_IF(outgoing_stream_count_ >= outgoing_max_streams_)
138       << "Attempt to allocate a new outgoing stream that would exceed the "
139          "limit ("
140       << outgoing_max_streams_ << ")";
141   QuicStreamId id = next_outgoing_stream_id_;
142   next_outgoing_stream_id_ +=
143       QuicUtils::StreamIdDelta(version_.transport_version);
144   outgoing_stream_count_++;
145   return id;
146 }
147 
CanOpenNextOutgoingStream() const148 bool QuicStreamIdManager::CanOpenNextOutgoingStream() const {
149   DCHECK(VersionHasIetfQuicFrames(version_.transport_version));
150   return outgoing_stream_count_ < outgoing_max_streams_;
151 }
152 
MaybeIncreaseLargestPeerStreamId(const QuicStreamId stream_id,std::string * error_details)153 bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
154     const QuicStreamId stream_id,
155     std::string* error_details) {
156   // |stream_id| must be an incoming stream of the right directionality.
157   DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id, version_),
158             unidirectional_);
159   DCHECK_NE(QuicUtils::IsServerInitiatedStreamId(version_.transport_version,
160                                                  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     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 = quiche::QuicheStrCat("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   DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id, version_), unidirectional_);
205   if (QuicUtils::IsOutgoingStreamId(version_, id, perspective_)) {
206     // Stream IDs under next_ougoing_stream_id_ are either open or previously
207     // open but now closed.
208     return id >= next_outgoing_stream_id_;
209   }
210   // For peer created streams, we also need to consider available streams.
211   return largest_peer_created_stream_id_ ==
212              QuicUtils::GetInvalidStreamId(version_.transport_version) ||
213          id > largest_peer_created_stream_id_ ||
214          QuicContainsKey(available_streams_, id);
215 }
216 
GetFirstOutgoingStreamId() const217 QuicStreamId QuicStreamIdManager::GetFirstOutgoingStreamId() const {
218   return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
219                                  version_.transport_version, perspective_)
220                            : QuicUtils::GetFirstBidirectionalStreamId(
221                                  version_.transport_version, perspective_);
222 }
223 
GetFirstIncomingStreamId() const224 QuicStreamId QuicStreamIdManager::GetFirstIncomingStreamId() const {
225   return (unidirectional_) ? QuicUtils::GetFirstUnidirectionalStreamId(
226                                  version_.transport_version,
227                                  QuicUtils::InvertPerspective(perspective_))
228                            : QuicUtils::GetFirstBidirectionalStreamId(
229                                  version_.transport_version,
230                                  QuicUtils::InvertPerspective(perspective_));
231 }
232 
available_incoming_streams() const233 QuicStreamCount QuicStreamIdManager::available_incoming_streams() const {
234   return incoming_advertised_max_streams_ - incoming_stream_count_;
235 }
236 
237 }  // namespace quic
238