| // 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_client_session.h" | |
| #include <memory> | |
| #include <utility> | |
| #include "url/gurl.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_server_id.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_arraysize.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" | |
| #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" | |
| #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 { | |
| namespace { | |
| using testing::_; | |
| using testing::ElementsAre; | |
| const char* kTestOrigin = "https://test-origin.test"; | |
| url::Origin GetTestOrigin() { | |
| GURL origin_url(kTestOrigin); | |
| return url::Origin::Create(origin_url); | |
| } | |
| ParsedQuicVersionVector GetVersions() { | |
| return {ParsedQuicVersion{PROTOCOL_TLS1_3, QUIC_VERSION_99}}; | |
| } | |
| std::string DataInStream(QuicStream* stream) { | |
| QuicStreamSendBuffer& send_buffer = QuicStreamPeer::SendBuffer(stream); | |
| std::string result; | |
| result.resize(send_buffer.stream_offset()); | |
| QuicDataWriter writer(result.size(), &result[0]); | |
| EXPECT_TRUE( | |
| send_buffer.WriteStreamData(0, send_buffer.stream_offset(), &writer)); | |
| return result; | |
| } | |
| class QuicTransportClientSessionTest : public QuicTest { | |
| protected: | |
| QuicTransportClientSessionTest() | |
| : connection_(&helper_, | |
| &alarm_factory_, | |
| Perspective::IS_CLIENT, | |
| GetVersions()), | |
| crypto_config_(crypto_test_utils::ProofVerifierForTesting()) { | |
| CreateSession(GetTestOrigin(), ""); | |
| } | |
| 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()); | |
| } | |
| void Connect() { | |
| session_->CryptoConnect(); | |
| QuicConfig server_config = DefaultQuicConfig(); | |
| std::unique_ptr<QuicCryptoServerConfig> crypto_config( | |
| crypto_test_utils::CryptoServerConfigForTesting()); | |
| crypto_test_utils::HandshakeWithFakeServer( | |
| &server_config, crypto_config.get(), &helper_, &alarm_factory_, | |
| &connection_, crypto_stream_, QuicTransportAlpn()); | |
| } | |
| MockAlarmFactory alarm_factory_; | |
| MockQuicConnectionHelper helper_; | |
| PacketSavingConnection connection_; | |
| QuicCryptoClientConfig crypto_config_; | |
| MockClientVisitor visitor_; | |
| std::unique_ptr<QuicTransportClientSession> session_; | |
| QuicCryptoClientStream* crypto_stream_; | |
| }; | |
| TEST_F(QuicTransportClientSessionTest, HasValidAlpn) { | |
| EXPECT_THAT(session_->GetAlpnsToOffer(), ElementsAre(QuicTransportAlpn())); | |
| } | |
| 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 | |
| EXPECT_CALL(visitor_, OnSessionReady()); | |
| 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()); | |
| 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, 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), ""); | |
| 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 |