blob: 90c449cb74668c5628404d6050399ad47f534c2f [file] [log] [blame]
// 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 "quic/quic_transport/quic_transport_client_session.h"
#include <memory>
#include <utility>
#include "absl/base/macros.h"
#include "url/gurl.h"
#include "quic/core/quic_data_writer.h"
#include "quic/core/quic_server_id.h"
#include "quic/core/quic_types.h"
#include "quic/core/quic_utils.h"
#include "quic/platform/api/quic_expect_bug.h"
#include "quic/platform/api/quic_test.h"
#include "quic/test_tools/crypto_test_utils.h"
#include "quic/test_tools/quic_session_peer.h"
#include "quic/test_tools/quic_stream_peer.h"
#include "quic/test_tools/quic_test_utils.h"
#include "quic/test_tools/quic_transport_test_tools.h"
namespace quic {
namespace test {
namespace {
using testing::_;
using testing::ElementsAre;
using testing::Eq;
const char* kTestOrigin = "https://test-origin.test";
url::Origin GetTestOrigin() {
GURL origin_url(kTestOrigin);
return url::Origin::Create(origin_url);
}
ParsedQuicVersionVector GetVersions() {
return {DefaultVersionForQuicTransport()};
}
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()) {
QuicEnableVersion(DefaultVersionForQuicTransport());
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_, /*datagram_observer=*/nullptr);
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;
client_indication_stream =
QuicSessionPeer::stream_map(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,
ABSL_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;
client_indication_stream =
QuicSessionPeer::stream_map(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,
ABSL_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);
}
TEST_F(QuicTransportClientSessionTest, ReceiveDatagram) {
EXPECT_CALL(visitor_, OnDatagramReceived(Eq("test")));
session_->OnMessageReceived("test");
}
} // namespace
} // namespace test
} // namespace quic