summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quic/quic_transport
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/third_party/quiche/src/quic/quic_transport')
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.cc130
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h62
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session_test.cc103
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_integration_test.cc331
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h7
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.cc10
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h15
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session_test.cc12
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h4
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc112
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h72
-rw-r--r--chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream_test.cc123
12 files changed, 904 insertions, 77 deletions
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.cc
index 032f61d3bcb..e9487ec0458 100644
--- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.cc
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.cc
@@ -16,9 +16,12 @@
#include "net/third_party/quiche/src/quic/core/quic_session.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
namespace quic {
@@ -40,15 +43,18 @@ QuicTransportClientSession::QuicTransportClientSession(
Visitor* owner,
const QuicConfig& config,
const ParsedQuicVersionVector& supported_versions,
- const QuicServerId& server_id,
+ const GURL& url,
QuicCryptoClientConfig* crypto_config,
- url::Origin origin)
+ url::Origin origin,
+ ClientVisitor* visitor)
: QuicSession(connection,
owner,
config,
supported_versions,
/*num_expected_unidirectional_static_streams*/ 0),
- origin_(origin) {
+ url_(url),
+ origin_(origin),
+ visitor_(visitor) {
for (const ParsedQuicVersion& version : supported_versions) {
QUIC_BUG_IF(version.handshake_protocol != PROTOCOL_TLS1_3)
<< "QuicTransport requires TLS 1.3 handshake";
@@ -56,8 +62,22 @@ QuicTransportClientSession::QuicTransportClientSession(
// ProofHandler API is not used by TLS 1.3.
static DummyProofHandler* proof_handler = new DummyProofHandler();
crypto_stream_ = std::make_unique<QuicCryptoClientStream>(
- server_id, this, crypto_config->proof_verifier()->CreateDefaultContext(),
- crypto_config, proof_handler);
+ QuicServerId(url.host(), url.EffectiveIntPort()), this,
+ crypto_config->proof_verifier()->CreateDefaultContext(), crypto_config,
+ proof_handler);
+}
+
+QuicStream* QuicTransportClientSession::CreateIncomingStream(QuicStreamId id) {
+ QUIC_DVLOG(1) << "Creating incoming QuicTransport stream " << id;
+ QuicTransportStream* stream = CreateStream(id);
+ if (stream->type() == BIDIRECTIONAL) {
+ incoming_bidirectional_streams_.push_back(stream);
+ visitor_->OnIncomingBidirectionalStreamAvailable();
+ } else {
+ incoming_unidirectional_streams_.push_back(stream);
+ visitor_->OnIncomingUnidirectionalStreamAvailable();
+ }
+ return stream;
}
void QuicTransportClientSession::OnCryptoHandshakeEvent(
@@ -70,6 +90,59 @@ void QuicTransportClientSession::OnCryptoHandshakeEvent(
SendClientIndication();
}
+void QuicTransportClientSession::SetDefaultEncryptionLevel(
+ EncryptionLevel level) {
+ QuicSession::SetDefaultEncryptionLevel(level);
+ if (level == ENCRYPTION_FORWARD_SECURE) {
+ SendClientIndication();
+ }
+}
+
+QuicTransportStream*
+QuicTransportClientSession::AcceptIncomingBidirectionalStream() {
+ if (incoming_bidirectional_streams_.empty()) {
+ return nullptr;
+ }
+ QuicTransportStream* stream = incoming_bidirectional_streams_.front();
+ incoming_bidirectional_streams_.pop_front();
+ return stream;
+}
+
+QuicTransportStream*
+QuicTransportClientSession::AcceptIncomingUnidirectionalStream() {
+ if (incoming_unidirectional_streams_.empty()) {
+ return nullptr;
+ }
+ QuicTransportStream* stream = incoming_unidirectional_streams_.front();
+ incoming_unidirectional_streams_.pop_front();
+ return stream;
+}
+
+QuicTransportStream*
+QuicTransportClientSession::OpenOutgoingBidirectionalStream() {
+ if (!CanOpenNextOutgoingBidirectionalStream()) {
+ QUIC_BUG << "Attempted to open a stream in violation of flow control";
+ return nullptr;
+ }
+ return CreateStream(GetNextOutgoingBidirectionalStreamId());
+}
+
+QuicTransportStream*
+QuicTransportClientSession::OpenOutgoingUnidirectionalStream() {
+ if (!CanOpenNextOutgoingUnidirectionalStream()) {
+ QUIC_BUG << "Attempted to open a stream in violation of flow control";
+ return nullptr;
+ }
+ return CreateStream(GetNextOutgoingUnidirectionalStreamId());
+}
+
+QuicTransportStream* QuicTransportClientSession::CreateStream(QuicStreamId id) {
+ auto stream = std::make_unique<QuicTransportStream>(id, this, this);
+ QuicTransportStream* stream_ptr = stream.get();
+ ActivateStream(std::move(stream));
+ return stream_ptr;
+}
+
std::string QuicTransportClientSession::SerializeClientIndication() {
std::string serialized_origin = origin_.Serialize();
if (serialized_origin.size() > std::numeric_limits<uint16_t>::max()) {
@@ -82,16 +155,40 @@ std::string QuicTransportClientSession::SerializeClientIndication() {
QUIC_DLOG(INFO) << "Sending client indication with origin "
<< serialized_origin;
+ std::string path = url_.PathForRequest();
+ if (path.size() > std::numeric_limits<uint16_t>::max()) {
+ connection()->CloseConnection(
+ QUIC_TRANSPORT_INVALID_CLIENT_INDICATION, "Requested URL path too long",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return "";
+ }
+
+ constexpr size_t kPrefixSize =
+ sizeof(QuicTransportClientIndicationKeys) + sizeof(uint16_t);
+ const size_t buffer_size =
+ 2 * kPrefixSize + serialized_origin.size() + path.size();
+ if (buffer_size > std::numeric_limits<uint16_t>::max()) {
+ connection()->CloseConnection(
+ QUIC_TRANSPORT_INVALID_CLIENT_INDICATION,
+ "Client indication size limit exceeded",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return "";
+ }
+
std::string buffer;
- buffer.resize(/* key */ sizeof(QuicTransportClientIndicationKeys) +
- /* length */ sizeof(uint16_t) + serialized_origin.size());
+ buffer.resize(buffer_size);
QuicDataWriter writer(buffer.size(), &buffer[0]);
- writer.WriteUInt16(
- static_cast<uint16_t>(QuicTransportClientIndicationKeys::kOrigin));
- writer.WriteUInt16(serialized_origin.size());
- writer.WriteStringPiece(serialized_origin);
-
- buffer.resize(writer.length());
+ bool success =
+ writer.WriteUInt16(
+ static_cast<uint16_t>(QuicTransportClientIndicationKeys::kOrigin)) &&
+ writer.WriteUInt16(serialized_origin.size()) &&
+ writer.WriteStringPiece(serialized_origin) &&
+ writer.WriteUInt16(
+ static_cast<uint16_t>(QuicTransportClientIndicationKeys::kPath)) &&
+ writer.WriteUInt16(path.size()) && writer.WriteStringPiece(path);
+ QUIC_BUG_IF(!success) << "Failed to serialize client indication";
+ QUIC_BUG_IF(writer.length() != buffer.length())
+ << "Serialized client indication has length different from expected";
return buffer;
}
@@ -113,8 +210,11 @@ void QuicTransportClientSession::SendClientIndication() {
}
auto client_indication_owned = std::make_unique<ClientIndication>(
- /*stream_id=*/ClientIndicationStream(), this, /*is_static=*/false,
- WRITE_UNIDIRECTIONAL);
+ /*stream_id=*/GetNextOutgoingUnidirectionalStreamId(), this,
+ /*is_static=*/false, WRITE_UNIDIRECTIONAL);
+ QUIC_BUG_IF(client_indication_owned->id() != ClientIndicationStream())
+ << "Client indication stream is " << client_indication_owned->id()
+ << " instead of expected " << ClientIndicationStream();
ClientIndication* client_indication = client_indication_owned.get();
ActivateStream(std::move(client_indication_owned));
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h
index 32149dfbb00..b3d8f17186b 100644
--- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h
@@ -8,6 +8,7 @@
#include <cstdint>
#include <memory>
+#include "url/gurl.h"
#include "url/origin.h"
#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
#include "net/third_party/quiche/src/quic/core/quic_config.h"
@@ -19,24 +20,38 @@
#include "net/third_party/quiche/src/quic/core/quic_stream.h"
#include "net/third_party/quiche/src/quic/core/quic_versions.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
namespace quic {
// A client session for the QuicTransport protocol.
-class QUIC_EXPORT QuicTransportClientSession
+class QUIC_EXPORT_PRIVATE QuicTransportClientSession
: public QuicSession,
public QuicTransportSessionInterface {
public:
+ class QUIC_EXPORT_PRIVATE ClientVisitor {
+ public:
+ virtual ~ClientVisitor() {}
+
+ // Notifies the visitor when a new stream has been received. The stream in
+ // question can be retrieved using AcceptIncomingBidirectionalStream() or
+ // AcceptIncomingUnidirectionalStream().
+ virtual void OnIncomingBidirectionalStreamAvailable() = 0;
+ virtual void OnIncomingUnidirectionalStreamAvailable() = 0;
+ };
+
QuicTransportClientSession(QuicConnection* connection,
Visitor* owner,
const QuicConfig& config,
const ParsedQuicVersionVector& supported_versions,
- const QuicServerId& server_id,
+ const GURL& url,
QuicCryptoClientConfig* crypto_config,
- url::Origin origin);
+ url::Origin origin,
+ ClientVisitor* visitor);
std::vector<std::string> GetAlpnsToOffer() const override {
return std::vector<std::string>({QuicTransportAlpn()});
@@ -53,12 +68,35 @@ class QUIC_EXPORT QuicTransportClientSession
return crypto_stream_.get();
}
+ // Returns true once the encryption has been established and the client
+ // indication has been sent. No application data will be read or written
+ // before the connection is ready. Once the connection becomes ready, this
+ // method will never return false.
bool IsSessionReady() const override { return ready_; }
+ QuicStream* CreateIncomingStream(QuicStreamId id) override;
+ QuicStream* CreateIncomingStream(PendingStream* /*pending*/) override {
+ QUIC_BUG << "QuicTransportClientSession::CreateIncomingStream("
+ "PendingStream) not implemented";
+ return nullptr;
+ }
+
void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+ void SetDefaultEncryptionLevel(EncryptionLevel level) override;
+
+ // Return the earliest incoming stream that has been received by the session
+ // but has not been accepted. Returns nullptr if there are no incoming
+ // streams.
+ QuicTransportStream* AcceptIncomingBidirectionalStream();
+ QuicTransportStream* AcceptIncomingUnidirectionalStream();
+
+ using QuicSession::CanOpenNextOutgoingBidirectionalStream;
+ using QuicSession::CanOpenNextOutgoingUnidirectionalStream;
+ QuicTransportStream* OpenOutgoingBidirectionalStream();
+ QuicTransportStream* OpenOutgoingUnidirectionalStream();
protected:
- class ClientIndication : public QuicStream {
+ class QUIC_EXPORT_PRIVATE ClientIndication : public QuicStream {
public:
using QuicStream::QuicStream;
@@ -69,6 +107,9 @@ class QUIC_EXPORT QuicTransportClientSession
}
};
+ // Creates and activates a QuicTransportStream for the given ID.
+ QuicTransportStream* CreateStream(QuicStreamId id);
+
// Serializes the client indication as described in
// https://vasilvv.github.io/webtransport/draft-vvv-webtransport-quic.html#rfc.section.3.2
std::string SerializeClientIndication();
@@ -76,9 +117,22 @@ class QUIC_EXPORT QuicTransportClientSession
void SendClientIndication();
std::unique_ptr<QuicCryptoClientStream> crypto_stream_;
+ GURL url_;
url::Origin origin_;
+ ClientVisitor* visitor_; // not owned
bool client_indication_sent_ = false;
bool ready_ = false;
+
+ // Contains all of the streams that has been received by the session but have
+ // not been processed by the application.
+ // TODO(vasilvv): currently, we always send MAX_STREAMS as long as the overall
+ // maximum number of streams for the connection has not been exceeded. We
+ // should also limit the maximum number of streams that the consuming code
+ // has not accepted to a smaller number, by checking the size of
+ // |incoming_bidirectional_streams_| and |incoming_unidirectional_streams_|
+ // before sending MAX_STREAMS.
+ QuicDeque<QuicTransportStream*> incoming_bidirectional_streams_;
+ QuicDeque<QuicTransportStream*> incoming_unidirectional_streams_;
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session_test.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session_test.cc
index 24e1071d3e9..17a475751d6 100644
--- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session_test.cc
@@ -20,6 +20,7 @@
#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h"
namespace quic {
namespace test {
@@ -29,8 +30,6 @@ using testing::_;
using testing::ElementsAre;
const char* kTestOrigin = "https://test-origin.test";
-constexpr char kTestOriginClientIndication[] =
- "\0\0\0\x18https://test-origin.test";
url::Origin GetTestOrigin() {
GURL origin_url(kTestOrigin);
return url::Origin::Create(origin_url);
@@ -50,32 +49,6 @@ std::string DataInStream(QuicStream* stream) {
return result;
}
-class TestClientSession : public QuicTransportClientSession {
- public:
- using QuicTransportClientSession::QuicTransportClientSession;
-
- class Stream : public QuicStream {
- public:
- using QuicStream::QuicStream;
- void OnDataAvailable() override {}
- };
-
- QuicStream* CreateIncomingStream(QuicStreamId id) override {
- auto stream = std::make_unique<Stream>(
- id, this, /*is_static=*/false,
- QuicUtils::GetStreamType(id, connection()->perspective(),
- /*peer_initiated=*/true));
- QuicStream* result = stream.get();
- ActivateStream(std::move(stream));
- return result;
- }
-
- QuicStream* CreateIncomingStream(PendingStream* /*pending*/) override {
- QUIC_NOTREACHED();
- return nullptr;
- }
-};
-
class QuicTransportClientSessionTest : public QuicTest {
protected:
QuicTransportClientSessionTest()
@@ -83,16 +56,16 @@ class QuicTransportClientSessionTest : public QuicTest {
&alarm_factory_,
Perspective::IS_CLIENT,
GetVersions()),
- server_id_("test.example.com", 443),
crypto_config_(crypto_test_utils::ProofVerifierForTesting()) {
SetQuicReloadableFlag(quic_supports_tls_handshake, true);
- CreateSession(GetTestOrigin());
+ CreateSession(GetTestOrigin(), "");
}
- void CreateSession(url::Origin origin) {
- session_ = std::make_unique<TestClientSession>(
- &connection_, nullptr, DefaultQuicConfig(), GetVersions(), server_id_,
- &crypto_config_, origin);
+ void CreateSession(url::Origin origin, std::string url_suffix) {
+ session_ = std::make_unique<QuicTransportClientSession>(
+ &connection_, nullptr, DefaultQuicConfig(), GetVersions(),
+ GURL("quic-transport://test.example.com:50000" + url_suffix),
+ &crypto_config_, origin, &visitor_);
session_->Initialize();
crypto_stream_ = static_cast<QuicCryptoClientStream*>(
session_->GetMutableCryptoStream());
@@ -101,18 +74,20 @@ class QuicTransportClientSessionTest : public QuicTest {
void Connect() {
session_->CryptoConnect();
QuicConfig server_config = DefaultQuicConfig();
+ std::unique_ptr<QuicCryptoServerConfig> crypto_config(
+ crypto_test_utils::CryptoServerConfigForTesting());
crypto_test_utils::HandshakeWithFakeServer(
- &server_config, &helper_, &alarm_factory_, &connection_, crypto_stream_,
- QuicTransportAlpn());
+ &server_config, crypto_config.get(), &helper_, &alarm_factory_,
+ &connection_, crypto_stream_, QuicTransportAlpn());
}
MockAlarmFactory alarm_factory_;
MockQuicConnectionHelper helper_;
PacketSavingConnection connection_;
- QuicServerId server_id_;
QuicCryptoClientConfig crypto_config_;
- std::unique_ptr<TestClientSession> session_;
+ MockClientVisitor visitor_;
+ std::unique_ptr<QuicTransportClientSession> session_;
QuicCryptoClientStream* crypto_stream_;
};
@@ -121,6 +96,39 @@ TEST_F(QuicTransportClientSessionTest, HasValidAlpn) {
}
TEST_F(QuicTransportClientSessionTest, SuccessfulConnection) {
+ constexpr char kTestOriginClientIndication[] =
+ "\0\0" // key (0x0000, origin)
+ "\0\x18" // length
+ "https://test-origin.test" // value
+ "\0\x01" // key (0x0001, path)
+ "\0\x01" // length
+ "/"; // value
+
+ Connect();
+ EXPECT_TRUE(session_->IsSessionReady());
+
+ QuicStream* client_indication_stream =
+ QuicSessionPeer::zombie_streams(session_.get())[ClientIndicationStream()]
+ .get();
+ ASSERT_TRUE(client_indication_stream != nullptr);
+ const std::string client_indication = DataInStream(client_indication_stream);
+ const std::string expected_client_indication{
+ kTestOriginClientIndication,
+ QUIC_ARRAYSIZE(kTestOriginClientIndication) - 1};
+ EXPECT_EQ(client_indication, expected_client_indication);
+}
+
+TEST_F(QuicTransportClientSessionTest, SuccessfulConnectionWithPath) {
+ constexpr char kSuffix[] = "/foo/bar?hello=world#not-sent";
+ constexpr char kTestOriginClientIndication[] =
+ "\0\0" // key (0x0000, origin)
+ "\0\x18" // length
+ "https://test-origin.test" // value
+ "\0\x01" // key (0x0001, path)
+ "\0\x14" // length
+ "/foo/bar?hello=world"; // value
+
+ CreateSession(GetTestOrigin(), kSuffix);
Connect();
EXPECT_TRUE(session_->IsSessionReady());
@@ -139,11 +147,28 @@ TEST_F(QuicTransportClientSessionTest, OriginTooLong) {
std::string long_string(68000, 'a');
GURL bad_origin_url{"https://" + long_string + ".example/"};
EXPECT_TRUE(bad_origin_url.is_valid());
- CreateSession(url::Origin::Create(bad_origin_url));
+ CreateSession(url::Origin::Create(bad_origin_url), "");
EXPECT_QUIC_BUG(Connect(), "Client origin too long");
}
+TEST_F(QuicTransportClientSessionTest, ReceiveNewStreams) {
+ Connect();
+ ASSERT_TRUE(session_->IsSessionReady());
+ ASSERT_TRUE(session_->AcceptIncomingUnidirectionalStream() == nullptr);
+
+ const QuicStreamId id = GetNthServerInitiatedUnidirectionalStreamId(
+ session_->transport_version(), 0);
+ QuicStreamFrame frame(id, /*fin=*/false, /*offset=*/0, "test");
+ EXPECT_CALL(visitor_, OnIncomingUnidirectionalStreamAvailable()).Times(1);
+ session_->OnStreamFrame(frame);
+
+ QuicTransportStream* stream = session_->AcceptIncomingUnidirectionalStream();
+ ASSERT_TRUE(stream != nullptr);
+ EXPECT_EQ(stream->ReadableBytes(), 4u);
+ EXPECT_EQ(stream->id(), id);
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_integration_test.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_integration_test.cc
new file mode 100644
index 00000000000..57f53804c3a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_integration_test.cc
@@ -0,0 +1,331 @@
+// 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.
+
+// An integration test that covers interactions between QuicTransport client and
+// server sessions.
+
+#include <memory>
+#include <vector>
+
+#include "url/gurl.h"
+#include "url/origin.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+#include "net/third_party/quiche/src/quic/tools/quic_transport_simple_server_session.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+using simulator::QuicEndpointBase;
+using simulator::Simulator;
+using testing::Assign;
+
+url::Origin GetTestOrigin() {
+ constexpr char kTestOrigin[] = "https://test-origin.test";
+ GURL origin_url(kTestOrigin);
+ return url::Origin::Create(origin_url);
+}
+
+ParsedQuicVersionVector GetVersions() {
+ return {ParsedQuicVersion{PROTOCOL_TLS1_3, QUIC_VERSION_99}};
+}
+
+class QuicTransportEndpointBase : public QuicEndpointBase {
+ public:
+ QuicTransportEndpointBase(Simulator* simulator,
+ const std::string& name,
+ const std::string& peer_name,
+ Perspective perspective)
+ : QuicEndpointBase(simulator, name, peer_name) {
+ connection_ = std::make_unique<QuicConnection>(
+ TestConnectionId(0x10), simulator::GetAddressFromName(peer_name),
+ simulator, simulator->GetAlarmFactory(), &writer_,
+ /*owns_writer=*/false, perspective, GetVersions());
+ connection_->SetSelfAddress(simulator::GetAddressFromName(name));
+
+ SetQuicReloadableFlag(quic_supports_tls_handshake, true);
+ }
+};
+
+class QuicTransportClientEndpoint : public QuicTransportEndpointBase {
+ public:
+ QuicTransportClientEndpoint(Simulator* simulator,
+ const std::string& name,
+ const std::string& peer_name,
+ url::Origin origin)
+ : QuicTransportEndpointBase(simulator,
+ name,
+ peer_name,
+ Perspective::IS_CLIENT),
+ crypto_config_(crypto_test_utils::ProofVerifierForTesting()),
+ session_(connection_.get(),
+ nullptr,
+ DefaultQuicConfig(),
+ GetVersions(),
+ GURL("quic-transport://test.example.com:50000"),
+ &crypto_config_,
+ origin,
+ &visitor_) {
+ session_.Initialize();
+ }
+
+ QuicTransportClientSession* session() { return &session_; }
+ MockClientVisitor* visitor() { return &visitor_; }
+
+ private:
+ QuicCryptoClientConfig crypto_config_;
+ MockClientVisitor visitor_;
+ QuicTransportClientSession session_;
+};
+
+class QuicTransportServerEndpoint : public QuicTransportEndpointBase {
+ public:
+ QuicTransportServerEndpoint(Simulator* simulator,
+ const std::string& name,
+ const std::string& peer_name,
+ QuicTransportSimpleServerSession::Mode mode,
+ std::vector<url::Origin> accepted_origins)
+ : QuicTransportEndpointBase(simulator,
+ name,
+ peer_name,
+ Perspective::IS_SERVER),
+ crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default()),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
+ session_(connection_.get(),
+ /*owns_connection=*/false,
+ nullptr,
+ DefaultQuicConfig(),
+ GetVersions(),
+ &crypto_config_,
+ &compressed_certs_cache_,
+ mode,
+ accepted_origins) {
+ session_.Initialize();
+ }
+
+ QuicTransportServerSession* session() { return &session_; }
+
+ private:
+ QuicCryptoServerConfig crypto_config_;
+ QuicCompressedCertsCache compressed_certs_cache_;
+ QuicTransportSimpleServerSession session_;
+};
+
+std::unique_ptr<MockStreamVisitor> VisitorExpectingFin() {
+ auto visitor = std::make_unique<MockStreamVisitor>();
+ EXPECT_CALL(*visitor, OnFinRead());
+ return visitor;
+}
+
+constexpr QuicBandwidth kClientBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(10000);
+constexpr QuicTime::Delta kClientPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(2);
+constexpr QuicBandwidth kServerBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(4000);
+constexpr QuicTime::Delta kServerPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(50);
+const QuicTime::Delta kTransferTime =
+ kClientBandwidth.TransferTime(kMaxOutgoingPacketSize) +
+ kServerBandwidth.TransferTime(kMaxOutgoingPacketSize);
+const QuicTime::Delta kRtt =
+ (kClientPropagationDelay + kServerPropagationDelay + kTransferTime) * 2;
+const QuicByteCount kBdp = kRtt * kServerBandwidth;
+
+constexpr QuicTime::Delta kDefaultTimeout = QuicTime::Delta::FromSeconds(3);
+
+class QuicTransportIntegrationTest : public QuicTest {
+ public:
+ QuicTransportIntegrationTest()
+ : switch_(&simulator_, "Switch", 8, 2 * kBdp) {}
+
+ void CreateDefaultEndpoints(QuicTransportSimpleServerSession::Mode mode) {
+ client_ = std::make_unique<QuicTransportClientEndpoint>(
+ &simulator_, "Client", "Server", GetTestOrigin());
+ server_ = std::make_unique<QuicTransportServerEndpoint>(
+ &simulator_, "Server", "Client", mode, accepted_origins_);
+ }
+
+ void WireUpEndpoints() {
+ client_link_ = std::make_unique<simulator::SymmetricLink>(
+ client_.get(), switch_.port(1), kClientBandwidth,
+ kClientPropagationDelay);
+ server_link_ = std::make_unique<simulator::SymmetricLink>(
+ server_.get(), switch_.port(2), kServerBandwidth,
+ kServerPropagationDelay);
+ }
+
+ void RunHandshake() {
+ client_->session()->CryptoConnect();
+ bool result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return IsHandshakeDone(client_->session()) &&
+ IsHandshakeDone(server_->session());
+ },
+ kDefaultTimeout);
+ EXPECT_TRUE(result);
+ }
+
+ protected:
+ template <class Session>
+ static bool IsHandshakeDone(const Session* session) {
+ return session->IsSessionReady() || session->error() != QUIC_NO_ERROR;
+ }
+
+ Simulator simulator_;
+ simulator::Switch switch_;
+ std::unique_ptr<simulator::SymmetricLink> client_link_;
+ std::unique_ptr<simulator::SymmetricLink> server_link_;
+
+ std::unique_ptr<QuicTransportClientEndpoint> client_;
+ std::unique_ptr<QuicTransportServerEndpoint> server_;
+
+ std::vector<url::Origin> accepted_origins_ = {GetTestOrigin()};
+};
+
+TEST_F(QuicTransportIntegrationTest, SuccessfulHandshake) {
+ CreateDefaultEndpoints(QuicTransportSimpleServerSession::DISCARD);
+ WireUpEndpoints();
+ RunHandshake();
+ EXPECT_TRUE(client_->session()->IsSessionReady());
+ EXPECT_TRUE(server_->session()->IsSessionReady());
+}
+
+TEST_F(QuicTransportIntegrationTest, OriginMismatch) {
+ accepted_origins_ = {url::Origin::Create(GURL{"https://wrong-origin.test"})};
+ CreateDefaultEndpoints(QuicTransportSimpleServerSession::DISCARD);
+ WireUpEndpoints();
+ RunHandshake();
+ // Wait until the client receives CONNECTION_CLOSE.
+ simulator_.RunUntilOrTimeout(
+ [this]() { return !client_->session()->connection()->connected(); },
+ kDefaultTimeout);
+ EXPECT_TRUE(client_->session()->IsSessionReady());
+ EXPECT_FALSE(server_->session()->IsSessionReady());
+ EXPECT_FALSE(client_->session()->connection()->connected());
+ EXPECT_FALSE(server_->session()->connection()->connected());
+ EXPECT_THAT(client_->session()->error(),
+ IsError(QUIC_TRANSPORT_INVALID_CLIENT_INDICATION));
+ EXPECT_THAT(server_->session()->error(),
+ IsError(QUIC_TRANSPORT_INVALID_CLIENT_INDICATION));
+}
+
+TEST_F(QuicTransportIntegrationTest, SendOutgoingStreams) {
+ CreateDefaultEndpoints(QuicTransportSimpleServerSession::DISCARD);
+ WireUpEndpoints();
+ RunHandshake();
+
+ std::vector<QuicTransportStream*> streams;
+ for (int i = 0; i < 10; i++) {
+ QuicTransportStream* stream =
+ client_->session()->OpenOutgoingUnidirectionalStream();
+ ASSERT_TRUE(stream->Write("test"));
+ streams.push_back(stream);
+ }
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [this]() {
+ return server_->session()->GetNumOpenIncomingStreams() == 10;
+ },
+ kDefaultTimeout));
+
+ for (QuicTransportStream* stream : streams) {
+ ASSERT_TRUE(stream->SendFin());
+ }
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [this]() { return server_->session()->GetNumOpenIncomingStreams() == 0; },
+ kDefaultTimeout));
+}
+
+TEST_F(QuicTransportIntegrationTest, EchoBidirectionalStreams) {
+ CreateDefaultEndpoints(QuicTransportSimpleServerSession::ECHO);
+ WireUpEndpoints();
+ RunHandshake();
+
+ QuicTransportStream* stream =
+ client_->session()->OpenOutgoingBidirectionalStream();
+ EXPECT_TRUE(stream->Write("Hello!"));
+
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [stream]() { return stream->ReadableBytes() == strlen("Hello!"); },
+ kDefaultTimeout));
+ std::string received;
+ EXPECT_EQ(stream->Read(&received), strlen("Hello!"));
+ EXPECT_EQ(received, "Hello!");
+
+ EXPECT_TRUE(stream->SendFin());
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [this]() { return server_->session()->GetNumOpenIncomingStreams() == 0; },
+ kDefaultTimeout));
+}
+
+TEST_F(QuicTransportIntegrationTest, EchoUnidirectionalStreams) {
+ CreateDefaultEndpoints(QuicTransportSimpleServerSession::ECHO);
+ WireUpEndpoints();
+ RunHandshake();
+
+ // Send two streams, but only send FIN on the second one.
+ QuicTransportStream* stream1 =
+ client_->session()->OpenOutgoingUnidirectionalStream();
+ EXPECT_TRUE(stream1->Write("Stream One"));
+ QuicTransportStream* stream2 =
+ client_->session()->OpenOutgoingUnidirectionalStream();
+ EXPECT_TRUE(stream2->Write("Stream Two"));
+ EXPECT_TRUE(stream2->SendFin());
+
+ // Wait until a stream is received.
+ bool stream_received = false;
+ EXPECT_CALL(*client_->visitor(), OnIncomingUnidirectionalStreamAvailable())
+ .Times(2)
+ .WillRepeatedly(Assign(&stream_received, true));
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [&stream_received]() { return stream_received; }, kDefaultTimeout));
+
+ // Receive a reply stream and expect it to be the second one.
+ QuicTransportStream* reply =
+ client_->session()->AcceptIncomingUnidirectionalStream();
+ ASSERT_TRUE(reply != nullptr);
+ std::string buffer;
+ reply->set_visitor(VisitorExpectingFin());
+ EXPECT_GT(reply->Read(&buffer), 0u);
+ EXPECT_EQ(buffer, "Stream Two");
+
+ // Reset reply-related variables.
+ stream_received = false;
+ buffer = "";
+
+ // Send FIN on the first stream, and expect to receive it back.
+ EXPECT_TRUE(stream1->SendFin());
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [&stream_received]() { return stream_received; }, kDefaultTimeout));
+ reply = client_->session()->AcceptIncomingUnidirectionalStream();
+ ASSERT_TRUE(reply != nullptr);
+ reply->set_visitor(VisitorExpectingFin());
+ EXPECT_GT(reply->Read(&buffer), 0u);
+ EXPECT_EQ(buffer, "Stream One");
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h
index 307354f32ad..f97b8e777de 100644
--- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h
@@ -12,23 +12,24 @@
namespace quic {
// The ALPN used by QuicTransport.
-QUIC_EXPORT inline const char* QuicTransportAlpn() {
+QUIC_EXPORT_PRIVATE inline const char* QuicTransportAlpn() {
return "wq-vvv-01";
}
// The stream ID on which the client indication is sent.
-QUIC_EXPORT constexpr QuicStreamId ClientIndicationStream() {
+QUIC_EXPORT_PRIVATE constexpr QuicStreamId ClientIndicationStream() {
return 2;
}
// The maximum allowed size of the client indication.
-QUIC_EXPORT constexpr QuicByteCount ClientIndicationMaxSize() {
+QUIC_EXPORT_PRIVATE constexpr QuicByteCount ClientIndicationMaxSize() {
return 65536;
}
// The keys of the fields in the client indication.
enum class QuicTransportClientIndicationKeys : uint16_t {
kOrigin = 0x0000,
+ kPath = 0x0001,
};
} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.cc
index 92cffcd9156..7f00acd470f 100644
--- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.cc
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.cc
@@ -12,8 +12,8 @@
#include "net/third_party/quiche/src/quic/core/quic_types.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/quic_transport/quic_transport_client_session.h"
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
namespace quic {
@@ -63,9 +63,11 @@ QuicStream* QuicTransportServerSession::CreateIncomingStream(QuicStreamId id) {
return indication_ptr;
}
- // TODO(vasilvv): implement incoming data streams.
- QUIC_BUG << "Not implemented";
- return nullptr;
+ auto stream = std::make_unique<QuicTransportStream>(id, this, this);
+ QuicTransportStream* stream_ptr = stream.get();
+ ActivateStream(std::move(stream));
+ OnIncomingDataStream(stream_ptr);
+ return stream_ptr;
}
QuicTransportServerSession::ClientIndication::ClientIndication(
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h
index 7183eada1c8..b3fcfa07588 100644
--- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h
@@ -12,15 +12,16 @@
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
namespace quic {
// A server session for the QuicTransport protocol.
-class QUIC_EXPORT QuicTransportServerSession
+class QUIC_EXPORT_PRIVATE QuicTransportServerSession
: public QuicSession,
public QuicTransportSessionInterface {
public:
- class ServerVisitor {
+ class QUIC_EXPORT_PRIVATE ServerVisitor {
public:
virtual ~ServerVisitor() {}
@@ -49,6 +50,10 @@ class QUIC_EXPORT QuicTransportServerSession
return crypto_stream_.get();
}
+ // Returns true once the encryption has been established, the client
+ // indication has been received and the origin has been verified. No
+ // application data will be read or written before the connection is ready.
+ // Once the connection becomes ready, this method will never return false.
bool IsSessionReady() const override { return ready_; }
QuicStream* CreateIncomingStream(QuicStreamId id) override;
@@ -59,7 +64,7 @@ class QUIC_EXPORT QuicTransportServerSession
}
protected:
- class ClientIndication : public QuicStream {
+ class QUIC_EXPORT_PRIVATE ClientIndication : public QuicStream {
public:
explicit ClientIndication(QuicTransportServerSession* session);
void OnDataAvailable() override;
@@ -70,7 +75,7 @@ class QUIC_EXPORT QuicTransportServerSession
};
// Utility class for parsing the client indication.
- class ClientIndicationParser {
+ class QUIC_EXPORT_PRIVATE ClientIndicationParser {
public:
ClientIndicationParser(QuicTransportServerSession* session,
QuicStringPiece indication)
@@ -93,6 +98,8 @@ class QUIC_EXPORT QuicTransportServerSession
// https://vasilvv.github.io/webtransport/draft-vvv-webtransport-quic.html#rfc.section.3.2
void ProcessClientIndication(QuicStringPiece indication);
+ virtual void OnIncomingDataStream(QuicTransportStream* /*stream*/) {}
+
std::unique_ptr<QuicCryptoServerStream> crypto_stream_;
bool ready_ = false;
ServerVisitor* visitor_;
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session_test.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session_test.cc
index 818c08db9e4..5e6774810f0 100644
--- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session_test.cc
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session_test.cc
@@ -21,6 +21,7 @@
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h"
namespace quic {
namespace test {
@@ -48,11 +49,6 @@ ParsedQuicVersionVector GetVersions() {
return {ParsedQuicVersion{PROTOCOL_TLS1_3, QUIC_VERSION_99}};
}
-class MockVisitor : public QuicTransportServerSession::ServerVisitor {
- public:
- MOCK_METHOD1(CheckOrigin, bool(url::Origin));
-};
-
class QuicTransportServerSessionTest : public QuicTest {
public:
QuicTransportServerSessionTest()
@@ -76,7 +72,9 @@ class QuicTransportServerSessionTest : public QuicTest {
session_->Initialize();
crypto_stream_ = static_cast<QuicCryptoServerStream*>(
session_->GetMutableCryptoStream());
- crypto_stream_->OnSuccessfulVersionNegotiation(GetVersions()[0]);
+ if (!GetQuicReloadableFlag(quic_version_negotiated_by_default_at_server)) {
+ crypto_stream_->OnSuccessfulVersionNegotiation(GetVersions()[0]);
+ }
}
void Connect() {
@@ -111,7 +109,7 @@ class QuicTransportServerSessionTest : public QuicTest {
QuicCryptoServerConfig crypto_config_;
std::unique_ptr<QuicTransportServerSession> session_;
QuicCompressedCertsCache compressed_certs_cache_;
- testing::StrictMock<MockVisitor> visitor_;
+ testing::StrictMock<MockServerVisitor> visitor_;
QuicCryptoServerStream* crypto_stream_;
};
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h
index 971fa5b3c73..cdb3e999790 100644
--- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h
@@ -5,11 +5,13 @@
#ifndef QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_SESSION_INTERFACE_H_
#define QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_SESSION_INTERFACE_H_
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
namespace quic {
// Shared interface between QuicTransportClientSession and
// QuicTransportServerSession.
-class QuicTransportSessionInterface {
+class QUIC_EXPORT_PRIVATE QuicTransportSessionInterface {
public:
virtual ~QuicTransportSessionInterface() {}
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc
new file mode 100644
index 00000000000..61f4345d61b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc
@@ -0,0 +1,112 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
+
+#include <sys/types.h>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+QuicTransportStream::QuicTransportStream(
+ QuicStreamId id,
+ QuicSession* session,
+ QuicTransportSessionInterface* session_interface)
+ : QuicStream(id,
+ session,
+ /*is_static=*/false,
+ QuicUtils::GetStreamType(id,
+ session->connection()->perspective(),
+ session->IsIncomingStream(id))),
+ session_interface_(session_interface) {}
+
+size_t QuicTransportStream::Read(char* buffer, size_t buffer_size) {
+ if (!session_interface_->IsSessionReady()) {
+ return 0;
+ }
+
+ iovec iov;
+ iov.iov_base = buffer;
+ iov.iov_len = buffer_size;
+ const size_t result = sequencer()->Readv(&iov, 1);
+ if (sequencer()->IsClosed() && visitor_ != nullptr) {
+ visitor_->OnFinRead();
+ }
+ return result;
+}
+
+size_t QuicTransportStream::Read(std::string* output) {
+ const size_t old_size = output->size();
+ const size_t bytes_to_read = ReadableBytes();
+ output->resize(old_size + bytes_to_read);
+ size_t bytes_read = Read(&(*output)[old_size], bytes_to_read);
+ DCHECK_EQ(bytes_to_read, bytes_read);
+ output->resize(old_size + bytes_read);
+ return bytes_read;
+}
+
+bool QuicTransportStream::Write(QuicStringPiece data) {
+ if (!CanWrite()) {
+ return false;
+ }
+
+ // TODO(vasilvv): use WriteMemSlices()
+ WriteOrBufferData(data, /*fin=*/false, nullptr);
+ return true;
+}
+
+bool QuicTransportStream::SendFin() {
+ if (!CanWrite()) {
+ return false;
+ }
+
+ WriteOrBufferData(QuicStringPiece(), /*fin=*/true, nullptr);
+ return true;
+}
+
+bool QuicTransportStream::CanWrite() const {
+ return session_interface_->IsSessionReady() && CanWriteNewData();
+}
+
+size_t QuicTransportStream::ReadableBytes() const {
+ if (!session_interface_->IsSessionReady()) {
+ return 0;
+ }
+
+ return sequencer()->ReadableBytes();
+}
+
+void QuicTransportStream::OnDataAvailable() {
+ if (sequencer()->IsClosed()) {
+ if (visitor_ != nullptr) {
+ visitor_->OnFinRead();
+ }
+ OnFinRead();
+ return;
+ }
+
+ if (visitor_ == nullptr) {
+ return;
+ }
+ if (ReadableBytes() == 0) {
+ return;
+ }
+ visitor_->OnCanRead();
+}
+
+void QuicTransportStream::OnCanWriteNewData() {
+ // Ensure the origin check has been completed, as the stream can be notified
+ // about being writable before that.
+ if (!CanWrite()) {
+ return;
+ }
+ if (visitor_ != nullptr) {
+ visitor_->OnCanWrite();
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h
new file mode 100644
index 00000000000..1651a1cb082
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h
@@ -0,0 +1,72 @@
+// 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_QUIC_TRANSPORT_QUIC_TRANSPORT_STREAM_H_
+#define QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_STREAM_H_
+
+#include <cstddef>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h"
+
+namespace quic {
+
+// QuicTransportStream is an extension of QuicStream that provides I/O interface
+// that is safe to use in the QuicTransport context. The interface ensures no
+// application data is processed before the client indication is processed.
+class QUIC_EXPORT_PRIVATE QuicTransportStream : public QuicStream {
+ public:
+ class QUIC_EXPORT_PRIVATE Visitor {
+ public:
+ virtual ~Visitor() {}
+ virtual void OnCanRead() = 0;
+ virtual void OnFinRead() = 0;
+ virtual void OnCanWrite() = 0;
+ };
+
+ QuicTransportStream(QuicStreamId id,
+ QuicSession* session,
+ QuicTransportSessionInterface* session_interface);
+
+ // Reads at most |buffer_size| bytes into |buffer| and returns the number of
+ // bytes actually read.
+ size_t Read(char* buffer, size_t buffer_size);
+ // Reads all available data and appends it to the end of |output|.
+ size_t Read(std::string* output);
+ // Writes |data| into the stream. Returns true on success.
+ QUIC_MUST_USE_RESULT bool Write(QuicStringPiece data);
+ // Sends the FIN on the stream. Returns true on success.
+ QUIC_MUST_USE_RESULT bool SendFin();
+
+ // Indicates whether it is possible to write into stream right now.
+ bool CanWrite() const;
+ // Indicates the number of bytes that can be read from the stream.
+ size_t ReadableBytes() const;
+
+ // QuicSession method implementations.
+ void OnDataAvailable() override;
+ void OnCanWriteNewData() override;
+
+ Visitor* visitor() { return visitor_.get(); }
+ void set_visitor(std::unique_ptr<Visitor> visitor) {
+ visitor_ = std::move(visitor);
+ }
+
+ protected:
+ // Hide the methods that allow writing data without checking IsSessionReady().
+ using QuicStream::WriteMemSlices;
+ using QuicStream::WriteOrBufferData;
+
+ QuicTransportSessionInterface* session_interface_;
+ std::unique_ptr<Visitor> visitor_ = nullptr;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream_test.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream_test.cc
new file mode 100644
index 00000000000..c291b54cf2b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream_test.cc
@@ -0,0 +1,123 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+using testing::Return;
+
+ParsedQuicVersionVector GetVersions() {
+ return {ParsedQuicVersion{PROTOCOL_TLS1_3, QUIC_VERSION_99}};
+}
+
+class MockQuicTransportSessionInterface : public QuicTransportSessionInterface {
+ public:
+ MOCK_CONST_METHOD0(IsSessionReady, bool());
+};
+
+class QuicTransportStreamTest : public QuicTest {
+ public:
+ QuicTransportStreamTest()
+ : connection_(new MockQuicConnection(&helper_,
+ &alarm_factory_,
+ Perspective::IS_CLIENT,
+ GetVersions())),
+ session_(connection_) {
+ session_.Initialize();
+
+ stream_ = new QuicTransportStream(0, &session_, &interface_);
+ session_.ActivateStream(QuicWrapUnique(stream_));
+
+ auto visitor = std::make_unique<MockStreamVisitor>();
+ visitor_ = visitor.get();
+ stream_->set_visitor(std::move(visitor));
+ }
+
+ void ReceiveStreamData(QuicStringPiece data, QuicStreamOffset offset) {
+ QuicStreamFrame frame(0, false, offset, data);
+ stream_->OnStreamFrame(frame);
+ }
+
+ protected:
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnectionHelper helper_;
+
+ MockQuicConnection* connection_; // Owned by |session_|.
+ MockQuicSession session_;
+ MockQuicTransportSessionInterface interface_;
+ QuicTransportStream* stream_; // Owned by |session_|.
+ MockStreamVisitor* visitor_; // Owned by |stream_|.
+};
+
+TEST_F(QuicTransportStreamTest, NotReady) {
+ EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(false));
+ ReceiveStreamData("test", 0);
+ EXPECT_EQ(stream_->ReadableBytes(), 0u);
+ EXPECT_FALSE(stream_->CanWrite());
+}
+
+TEST_F(QuicTransportStreamTest, ReadWhenNotReady) {
+ EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(false));
+ ReceiveStreamData("test", 0);
+ char buffer[4];
+ QuicByteCount bytes_read = stream_->Read(buffer, sizeof(buffer));
+ EXPECT_EQ(bytes_read, 0u);
+}
+
+TEST_F(QuicTransportStreamTest, WriteWhenNotReady) {
+ EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(false));
+ EXPECT_FALSE(stream_->Write("test"));
+}
+
+TEST_F(QuicTransportStreamTest, Ready) {
+ EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(true));
+ ReceiveStreamData("test", 0);
+ EXPECT_EQ(stream_->ReadableBytes(), 4u);
+ EXPECT_TRUE(stream_->CanWrite());
+ EXPECT_TRUE(stream_->Write("test"));
+}
+
+TEST_F(QuicTransportStreamTest, ReceiveData) {
+ EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*visitor_, OnCanRead());
+ ReceiveStreamData("test", 0);
+}
+
+TEST_F(QuicTransportStreamTest, FinReadWithNoDataPending) {
+ EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*visitor_, OnFinRead());
+ QuicStreamFrame frame(0, true, 0, "");
+ stream_->OnStreamFrame(frame);
+}
+
+TEST_F(QuicTransportStreamTest, FinReadWithDataPending) {
+ EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(true));
+
+ EXPECT_CALL(*visitor_, OnCanRead());
+ EXPECT_CALL(*visitor_, OnFinRead()).Times(0);
+ QuicStreamFrame frame(0, true, 0, "test");
+ stream_->OnStreamFrame(frame);
+
+ EXPECT_CALL(*visitor_, OnFinRead()).Times(1);
+ std::string buffer;
+ ASSERT_EQ(stream_->Read(&buffer), 4u);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic