Add Qpack stream classes. The streams are now stand-alone. They will be connected to QuicSpdySession in follow up CLs. gfe-relnote: n/a --unused code. PiperOrigin-RevId: 253243356 Change-Id: I23ca96ac64de16fe4f353a6801882dcbff93c998
diff --git a/quic/core/qpack/qpack_receive_stream.cc b/quic/core/qpack/qpack_receive_stream.cc new file mode 100644 index 0000000..1cf2060 --- /dev/null +++ b/quic/core/qpack/qpack_receive_stream.cc
@@ -0,0 +1,21 @@ +// Copyright 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. + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h" + +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" + +namespace quic { +QpackReceiveStream::QpackReceiveStream(PendingStream* pending) + : QuicStream(pending, READ_UNIDIRECTIONAL, /*is_static=*/true) {} + +void QpackReceiveStream::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 Qpack receive stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_receive_stream.h b/quic/core/qpack/qpack_receive_stream.h new file mode 100644 index 0000000..2f09f30 --- /dev/null +++ b/quic/core/qpack/qpack_receive_stream.h
@@ -0,0 +1,36 @@ +// Copyright 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_CORE_QPACK_QPACK_RECEIVE_STREAM_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_RECEIVE_STREAM_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; + +// QPACK 4.2.1 Encoder and Decoder Streams. +// The QPACK receive stream is peer initiated and is read only. +class QUIC_EXPORT_PRIVATE QpackReceiveStream : public QuicStream { + public: + // Construct receive stream from pending stream, the |pending| object needs + // to be deleted after the construction. + explicit QpackReceiveStream(PendingStream* pending); + QpackReceiveStream(const QpackReceiveStream&) = delete; + QpackReceiveStream& operator=(const QpackReceiveStream&) = delete; + ~QpackReceiveStream() override = default; + + // Overriding QuicStream::OnStreamReset to make sure QPACK stream is never + // closed before connection. + void OnStreamReset(const QuicRstStreamFrame& frame) override; + + // Implementation of QuicStream. Unimplemented yet. + // TODO(bnc): Feed data to QPACK. + void OnDataAvailable() override {} +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_RECEIVE_STREAM_H_
diff --git a/quic/core/qpack/qpack_receive_stream_test.cc b/quic/core/qpack/qpack_receive_stream_test.cc new file mode 100644 index 0000000..4ecbd79 --- /dev/null +++ b/quic/core/qpack/qpack_receive_stream_test.cc
@@ -0,0 +1,91 @@ +// Copyright 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. + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h" + +#include "net/third_party/quiche/src/quic/core/quic_utils.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 (!VersionHasStreamType(version.transport_version)) { + continue; + } + for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) { + params.emplace_back(version, p); + } + } + return params; +} + +class QpackReceiveStreamTest : public QuicTestWithParam<TestParams> { + public: + QpackReceiveStreamTest() + : connection_(new StrictMock<MockQuicConnection>( + &helper_, + &alarm_factory_, + perspective(), + SupportedVersions(GetParam().version))), + session_(connection_) { + session_.Initialize(); + PendingStream* pending = new PendingStream( + QuicUtils::GetFirstUnidirectionalStreamId( + GetParam().version.transport_version, + perspective() == Perspective::IS_CLIENT ? Perspective::IS_SERVER + : Perspective::IS_CLIENT), + &session_); + qpack_receive_stream_ = QuicMakeUnique<QpackReceiveStream>(pending); + delete pending; + } + + Perspective perspective() const { return GetParam().perspective; } + + MockQuicConnectionHelper helper_; + MockAlarmFactory alarm_factory_; + StrictMock<MockQuicConnection>* connection_; + StrictMock<MockQuicSpdySession> session_; + std::unique_ptr<QpackReceiveStream> qpack_receive_stream_; +}; + +INSTANTIATE_TEST_SUITE_P(Tests, + QpackReceiveStreamTest, + ::testing::ValuesIn(GetTestParams())); + +TEST_P(QpackReceiveStreamTest, ResetQpackReceiveStream) { + EXPECT_TRUE(qpack_receive_stream_->is_static()); + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, + qpack_receive_stream_->id(), + QUIC_STREAM_CANCELLED, 1234); + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); + qpack_receive_stream_->OnStreamReset(rst_frame); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/core/qpack/qpack_send_stream.cc b/quic/core/qpack/qpack_send_stream.cc new file mode 100644 index 0000000..73d62f3 --- /dev/null +++ b/quic/core/qpack/qpack_send_stream.cc
@@ -0,0 +1,40 @@ +// Copyright 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. + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.h" + +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" + +namespace quic { +QpackSendStream::QpackSendStream(QuicStreamId id, + QuicSpdySession* session, + uint64_t stream_type) + : QuicStream(id, session, /*is_static = */ true, WRITE_UNIDIRECTIONAL), + stream_type_(stream_type), + stream_type_sent_(false) {} + +void QpackSendStream::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 qpack send stream", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); +} + +void QpackSendStream::WriteStreamData(QuicStringPiece data) { + QuicConnection::ScopedPacketFlusher flusher( + session()->connection(), QuicConnection::SEND_ACK_IF_PENDING); + if (!stream_type_sent_) { + char type[sizeof(stream_type_)]; + QuicDataWriter writer(QUIC_ARRAYSIZE(type), type); + writer.WriteVarInt62(stream_type_); + WriteOrBufferData(QuicStringPiece(writer.data(), writer.length()), false, + nullptr); + stream_type_sent_ = true; + } + WriteOrBufferData(data, false, nullptr); +} + +} // namespace quic
diff --git a/quic/core/qpack/qpack_send_stream.h b/quic/core/qpack/qpack_send_stream.h new file mode 100644 index 0000000..dab4fd0 --- /dev/null +++ b/quic/core/qpack/qpack_send_stream.h
@@ -0,0 +1,47 @@ +// Copyright 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_CORE_QPACK_QPACK_SEND_STREAM_H_ +#define QUICHE_QUIC_CORE_QPACK_QPACK_SEND_STREAM_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; + +// QPACK 4.2.1 Encoder and Decoder Streams. +// The QPACK send stream is self initiated and is write only. +class QUIC_EXPORT_PRIVATE QpackSendStream : public QuicStream { + public: + // |session| can't be nullptr, and the ownership is not passed. |session| owns + // this stream. + QpackSendStream(QuicStreamId id, + QuicSpdySession* session, + uint64_t stream_type); + QpackSendStream(const QpackSendStream&) = delete; + QpackSendStream& operator=(const QpackSendStream&) = delete; + ~QpackSendStream() override = default; + + // Overriding QuicStream::OnStreamReset to make sure QPACK stream is + // never closed before connection. + void OnStreamReset(const QuicRstStreamFrame& frame) override; + + // The send QPACK stream is write unidirectional, so this method + // should never be called. + void OnDataAvailable() override { QUIC_NOTREACHED(); } + + // Writes the instructions to peer. The stream type will be sent + // before the first instruction so that the peer can open an qpack stream. + void WriteStreamData(QuicStringPiece data); + + private: + const uint64_t stream_type_; + bool stream_type_sent_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_QPACK_SEND_STREAM_H_
diff --git a/quic/core/qpack/qpack_send_stream_test.cc b/quic/core/qpack/qpack_send_stream_test.cc new file mode 100644 index 0000000..d31856b --- /dev/null +++ b/quic/core/qpack/qpack_send_stream_test.cc
@@ -0,0 +1,109 @@ +// Copyright 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. + +#include "net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.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 (!VersionHasStreamType(version.transport_version)) { + continue; + } + for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) { + params.emplace_back(version, p); + } + } + return params; +} + +class QpackSendStreamTest : public QuicTestWithParam<TestParams> { + public: + QpackSendStreamTest() + : connection_(new StrictMock<MockQuicConnection>( + &helper_, + &alarm_factory_, + perspective(), + SupportedVersions(GetParam().version))), + session_(connection_) { + session_.Initialize(); + qpack_send_stream_ = QuicMakeUnique<QpackSendStream>( + QuicSpdySessionPeer::GetNextOutgoingUnidirectionalStreamId(&session_), + &session_, kQpackEncoderStream); + 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_; + std::unique_ptr<QpackSendStream> qpack_send_stream_; +}; + +INSTANTIATE_TEST_SUITE_P(Tests, + QpackSendStreamTest, + ::testing::ValuesIn(GetTestParams())); + +TEST_P(QpackSendStreamTest, WriteStreamTypeOnlyFirstTime) { + if (GetParam().version.handshake_protocol == PROTOCOL_TLS1_3) { + // TODO(nharper, b/112643533): Figure out why this test fails when TLS is + // enabled and fix it. + return; + } + std::string data = "data"; + EXPECT_CALL(session_, WritevData(_, _, 1, _, _)); + EXPECT_CALL(session_, WritevData(_, _, data.length(), _, _)); + qpack_send_stream_->WriteStreamData(QuicStringPiece(data)); + + EXPECT_CALL(session_, WritevData(_, _, data.length(), _, _)); + qpack_send_stream_->WriteStreamData(QuicStringPiece(data)); +} + +TEST_P(QpackSendStreamTest, ResetQpackStream) { + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, qpack_send_stream_->id(), + QUIC_STREAM_CANCELLED, 1234); + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); + qpack_send_stream_->OnStreamReset(rst_frame); +} + +TEST_P(QpackSendStreamTest, ReceiveDataOnSendStream) { + QuicStreamFrame frame(qpack_send_stream_->id(), false, 0, "test"); + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM, _, _)); + qpack_send_stream_->OnStreamFrame(frame); +} + +} // namespace +} // namespace test +} // namespace quic