summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc')
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc351
1 files changed, 351 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc
new file mode 100644
index 00000000000..fb6e14b8957
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc
@@ -0,0 +1,351 @@
+// 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 "net/third_party/quiche/src/quic/core/quic_stream_id_manager.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+#define ENDPOINT \
+ (session_->perspective() == Perspective::IS_SERVER ? " Server: " \
+ : " Client: ")
+
+QuicStreamIdManager::QuicStreamIdManager(
+ QuicSession* session,
+ QuicStreamId next_outgoing_stream_id,
+ QuicStreamId largest_peer_created_stream_id,
+ QuicStreamId first_incoming_dynamic_stream_id,
+ size_t max_allowed_outgoing_streams,
+ size_t max_allowed_incoming_streams)
+ : session_(session),
+ next_outgoing_stream_id_(next_outgoing_stream_id),
+ largest_peer_created_stream_id_(largest_peer_created_stream_id),
+ max_allowed_outgoing_stream_id_(0),
+ actual_max_allowed_incoming_stream_id_(0),
+ advertised_max_allowed_incoming_stream_id_(0),
+ max_stream_id_window_(max_allowed_incoming_streams /
+ kMaxStreamIdWindowDivisor),
+ max_allowed_incoming_streams_(max_allowed_incoming_streams),
+ first_incoming_dynamic_stream_id_(first_incoming_dynamic_stream_id),
+ first_outgoing_dynamic_stream_id_(next_outgoing_stream_id) {
+ available_incoming_streams_ = max_allowed_incoming_streams_;
+ SetMaxOpenOutgoingStreams(max_allowed_outgoing_streams);
+ SetMaxOpenIncomingStreams(max_allowed_incoming_streams);
+}
+
+QuicStreamIdManager::~QuicStreamIdManager() {
+ QUIC_LOG_IF(WARNING,
+ session_->num_locally_closed_incoming_streams_highest_offset() >
+ max_allowed_incoming_streams_)
+ << "Surprisingly high number of locally closed peer initiated streams"
+ "still waiting for final byte offset: "
+ << session_->num_locally_closed_incoming_streams_highest_offset();
+ QUIC_LOG_IF(WARNING,
+ session_->GetNumLocallyClosedOutgoingStreamsHighestOffset() >
+ max_allowed_outgoing_streams_)
+ << "Surprisingly high number of locally closed self initiated streams"
+ "still waiting for final byte offset: "
+ << session_->GetNumLocallyClosedOutgoingStreamsHighestOffset();
+}
+
+bool QuicStreamIdManager::OnMaxStreamIdFrame(
+ const QuicMaxStreamIdFrame& frame) {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(frame.max_stream_id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ // Need to determine whether the stream id matches our client/server
+ // perspective or not. If not, it's an error. If so, update appropriate
+ // maxima.
+ QUIC_CODE_COUNT_N(max_stream_id_received, 2, 2);
+ // TODO(fkastenholz): this test needs to be broader to handle uni- and bi-
+ // directional stream ids when that functionality is supported.
+ if (IsIncomingStream(frame.max_stream_id)) {
+ // TODO(fkastenholz): This, and following, closeConnection may
+ // need modification when proper support for IETF CONNECTION
+ // CLOSE is done.
+ QUIC_CODE_COUNT(max_stream_id_bad_direction);
+ session_->connection()->CloseConnection(
+ QUIC_MAX_STREAM_ID_ERROR,
+ "Received max stream ID with wrong initiator bit setting",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ // If a MAX_STREAM_ID advertises a stream ID that is smaller than previously
+ // advertised, it is to be ignored.
+ if (frame.max_stream_id < max_allowed_outgoing_stream_id_) {
+ QUIC_CODE_COUNT(max_stream_id_ignored);
+ return true;
+ }
+ max_allowed_outgoing_stream_id_ = frame.max_stream_id;
+
+ // Outgoing stream limit has increased, tell the applications
+ session_->OnCanCreateNewOutgoingStream();
+
+ return true;
+}
+
+bool QuicStreamIdManager::OnStreamIdBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame) {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(frame.stream_id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ QUIC_CODE_COUNT_N(stream_id_blocked_received, 2, 2);
+ QuicStreamId id = frame.stream_id;
+ if (!IsIncomingStream(frame.stream_id)) {
+ // Client/server mismatch, close the connection
+ // TODO(fkastenholz): revise when proper IETF Connection Close support is
+ // done.
+ QUIC_CODE_COUNT(stream_id_blocked_bad_direction);
+ session_->connection()->CloseConnection(
+ QUIC_STREAM_ID_BLOCKED_ERROR,
+ "Invalid stream ID directionality specified",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ if (id > advertised_max_allowed_incoming_stream_id_) {
+ // Peer thinks it can send more streams that we've told it.
+ // This is a protocol error.
+ // TODO(fkastenholz): revise when proper IETF Connection Close support is
+ // done.
+ QUIC_CODE_COUNT(stream_id_blocked_id_too_big);
+ session_->connection()->CloseConnection(
+ QUIC_STREAM_ID_BLOCKED_ERROR, "Invalid stream ID specified",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ if (id < actual_max_allowed_incoming_stream_id_) {
+ // Peer thinks it's blocked on an ID that is less than our current
+ // max. Inform the peer of the correct stream ID.
+ SendMaxStreamIdFrame();
+ return true;
+ }
+ // The peer's notion of the maximum ID is correct,
+ // there is nothing to do.
+ QUIC_CODE_COUNT(stream_id_blocked_id_correct);
+ return true;
+}
+
+// TODO(fkastenholz): Many changes will be needed here:
+// -- Use IETF QUIC server/client-initiation sense
+// -- Support both BIDI and UNI streams.
+// -- can not change the max number of streams after config negotiation has
+// been done.
+void QuicStreamIdManager::SetMaxOpenOutgoingStreams(size_t max_streams) {
+ max_allowed_outgoing_streams_ = max_streams;
+ max_allowed_outgoing_stream_id_ =
+ next_outgoing_stream_id_ + (max_streams - 1) * kV99StreamIdIncrement;
+}
+
+// TODO(fkastenholz): Many changes will be needed here:
+// -- can not change the max number of streams after config negotiation has
+// been done.
+// -- Currently uses the Google Client/server-initiation sense, needs to
+// be IETF.
+// -- Support both BIDI and UNI streams.
+// -- Convert calculation of the maximum ID from Google-QUIC semantics to IETF
+// QUIC semantics.
+void QuicStreamIdManager::SetMaxOpenIncomingStreams(size_t max_streams) {
+ max_allowed_incoming_streams_ = max_streams;
+ // The peer should always believe that it has the negotiated
+ // number of stream ids available for use.
+ available_incoming_streams_ = max_allowed_incoming_streams_;
+
+ // the window is a fraction of the peer's notion of its stream-id space.
+ max_stream_id_window_ =
+ available_incoming_streams_ / kMaxStreamIdWindowDivisor;
+ if (max_stream_id_window_ == 0) {
+ max_stream_id_window_ = 1;
+ }
+
+ actual_max_allowed_incoming_stream_id_ =
+ first_incoming_dynamic_stream_id_ +
+ (max_allowed_incoming_streams_ - 1) * kV99StreamIdIncrement;
+ // To start, we can assume advertised and actual are the same.
+ advertised_max_allowed_incoming_stream_id_ =
+ actual_max_allowed_incoming_stream_id_;
+}
+
+void QuicStreamIdManager::MaybeSendMaxStreamIdFrame() {
+ if (available_incoming_streams_ > max_stream_id_window_) {
+ // window too large, no advertisement
+ return;
+ }
+ // Calculate the number of streams that the peer will believe
+ // it has. The "/kV99StreamIdIncrement" converts from stream-id-
+ // values to number-of-stream-ids.
+ available_incoming_streams_ += (actual_max_allowed_incoming_stream_id_ -
+ advertised_max_allowed_incoming_stream_id_) /
+ kV99StreamIdIncrement;
+ SendMaxStreamIdFrame();
+}
+
+void QuicStreamIdManager::SendMaxStreamIdFrame() {
+ advertised_max_allowed_incoming_stream_id_ =
+ actual_max_allowed_incoming_stream_id_;
+ // And Advertise it.
+ session_->SendMaxStreamId(advertised_max_allowed_incoming_stream_id_);
+}
+
+void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(stream_id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ if (!IsIncomingStream(stream_id)) {
+ // Nothing to do for outbound streams with respect to the
+ // stream ID space management.
+ return;
+ }
+ // If the stream is inbound, we can increase the stream ID limit and maybe
+ // advertise the new limit to the peer.
+ if (actual_max_allowed_incoming_stream_id_ >=
+ (kMaxQuicStreamId - kV99StreamIdIncrement)) {
+ // Reached the maximum stream id value that the implementation
+ // supports. Nothing can be done here.
+ return;
+ }
+ actual_max_allowed_incoming_stream_id_ += kV99StreamIdIncrement;
+ MaybeSendMaxStreamIdFrame();
+}
+
+QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() {
+ QUIC_BUG_IF(next_outgoing_stream_id_ > max_allowed_outgoing_stream_id_)
+ << "Attempt allocate a new outgoing stream ID would exceed the limit";
+ QuicStreamId id = next_outgoing_stream_id_;
+ next_outgoing_stream_id_ += kV99StreamIdIncrement;
+ return id;
+}
+
+bool QuicStreamIdManager::CanOpenNextOutgoingStream() {
+ DCHECK_EQ(QUIC_VERSION_99, session_->connection()->transport_version());
+ if (next_outgoing_stream_id_ > max_allowed_outgoing_stream_id_) {
+ // Next stream ID would exceed the limit, need to inform the peer.
+ session_->SendStreamIdBlocked(max_allowed_outgoing_stream_id_);
+ QUIC_CODE_COUNT(reached_outgoing_stream_id_limit);
+ return false;
+ }
+ return true;
+}
+
+void QuicStreamIdManager::RegisterStaticStream(QuicStreamId stream_id) {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(stream_id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ QuicStreamId first_dynamic_stream_id = stream_id + kV99StreamIdIncrement;
+
+ if (IsIncomingStream(first_dynamic_stream_id)) {
+ // This code is predicated on static stream ids being allocated densely, in
+ // order, and starting with the first stream allowed. QUIC_BUG if this is
+ // not so.
+ QUIC_BUG_IF(stream_id > first_incoming_dynamic_stream_id_)
+ << "Error in incoming static stream allocation, expected to allocate "
+ << first_incoming_dynamic_stream_id_ << " got " << stream_id;
+
+ // This is a stream id for a stream that is started by the peer, deal with
+ // the incoming stream ids. Increase the floor and adjust everything
+ // accordingly.
+ if (stream_id == first_incoming_dynamic_stream_id_) {
+ actual_max_allowed_incoming_stream_id_ += kV99StreamIdIncrement;
+ first_incoming_dynamic_stream_id_ = first_dynamic_stream_id;
+ }
+ return;
+ }
+
+ // This code is predicated on static stream ids being allocated densely, in
+ // order, and starting with the first stream allowed. QUIC_BUG if this is
+ // not so.
+ QUIC_BUG_IF(stream_id > first_outgoing_dynamic_stream_id_)
+ << "Error in outgoing static stream allocation, expected to allocate "
+ << first_outgoing_dynamic_stream_id_ << " got " << stream_id;
+ // This is a stream id for a stream that is started by this node; deal with
+ // the outgoing stream ids. Increase the floor and adjust everything
+ // accordingly.
+ if (stream_id == first_outgoing_dynamic_stream_id_) {
+ max_allowed_outgoing_stream_id_ += kV99StreamIdIncrement;
+ first_outgoing_dynamic_stream_id_ = first_dynamic_stream_id;
+ }
+}
+
+bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
+ const QuicStreamId stream_id) {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(stream_id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ available_streams_.erase(stream_id);
+
+ if (largest_peer_created_stream_id_ !=
+ QuicUtils::GetInvalidStreamId(
+ session_->connection()->transport_version()) &&
+ stream_id <= largest_peer_created_stream_id_) {
+ return true;
+ }
+
+ if (stream_id > actual_max_allowed_incoming_stream_id_) {
+ // Desired stream ID is larger than the limit, do not increase.
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Failed to create a new incoming stream with id:"
+ << stream_id << ". Maximum allowed stream id is "
+ << actual_max_allowed_incoming_stream_id_ << ".";
+ session_->connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID,
+ QuicStrCat("Stream id ", stream_id, " above ",
+ actual_max_allowed_incoming_stream_id_),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ available_incoming_streams_--;
+
+ QuicStreamId id = largest_peer_created_stream_id_ + kV99StreamIdIncrement;
+ if (largest_peer_created_stream_id_ ==
+ QuicUtils::GetInvalidStreamId(
+ session_->connection()->transport_version())) {
+ // Adjust id based on perspective and whether stream_id is bidirectional or
+ // unidirectional.
+ if (QuicUtils::IsBidirectionalStreamId(stream_id)) {
+ // This should only happen on client side because server bidirectional
+ // stream ID manager's largest_peer_created_stream_id_ is initialized to
+ // the crypto stream ID.
+ DCHECK_EQ(Perspective::IS_CLIENT, session_->perspective());
+ id = 1;
+ } else {
+ id = session_->perspective() == Perspective::IS_SERVER ? 2 : 3;
+ }
+ }
+ for (; id < stream_id; id += kV99StreamIdIncrement) {
+ available_streams_.insert(id);
+ }
+ largest_peer_created_stream_id_ = stream_id;
+ return true;
+}
+
+bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ 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(
+ session_->connection()->transport_version()) ||
+ id > largest_peer_created_stream_id_ ||
+ QuicContainsKey(available_streams_, id);
+}
+
+bool QuicStreamIdManager::IsIncomingStream(QuicStreamId id) const {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ return id % kV99StreamIdIncrement !=
+ next_outgoing_stream_id_ % kV99StreamIdIncrement;
+}
+
+} // namespace quic