Introduce Control streams for HTTP/3.
gfe-relnote: n/a --unused code.
PiperOrigin-RevId: 242161401
Change-Id: I29928112a479348ef66636b442ea6065bd832194
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc
new file mode 100644
index 0000000..972dfc8
--- /dev/null
+++ b/quic/core/http/quic_receive_control_stream.cc
@@ -0,0 +1,165 @@
+// Copyright 2013 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/http/quic_receive_control_stream.h"
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_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"
+
+namespace quic {
+
+const uint16_t kSettingsMaxHeaderListSize = 6;
+const uint16_t kSettingsNumPlaceholders = 8;
+
+// Visitor of HttpDecoder that passes data frame to QuicSpdyStream and closes
+// the connection on unexpected frames.
+class QuicReceiveControlStream::HttpDecoderVisitor
+ : public HttpDecoder::Visitor {
+ public:
+ explicit HttpDecoderVisitor(QuicReceiveControlStream* stream)
+ : stream_(stream) {}
+ HttpDecoderVisitor(const HttpDecoderVisitor&) = delete;
+ HttpDecoderVisitor& operator=(const HttpDecoderVisitor&) = delete;
+
+ void OnError(HttpDecoder* decoder) override {
+ stream_->session()->connection()->CloseConnection(
+ QUIC_HTTP_DECODER_ERROR, "Http decoder internal error",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+
+ void OnPriorityFrame(const PriorityFrame& frame) override {
+ CloseConnectionOnWrongFrame("Priority");
+ }
+
+ void OnCancelPushFrame(const CancelPushFrame& frame) override {
+ CloseConnectionOnWrongFrame("Cancel Push");
+ }
+
+ void OnMaxPushIdFrame(const MaxPushIdFrame& frame) override {
+ CloseConnectionOnWrongFrame("Max Push Id");
+ }
+
+ void OnGoAwayFrame(const GoAwayFrame& frame) override {
+ CloseConnectionOnWrongFrame("Goaway");
+ }
+
+ void OnSettingsFrameStart(Http3FrameLengths frame_lengths) override {
+ stream_->OnSettingsFrameStart(frame_lengths);
+ }
+
+ void OnSettingsFrame(const SettingsFrame& frame) override {
+ stream_->OnSettingsFrame(frame);
+ }
+
+ void OnDuplicatePushFrame(const DuplicatePushFrame& frame) override {
+ CloseConnectionOnWrongFrame("Duplicate Push");
+ }
+
+ void OnDataFrameStart(Http3FrameLengths frame_lengths) override {
+ CloseConnectionOnWrongFrame("Data");
+ }
+
+ void OnDataFramePayload(QuicStringPiece payload) override {
+ CloseConnectionOnWrongFrame("Data");
+ }
+
+ void OnDataFrameEnd() override { CloseConnectionOnWrongFrame("Data"); }
+
+ void OnHeadersFrameStart(Http3FrameLengths frame_length) override {
+ CloseConnectionOnWrongFrame("Headers");
+ }
+
+ void OnHeadersFramePayload(QuicStringPiece payload) override {
+ CloseConnectionOnWrongFrame("Headers");
+ }
+
+ void OnHeadersFrameEnd(QuicByteCount frame_len) override {
+ CloseConnectionOnWrongFrame("Headers");
+ }
+
+ void OnPushPromiseFrameStart(PushId push_id) override {
+ CloseConnectionOnWrongFrame("Push Promise");
+ }
+
+ void OnPushPromiseFramePayload(QuicStringPiece payload) override {
+ CloseConnectionOnWrongFrame("Push Promise");
+ }
+
+ void OnPushPromiseFrameEnd() override {
+ CloseConnectionOnWrongFrame("Push Promise");
+ }
+
+ private:
+ void CloseConnectionOnWrongFrame(std::string frame_type) {
+ // TODO(renjietang): Change to HTTP/3 error type.
+ stream_->session()->connection()->CloseConnection(
+ QUIC_HTTP_DECODER_ERROR,
+ frame_type + " frame received on control stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+
+ QuicReceiveControlStream* stream_;
+};
+
+QuicReceiveControlStream::QuicReceiveControlStream(QuicStreamId id,
+ QuicSpdySession* session)
+ : QuicStream(id, session, /*is_static = */ true, READ_UNIDIRECTIONAL),
+ received_settings_length_(0),
+ http_decoder_visitor_(new HttpDecoderVisitor(this)) {
+ decoder_.set_visitor(http_decoder_visitor_.get());
+ sequencer()->set_level_triggered(true);
+}
+
+QuicReceiveControlStream::~QuicReceiveControlStream() {}
+
+void QuicReceiveControlStream::OnStreamReset(const QuicRstStreamFrame& frame) {
+ // TODO(renjietang) Change the error code to H/3 specific
+ // HTTP_CLOSED_CRITICAL_STREAM.
+ session()->connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Attempt to reset receive control stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+void QuicReceiveControlStream::OnDataAvailable() {
+ iovec iov;
+ while (!reading_stopped() && sequencer()->PrefetchNextRegion(&iov)) {
+ decoder_.ProcessInput(reinterpret_cast<const char*>(iov.iov_base),
+ iov.iov_len);
+ }
+}
+
+void QuicReceiveControlStream::OnSettingsFrameStart(
+ Http3FrameLengths frame_lengths) {
+ if (received_settings_length_ != 0) {
+ // TODO(renjietang): Change error code to HTTP_UNEXPECTED_FRAME.
+ session()->connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Settings frames are received twice.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ received_settings_length_ +=
+ frame_lengths.header_length + frame_lengths.payload_length;
+}
+
+void QuicReceiveControlStream::OnSettingsFrame(const SettingsFrame& settings) {
+ QuicSpdySession* spdy_session = static_cast<QuicSpdySession*>(session());
+ for (auto& it : settings.values) {
+ uint16_t setting_id = it.first;
+ switch (setting_id) {
+ case kSettingsMaxHeaderListSize:
+ spdy_session->set_max_inbound_header_list_size(it.second);
+ break;
+ case kSettingsNumPlaceholders:
+ // TODO: Support placeholder setting
+ break;
+ default:
+ break;
+ }
+ }
+ sequencer()->MarkConsumed(received_settings_length_);
+}
+
+} // namespace quic
diff --git a/quic/core/http/quic_receive_control_stream.h b/quic/core/http/quic_receive_control_stream.h
new file mode 100644
index 0000000..1805d6c
--- /dev/null
+++ b/quic/core/http/quic_receive_control_stream.h
@@ -0,0 +1,53 @@
+// Copyright 2013 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_CORE_HTTP_QUIC_RECEIVE_CONTROL_STREAM_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_RECEIVE_CONTROL_STREAM_H_
+
+#include "net/third_party/quiche/src/quic/core/http/http_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicSpdySession;
+
+// 3.2.1 Control Stream.
+// The receive control stream is peer initiated and is read only.
+class QUIC_EXPORT_PRIVATE QuicReceiveControlStream : public QuicStream {
+ public:
+ // |session| can't be nullptr, and the ownership is not passed. The stream can
+ // only be accessed through the session.
+ explicit QuicReceiveControlStream(QuicStreamId id, QuicSpdySession* session);
+ QuicReceiveControlStream(const QuicReceiveControlStream&) = delete;
+ QuicReceiveControlStream& operator=(const QuicReceiveControlStream&) = delete;
+ ~QuicReceiveControlStream() override;
+
+ // Overriding QuicStream::OnStreamReset to make sure control stream is never
+ // closed before connection.
+ void OnStreamReset(const QuicRstStreamFrame& frame) override;
+
+ // Implementation of QuicStream.
+ void OnDataAvailable() override;
+
+ protected:
+ // Called from HttpDecoderVisitor.
+ void OnSettingsFrameStart(Http3FrameLengths frame_lengths);
+ void OnSettingsFrame(const SettingsFrame& settings);
+
+ private:
+ class HttpDecoderVisitor;
+
+ HttpDecoder decoder_;
+
+ // Track the number of settings bytes received.
+ size_t received_settings_length_;
+
+ // HttpDecoder's visitor.
+ std::unique_ptr<HttpDecoderVisitor> http_decoder_visitor_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_RECEIVE_CONTROL_STREAM_H_
diff --git a/quic/core/http/quic_receive_control_stream_test.cc b/quic/core/http/quic_receive_control_stream_test.cc
new file mode 100644
index 0000000..1861d17
--- /dev/null
+++ b/quic/core/http/quic_receive_control_stream_test.cc
@@ -0,0 +1,172 @@
+// Copyright 2013 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/http/quic_receive_control_stream.h"
+
+#include <cstdint>
+#include <ostream>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/http/http_decoder.h"
+#include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.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_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+using testing::_;
+using testing::StrictMock;
+
+struct TestParams {
+ TestParams(const ParsedQuicVersion& version, Perspective perspective)
+ : version(version), perspective(perspective) {
+ QUIC_LOG(INFO) << "TestParams: version: "
+ << ParsedQuicVersionToString(version)
+ << ", perspective: " << perspective;
+ }
+
+ TestParams(const TestParams& other)
+ : version(other.version), perspective(other.perspective) {}
+
+ ParsedQuicVersion version;
+ Perspective perspective;
+};
+
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ ParsedQuicVersionVector all_supported_versions = AllSupportedVersions();
+ for (const auto& version : AllSupportedVersions()) {
+ if (!VersionHasControlStreams(version.transport_version)) {
+ continue;
+ }
+ for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) {
+ params.emplace_back(version, p);
+ }
+ }
+ return params;
+}
+
+class QuicReceiveControlStreamTest : public QuicTestWithParam<TestParams> {
+ public:
+ QuicReceiveControlStreamTest()
+ : connection_(new StrictMock<MockQuicConnection>(
+ &helper_,
+ &alarm_factory_,
+ perspective(),
+ SupportedVersions(GetParam().version))),
+ session_(connection_) {
+ session_.Initialize();
+ receive_control_stream_ = QuicMakeUnique<QuicReceiveControlStream>(
+ QuicUtils::GetFirstUnidirectionalStreamId(
+ GetParam().version.transport_version,
+ perspective() == Perspective::IS_CLIENT ? Perspective::IS_SERVER
+ : Perspective::IS_CLIENT),
+ &session_);
+ }
+
+ Perspective perspective() const { return GetParam().perspective; }
+
+ std::string EncodeSettings(const SettingsFrame& settings) {
+ HttpEncoder encoder;
+ std::unique_ptr<char[]> buffer;
+ auto header_length = encoder.SerializeSettingsFrame(settings, &buffer);
+ return std::string(buffer.get(), header_length);
+ }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ StrictMock<MockQuicSpdySession> session_;
+ HttpDecoder decoder_;
+ std::unique_ptr<QuicReceiveControlStream> receive_control_stream_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicReceiveControlStreamTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicReceiveControlStreamTest, ResetControlStream) {
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId,
+ receive_control_stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+ receive_control_stream_->OnStreamReset(rst_frame);
+}
+
+TEST_P(QuicReceiveControlStreamTest, ReceiveSettings) {
+ SettingsFrame settings;
+ settings.values[3] = 2;
+ settings.values[6] = 5;
+ std::string data = EncodeSettings(settings);
+ QuicStreamFrame frame(receive_control_stream_->id(), false, 0,
+ QuicStringPiece(data));
+ EXPECT_NE(5u, session_.max_inbound_header_list_size());
+ receive_control_stream_->OnStreamFrame(frame);
+ EXPECT_EQ(5u, session_.max_inbound_header_list_size());
+}
+
+TEST_P(QuicReceiveControlStreamTest, ReceiveSettingsTwice) {
+ SettingsFrame settings;
+ settings.values[3] = 2;
+ settings.values[6] = 5;
+ std::string data = EncodeSettings(settings);
+ QuicStreamFrame frame(receive_control_stream_->id(), false, 0,
+ QuicStringPiece(data));
+ QuicStreamFrame frame2(receive_control_stream_->id(), false, data.length(),
+ QuicStringPiece(data));
+ receive_control_stream_->OnStreamFrame(frame);
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Settings frames are received twice.", _));
+ receive_control_stream_->OnStreamFrame(frame2);
+}
+
+TEST_P(QuicReceiveControlStreamTest, ReceiveSettingsFragments) {
+ SettingsFrame settings;
+ settings.values[3] = 2;
+ settings.values[6] = 5;
+ std::string data = EncodeSettings(settings);
+ std::string data1 = data.substr(0, 1);
+ std::string data2 = data.substr(1, data.length() - 1);
+
+ QuicStreamFrame frame(receive_control_stream_->id(), false, 0,
+ QuicStringPiece(data.data(), 1));
+ QuicStreamFrame frame2(receive_control_stream_->id(), false, 1,
+ QuicStringPiece(data.data() + 1, data.length() - 1));
+ EXPECT_NE(5u, session_.max_inbound_header_list_size());
+ receive_control_stream_->OnStreamFrame(frame);
+ receive_control_stream_->OnStreamFrame(frame2);
+ EXPECT_EQ(5u, session_.max_inbound_header_list_size());
+}
+
+TEST_P(QuicReceiveControlStreamTest, ReceiveWrongFrame) {
+ GoAwayFrame goaway;
+ goaway.stream_id = 0x1;
+ HttpEncoder encoder;
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length = encoder.SerializeGoAwayFrame(goaway, &buffer);
+ std::string data = std::string(buffer.get(), header_length);
+
+ QuicStreamFrame frame(receive_control_stream_->id(), false, 0,
+ QuicStringPiece(data));
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_DECODER_ERROR, _, _));
+ receive_control_stream_->OnStreamFrame(frame);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/quic/core/http/quic_send_control_stream.cc b/quic/core/http/quic_send_control_stream.cc
new file mode 100644
index 0000000..40b6111
--- /dev/null
+++ b/quic/core/http/quic_send_control_stream.cc
@@ -0,0 +1,37 @@
+// Copyright 2013 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/http/quic_send_control_stream.h"
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_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"
+
+namespace quic {
+
+QuicSendControlStream::QuicSendControlStream(QuicStreamId id,
+ QuicSpdySession* session)
+ : QuicStream(id, session, /*is_static = */ true, WRITE_UNIDIRECTIONAL),
+ settings_sent_(false) {}
+
+void QuicSendControlStream::OnStreamReset(const QuicRstStreamFrame& frame) {
+ // TODO(renjietang) Change the error code to H/3 specific
+ // HTTP_CLOSED_CRITICAL_STREAM.
+ session()->connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Attempt to reset send control stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+void QuicSendControlStream::SendSettingsFrame(const SettingsFrame& settings) {
+ DCHECK(!settings_sent_);
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount frame_length =
+ encoder_.SerializeSettingsFrame(settings, &buffer);
+ WriteOrBufferData(QuicStringPiece(buffer.get(), frame_length),
+ /*fin = */ false, nullptr);
+ settings_sent_ = true;
+}
+
+} // namespace quic
diff --git a/quic/core/http/quic_send_control_stream.h b/quic/core/http/quic_send_control_stream.h
new file mode 100644
index 0000000..09bdafb
--- /dev/null
+++ b/quic/core/http/quic_send_control_stream.h
@@ -0,0 +1,47 @@
+// Copyright 2013 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_CORE_HTTP_QUIC_SEND_CONTROL_STREAM_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_SEND_CONTROL_STREAM_H_
+
+#include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicSpdySession;
+
+// 3.2.1 Control Stream.
+// The send control stream is self initiated and is write only.
+class QUIC_EXPORT_PRIVATE QuicSendControlStream : public QuicStream {
+ public:
+ // |session| can't be nullptr, and the ownership is not passed. The stream can
+ // only be accessed through the session.
+ explicit QuicSendControlStream(QuicStreamId id, QuicSpdySession* session);
+ QuicSendControlStream(const QuicSendControlStream&) = delete;
+ QuicSendControlStream& operator=(const QuicSendControlStream&) = delete;
+ ~QuicSendControlStream() override = default;
+
+ // Overriding QuicStream::OnStreamReset to make sure control stream is never
+ // closed before connection.
+ void OnStreamReset(const QuicRstStreamFrame& frame) override;
+
+ // Send |settings| on this stream.
+ // Settings frame must be the first frame sent on this stream.
+ void SendSettingsFrame(const SettingsFrame& settings);
+
+ // The send control stream is write unidirectional, so this method should
+ // never be called.
+ void OnDataAvailable() override { QUIC_NOTREACHED(); }
+
+ private:
+ HttpEncoder encoder_;
+ // Track if a settings frame is already sent.
+ bool settings_sent_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SEND_CONTROL_STREAM_H_
diff --git a/quic/core/http/quic_send_control_stream_test.cc b/quic/core/http/quic_send_control_stream_test.cc
new file mode 100644
index 0000000..980cdf5
--- /dev/null
+++ b/quic/core/http/quic_send_control_stream_test.cc
@@ -0,0 +1,124 @@
+// Copyright 2013 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/http/quic_send_control_stream.h"
+
+#include <cstdint>
+#include <ostream>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.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_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+using testing::_;
+using testing::Invoke;
+using testing::StrictMock;
+
+struct TestParams {
+ TestParams(const ParsedQuicVersion& version, Perspective perspective)
+ : version(version), perspective(perspective) {
+ QUIC_LOG(INFO) << "TestParams: version: "
+ << ParsedQuicVersionToString(version)
+ << ", perspective: " << perspective;
+ }
+
+ TestParams(const TestParams& other)
+ : version(other.version), perspective(other.perspective) {}
+
+ ParsedQuicVersion version;
+ Perspective perspective;
+};
+
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ ParsedQuicVersionVector all_supported_versions = AllSupportedVersions();
+ for (const auto& version : AllSupportedVersions()) {
+ if (!VersionHasControlStreams(version.transport_version)) {
+ continue;
+ }
+ for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) {
+ params.emplace_back(version, p);
+ }
+ }
+ return params;
+}
+
+class QuicSendControlStreamTest : public QuicTestWithParam<TestParams> {
+ public:
+ QuicSendControlStreamTest()
+ : connection_(new StrictMock<MockQuicConnection>(
+ &helper_,
+ &alarm_factory_,
+ perspective(),
+ SupportedVersions(GetParam().version))),
+ session_(connection_) {
+ session_.Initialize();
+ send_control_stream_ = QuicMakeUnique<QuicSendControlStream>(
+ QuicSpdySessionPeer::GetNextOutgoingUnidirectionalStreamId(&session_),
+ &session_);
+ ON_CALL(session_, WritevData(_, _, _, _, _))
+ .WillByDefault(Invoke(MockQuicSession::ConsumeData));
+ }
+
+ Perspective perspective() const { return GetParam().perspective; }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ StrictMock<MockQuicSpdySession> session_;
+ HttpEncoder encoder_;
+ std::unique_ptr<QuicSendControlStream> send_control_stream_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSendControlStreamTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicSendControlStreamTest, WriteSettingsOnStartUp) {
+ SettingsFrame settings;
+ settings.values[3] = 2;
+ settings.values[6] = 5;
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount frame_length =
+ encoder_.SerializeSettingsFrame(settings, &buffer);
+
+ EXPECT_CALL(session_, WritevData(_, _, frame_length, _, _));
+ send_control_stream_->SendSettingsFrame(settings);
+}
+
+TEST_P(QuicSendControlStreamTest, ResetControlStream) {
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId,
+ send_control_stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+ send_control_stream_->OnStreamReset(rst_frame);
+}
+
+TEST_P(QuicSendControlStreamTest, ReceiveDataOnSendControlStream) {
+ QuicStreamFrame frame(send_control_stream_->id(), false, 0, "test");
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM, _, _));
+ send_control_stream_->OnStreamFrame(frame);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index c968284..0ff1150 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -158,6 +158,10 @@
max_inbound_header_list_size_ = max_inbound_header_list_size;
}
+ size_t max_inbound_header_list_size() const {
+ return max_inbound_header_list_size_;
+ }
+
// Returns true if the session has active request streams.
bool HasActiveRequestStreams() const;
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h
index 2c78760..c270f95 100644
--- a/quic/core/quic_versions.h
+++ b/quic/core/quic_versions.h
@@ -353,6 +353,12 @@
return transport_version == QUIC_VERSION_99;
}
+// Returns whether |transport_version| has HTTP/3 Control stream.
+QUIC_EXPORT_PRIVATE inline bool VersionHasControlStreams(
+ QuicTransportVersion transport_version) {
+ return transport_version == QUIC_VERSION_99;
+}
+
// Initializes support for the provided IETF draft version by setting flags
// and the version label.
QUIC_EXPORT_PRIVATE void QuicVersionInitializeSupportForIetfDraft(
diff --git a/quic/core/quic_write_blocked_list.h b/quic/core/quic_write_blocked_list.h
index df6b27d..83c2e20 100644
--- a/quic/core/quic_write_blocked_list.h
+++ b/quic/core/quic_write_blocked_list.h
@@ -189,9 +189,6 @@
// Add |id| to the collection in unblocked state.
void Register(QuicStreamId id) {
DCHECK(!IsRegistered(id));
- DCHECK(streams_.empty() || id > streams_.back().id)
- << "stream_id: " << id
- << " last static stream: " << streams_.back().id;
streams_.push_back({id, false});
}
diff --git a/quic/test_tools/quic_spdy_session_peer.cc b/quic/test_tools/quic_spdy_session_peer.cc
index b739fa1..dcba12c 100644
--- a/quic/test_tools/quic_spdy_session_peer.cc
+++ b/quic/test_tools/quic_spdy_session_peer.cc
@@ -61,5 +61,11 @@
id, std::move(headers), fin, priority, std::move(ack_listener));
}
+// static
+QuicStreamId QuicSpdySessionPeer::GetNextOutgoingUnidirectionalStreamId(
+ QuicSpdySession* session) {
+ return session->GetNextOutgoingUnidirectionalStreamId();
+}
+
} // namespace test
} // namespace quic
diff --git a/quic/test_tools/quic_spdy_session_peer.h b/quic/test_tools/quic_spdy_session_peer.h
index 58a864d..83e8b0c 100644
--- a/quic/test_tools/quic_spdy_session_peer.h
+++ b/quic/test_tools/quic_spdy_session_peer.h
@@ -41,6 +41,9 @@
bool fin,
spdy::SpdyPriority priority,
QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+ // |session| can't be nullptr.
+ static QuicStreamId GetNextOutgoingUnidirectionalStreamId(
+ QuicSpdySession* session);
};
} // namespace test