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