diff options
Diffstat (limited to 'chromium/net/third_party/quiche/src/quic/quic_transport')
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 |