Don't use a flow controller for QuicCryptoStream when it uses CRYPTO frames
gfe-relnote: QuicCryptoStream change protected behind QUIC_VERSION_99
PiperOrigin-RevId: 248004335
Change-Id: I97d5b6ed0e5523a4264ce2f06e5f6c3249a99fe9
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index a859f87..85ae602 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -2005,11 +2005,16 @@
auto* server_session = static_cast<QuicSpdySession*>(GetServerSession());
ExpectFlowControlsSynced(client_session->flow_controller(),
server_session->flow_controller());
- ExpectFlowControlsSynced(
- QuicSessionPeer::GetMutableCryptoStream(client_session)
- ->flow_controller(),
- QuicSessionPeer::GetMutableCryptoStream(server_session)
- ->flow_controller());
+ if (!QuicVersionUsesCryptoFrames(client_->client()
+ ->client_session()
+ ->connection()
+ ->transport_version())) {
+ ExpectFlowControlsSynced(
+ QuicSessionPeer::GetMutableCryptoStream(client_session)
+ ->flow_controller(),
+ QuicSessionPeer::GetMutableCryptoStream(server_session)
+ ->flow_controller());
+ }
SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION);
SpdySettingsIR settings_frame;
settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE,
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index da920dc..1059a94 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -823,6 +823,12 @@
}
TEST_P(QuicSpdySessionTestServer, BufferedHandshake) {
+ // This tests prioritization of the crypto stream when flow control limits are
+ // reached. When CRYPTO frames are in use, there is no flow control for the
+ // crypto handshake, so this test is irrelevant.
+ if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ return;
+ }
session_.set_writev_consumes_all_data(true);
EXPECT_FALSE(session_.HasPendingHandshake()); // Default value.
@@ -910,8 +916,10 @@
// Mark the crypto and headers streams as write blocked, we expect them to be
// allowed to write later.
- session_.MarkConnectionLevelWriteBlocked(
- QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ session_.MarkConnectionLevelWriteBlocked(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+ }
// Create a data stream, and although it is write blocked we never expect it
// to be allowed to write as we are connection level flow control blocked.
@@ -921,10 +929,13 @@
// The crypto and headers streams should be called even though we are
// connection flow control blocked.
- TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
- EXPECT_CALL(*crypto_stream, OnCanWrite());
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite());
+ }
TestHeadersStream* headers_stream;
- if (!GetQuicReloadableFlag(quic_eliminate_static_stream_map_2)) {
+ if (!GetQuicReloadableFlag(quic_eliminate_static_stream_map_2) &&
+ !QuicVersionUsesCryptoFrames(connection_->transport_version())) {
QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr);
headers_stream = new TestHeadersStream(&session_);
QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream);
@@ -1213,6 +1224,11 @@
// various names that are dependent on the parameters passed.
TEST_P(QuicSpdySessionTestServer,
HandshakeUnblocksFlowControlBlockedHeadersStream) {
+ // This test depends on stream-level flow control for the crypto stream, which
+ // doesn't exist when CRYPTO frames are used.
+ if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ return;
+ }
// Test that if the header stream is flow control blocked, then if the SHLO
// contains a larger send window offset, the stream becomes unblocked.
session_.set_writev_consumes_all_data(true);
@@ -1644,7 +1660,8 @@
TEST_P(QuicSpdySessionTestClient, WritePriority) {
TestHeadersStream* headers_stream;
- if (!GetQuicReloadableFlag(quic_eliminate_static_stream_map_2)) {
+ if (!GetQuicReloadableFlag(quic_eliminate_static_stream_map_2) &&
+ !QuicVersionUsesCryptoFrames(connection_->transport_version())) {
QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr);
headers_stream = new TestHeadersStream(&session_);
QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream);
diff --git a/quic/core/quic_crypto_stream.cc b/quic/core/quic_crypto_stream.cc
index 836fbac..2263546 100644
--- a/quic/core/quic_crypto_stream.cc
+++ b/quic/core/quic_crypto_stream.cc
@@ -28,7 +28,10 @@
session->connection()->transport_version()),
session,
/*is_static=*/true,
- BIDIRECTIONAL),
+ QuicVersionUsesCryptoFrames(
+ session->connection()->transport_version())
+ ? CRYPTO
+ : BIDIRECTIONAL),
substreams_{{this, ENCRYPTION_INITIAL},
{this, ENCRYPTION_HANDSHAKE},
{this, ENCRYPTION_ZERO_RTT},
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 2505f7e..8bdf3b5 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -86,7 +86,8 @@
closed_streams_clean_up_alarm_(nullptr),
supported_versions_(supported_versions),
eliminate_static_stream_map_(
- GetQuicReloadableFlag(quic_eliminate_static_stream_map_2)) {
+ GetQuicReloadableFlag(quic_eliminate_static_stream_map_2) ||
+ QuicVersionUsesCryptoFrames(connection->transport_version())) {
closed_streams_clean_up_alarm_ =
QuicWrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm(
new ClosedStreamsCleanUpDelegate(this)));
@@ -1024,7 +1025,8 @@
for (auto const& kv : dynamic_stream_map_) {
kv.second->flow_controller()->UpdateReceiveWindowSize(stream_window);
}
- if (eliminate_static_stream_map_) {
+ if (eliminate_static_stream_map_ &&
+ !QuicVersionUsesCryptoFrames(connection_->transport_version())) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 11, 17);
GetMutableCryptoStream()->flow_controller()->UpdateReceiveWindowSize(
stream_window);
@@ -1074,7 +1076,8 @@
for (auto const& kv : dynamic_stream_map_) {
kv.second->UpdateSendWindowOffset(new_window);
}
- if (eliminate_static_stream_map_) {
+ if (eliminate_static_stream_map_ &&
+ !QuicVersionUsesCryptoFrames(connection_->transport_version())) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 12, 17);
GetMutableCryptoStream()->UpdateSendWindowOffset(new_window);
}
@@ -1454,6 +1457,7 @@
}
}
if (eliminate_static_stream_map_ &&
+ !QuicVersionUsesCryptoFrames(connection_->transport_version()) &&
GetMutableCryptoStream()->flow_controller()->IsBlocked()) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 14, 17);
return true;
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 1be72f8..a441dc4 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -952,6 +952,11 @@
}
TEST_P(QuicSessionTestServer, BufferedHandshake) {
+ // This test is testing behavior of crypto stream flow control, but when
+ // CRYPTO frames are used, there is no flow control for the crypto handshake.
+ if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ return;
+ }
session_.set_writev_consumes_all_data(true);
EXPECT_FALSE(session_.HasPendingHandshake()); // Default value.
@@ -1038,8 +1043,10 @@
// Mark the crypto and headers streams as write blocked, we expect them to be
// allowed to write later.
- session_.MarkConnectionLevelWriteBlocked(
- QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ session_.MarkConnectionLevelWriteBlocked(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+ }
// Create a data stream, and although it is write blocked we never expect it
// to be allowed to write as we are connection level flow control blocked.
@@ -1049,8 +1056,10 @@
// The crypto and headers streams should be called even though we are
// connection flow control blocked.
- TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
- EXPECT_CALL(*crypto_stream, OnCanWrite());
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite());
+ }
// After the crypto and header streams perform a write, the connection will be
// blocked by the flow control, hence it should become application-limited.
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index 0d654d6..32b168d 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -181,6 +181,30 @@
sequencer_.set_stream(this);
}
+namespace {
+
+QuicOptional<QuicFlowController> FlowController(QuicStreamId id,
+ QuicSession* session,
+ StreamType type) {
+ if (type == CRYPTO) {
+ // The only QuicStream with a StreamType of CRYPTO is QuicCryptoStream, when
+ // it is using crypto frames instead of stream frames. The QuicCryptoStream
+ // doesn't have any flow control in that case, so we don't create a
+ // QuicFlowController for it.
+ return QuicOptional<QuicFlowController>();
+ }
+ return QuicFlowController(
+ session, id,
+ /*is_connection_flow_controller*/ false,
+ GetReceivedFlowControlWindow(session),
+ GetInitialStreamFlowControlWindowToSend(session),
+ kStreamReceiveWindowLimit,
+ session->flow_controller()->auto_tune_receive_window(),
+ session->flow_controller());
+}
+
+} // namespace
+
QuicStream::QuicStream(QuicStreamId id,
QuicSession* session,
bool is_static,
@@ -192,15 +216,7 @@
type,
0,
false,
- QuicFlowController(
- session,
- id,
- /*is_connection_flow_controller*/ false,
- GetReceivedFlowControlWindow(session),
- GetInitialStreamFlowControlWindowToSend(session),
- kStreamReceiveWindowLimit,
- session->flow_controller()->auto_tune_receive_window(),
- session->flow_controller()),
+ FlowController(id, session, type),
session->flow_controller()) {}
QuicStream::QuicStream(QuicStreamId id,
@@ -210,7 +226,7 @@
StreamType type,
uint64_t stream_bytes_read,
bool fin_received,
- QuicFlowController flow_controller,
+ QuicOptional<QuicFlowController> flow_controller,
QuicFlowController* connection_flow_controller)
: sequencer_(std::move(sequencer)),
id_(id),
@@ -239,7 +255,8 @@
buffered_data_threshold_(GetQuicFlag(FLAGS_quic_buffered_data_threshold)),
is_static_(is_static),
deadline_(QuicTime::Zero()),
- type_(session->connection()->transport_version() == QUIC_VERSION_99
+ type_(session->connection()->transport_version() == QUIC_VERSION_99 &&
+ type != CRYPTO
? QuicUtils::GetStreamType(id_,
perspective_,
session->IsIncomingStream(id_))
@@ -252,7 +269,9 @@
CloseWriteSide();
}
SetFromConfig();
- session_->RegisterStreamPriority(id, is_static_, priority_);
+ if (type_ != CRYPTO) {
+ session_->RegisterStreamPriority(id, is_static_, priority_);
+ }
}
QuicStream::~QuicStream() {
@@ -263,7 +282,7 @@
<< send_buffer_.stream_bytes_outstanding()
<< ", fin_outstanding: " << fin_outstanding_;
}
- if (session_ != nullptr) {
+ if (session_ != nullptr && type_ != CRYPTO) {
session_->UnregisterStreamPriority(id(), is_static_);
}
}
@@ -323,7 +342,7 @@
MaybeIncreaseHighestReceivedOffset(frame.offset + frame_payload_size)) {
// As the highest received offset has changed, check to see if this is a
// violation of flow control.
- if (flow_controller_.FlowControlViolation() ||
+ if (flow_controller_->FlowControlViolation() ||
connection_flow_controller_->FlowControlViolation()) {
CloseConnectionWithDetails(
QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA,
@@ -352,7 +371,7 @@
return;
}
MaybeIncreaseHighestReceivedOffset(frame.byte_offset);
- if (flow_controller_.FlowControlViolation() ||
+ if (flow_controller_->FlowControlViolation() ||
connection_flow_controller_->FlowControlViolation()) {
CloseConnectionWithDetails(
QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA,
@@ -493,7 +512,7 @@
}
void QuicStream::MaybeSendBlocked() {
- if (flow_controller_.ShouldSendBlocked()) {
+ if (flow_controller_->ShouldSendBlocked()) {
session_->SendBlocked(id_);
}
if (!stream_contributes_to_connection_flow_control_) {
@@ -507,7 +526,7 @@
// the stream will be given a chance to write when a connection-level
// WINDOW_UPDATE arrives.
if (connection_flow_controller_->IsBlocked() &&
- !flow_controller_.IsBlocked()) {
+ !flow_controller_->IsBlocked()) {
session_->MarkConnectionLevelWriteBlocked(id());
}
}
@@ -707,7 +726,7 @@
rst_sent_ = true;
}
- if (flow_controller_.FlowControlViolation() ||
+ if (flow_controller_->FlowControlViolation() ||
connection_flow_controller_->FlowControlViolation()) {
return;
}
@@ -716,13 +735,13 @@
// the same connection level flow control state, mark all unreceived or
// buffered bytes as consumed.
QuicByteCount bytes_to_consume =
- flow_controller_.highest_received_byte_offset() -
- flow_controller_.bytes_consumed();
+ flow_controller_->highest_received_byte_offset() -
+ flow_controller_->bytes_consumed();
AddBytesConsumed(bytes_to_consume);
}
void QuicStream::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
- if (flow_controller_.UpdateSendWindowOffset(frame.byte_offset)) {
+ if (flow_controller_->UpdateSendWindowOffset(frame.byte_offset)) {
// Let session unblock this stream.
session_->MarkConnectionLevelWriteBlocked(id_);
}
@@ -731,8 +750,8 @@
bool QuicStream::MaybeIncreaseHighestReceivedOffset(
QuicStreamOffset new_offset) {
uint64_t increment =
- new_offset - flow_controller_.highest_received_byte_offset();
- if (!flow_controller_.UpdateHighestReceivedOffset(new_offset)) {
+ new_offset - flow_controller_->highest_received_byte_offset();
+ if (!flow_controller_->UpdateHighestReceivedOffset(new_offset)) {
return false;
}
@@ -748,16 +767,22 @@
}
void QuicStream::AddBytesSent(QuicByteCount bytes) {
- flow_controller_.AddBytesSent(bytes);
+ flow_controller_->AddBytesSent(bytes);
if (stream_contributes_to_connection_flow_control_) {
connection_flow_controller_->AddBytesSent(bytes);
}
}
void QuicStream::AddBytesConsumed(QuicByteCount bytes) {
+ if (type_ == CRYPTO) {
+ // A stream with type CRYPTO has no flow control, so there's nothing this
+ // function needs to do. This function still gets called by the
+ // QuicStreamSequencers used by QuicCryptoStream.
+ return;
+ }
// Only adjust stream level flow controller if still reading.
if (!read_side_closed_) {
- flow_controller_.AddBytesConsumed(bytes);
+ flow_controller_->AddBytesConsumed(bytes);
}
if (stream_contributes_to_connection_flow_control_) {
@@ -766,7 +791,7 @@
}
void QuicStream::UpdateSendWindowOffset(QuicStreamOffset new_window) {
- if (flow_controller_.UpdateSendWindowOffset(new_window)) {
+ if (flow_controller_->UpdateSendWindowOffset(new_window)) {
// Let session unblock this stream.
session_->MarkConnectionLevelWriteBlocked(id_);
}
@@ -919,7 +944,7 @@
bool fin = fin_buffered_;
// How much data flow control permits to be written.
- QuicByteCount send_window = flow_controller_.SendWindowSize();
+ QuicByteCount send_window = flow_controller_->SendWindowSize();
if (stream_contributes_to_connection_flow_control_) {
send_window =
std::min(send_window, connection_flow_controller_->SendWindowSize());
diff --git a/quic/core/quic_stream.h b/quic/core/quic_stream.h
index b6d03de..df1f7f8 100644
--- a/quic/core/quic_stream.h
+++ b/quic/core/quic_stream.h
@@ -30,6 +30,7 @@
#include "net/third_party/quiche/src/quic/core/session_notifier_interface.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_optional.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
@@ -224,7 +225,7 @@
int num_frames_received() const;
int num_duplicate_frames_received() const;
- QuicFlowController* flow_controller() { return &flow_controller_; }
+ QuicFlowController* flow_controller() { return &*flow_controller_; }
// Called when endpoint receives a frame which could increase the highest
// offset.
@@ -433,7 +434,7 @@
StreamType type,
uint64_t stream_bytes_read,
bool fin_received,
- QuicFlowController flow_controller,
+ QuicOptional<QuicFlowController> flow_controller,
QuicFlowController* connection_flow_controller);
// Subclasses and consumers should use reading_stopped.
@@ -504,7 +505,7 @@
// server or a client.
Perspective perspective_;
- QuicFlowController flow_controller_;
+ QuicOptional<QuicFlowController> flow_controller_;
// The connection level flow controller. Not owned.
QuicFlowController* connection_flow_controller_;
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index 199ec94..66a90c1 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -584,6 +584,9 @@
// Unidirectional streams carry data in one direction only.
WRITE_UNIDIRECTIONAL,
READ_UNIDIRECTIONAL,
+ // Not actually a stream type. Used only by QuicCryptoStream when it uses
+ // CRYPTO frames and isn't actually a QuicStream.
+ CRYPTO,
};
// A packet number space is the context in which a packet can be processed and
diff --git a/quic/platform/api/quic_optional.h b/quic/platform/api/quic_optional.h
new file mode 100644
index 0000000..921c3a8
--- /dev/null
+++ b/quic/platform/api/quic_optional.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2019 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.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_OPTIONAL_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_OPTIONAL_H_
+
+#include "net/quic/platform/impl/quic_optional_impl.h"
+
+namespace quic {
+
+template <typename T>
+using QuicOptional = QuicOptionalImpl<T>;
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_OPTIONAL_H_