blob: af062eed5efba3444e5bb91f85885736bb2b3a0c [file] [log] [blame]
// Copyright (c) 2017 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/quic/quartc/quartc_session.h"
#include "build/build_config.h"
#include "net/third_party/quic/core/crypto/crypto_server_config_protobuf.h"
#include "net/third_party/quic/core/quic_simple_buffer_allocator.h"
#include "net/third_party/quic/core/quic_types.h"
#include "net/third_party/quic/core/tls_client_handshaker.h"
#include "net/third_party/quic/core/tls_server_handshaker.h"
#include "net/third_party/quic/platform/api/quic_ptr_util.h"
#include "net/third_party/quic/platform/api/quic_test.h"
#include "net/third_party/quic/platform/api/quic_test_mem_slice_vector.h"
#include "net/third_party/quic/quartc/counting_packet_filter.h"
#include "net/third_party/quic/quartc/quartc_factory.h"
#include "net/third_party/quic/quartc/quartc_packet_writer.h"
#include "net/third_party/quic/quartc/simulated_packet_transport.h"
#include "net/third_party/quic/test_tools/mock_clock.h"
#include "net/third_party/quic/test_tools/simulator/packet_filter.h"
#include "net/third_party/quic/test_tools/simulator/simulator.h"
#include "starboard/string.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
// Tests flaky on iOS.
// TODO(vasilvv): figure out what's wrong and re-enable if possible.
#if !defined(OS_IOS)
namespace quic {
namespace {
static QuicByteCount kDefaultMaxPacketSize = 1200;
class FakeQuartcSessionDelegate : public QuartcSession::Delegate {
public:
explicit FakeQuartcSessionDelegate(QuartcStream::Delegate* stream_delegate)
: stream_delegate_(stream_delegate) {}
// Called when peers have established forward-secure encryption
void OnCryptoHandshakeComplete() override {
LOG(INFO) << "Crypto handshake complete!";
}
// Called when connection closes locally, or remotely by peer.
void OnConnectionClosed(QuicErrorCode error_code,
const QuicString& error_details,
ConnectionCloseSource source) override {
connected_ = false;
}
// Called when an incoming QUIC stream is created.
void OnIncomingStream(QuartcStream* quartc_stream) override {
last_incoming_stream_ = quartc_stream;
last_incoming_stream_->SetDelegate(stream_delegate_);
quartc_stream->set_deliver_on_complete(false);
}
void OnCongestionControlChange(QuicBandwidth bandwidth_estimate,
QuicBandwidth pacing_rate,
QuicTime::Delta latest_rtt) override {}
QuartcStream* incoming_stream() { return last_incoming_stream_; }
bool connected() { return connected_; }
private:
QuartcStream* last_incoming_stream_;
bool connected_ = true;
QuartcStream::Delegate* stream_delegate_;
};
class FakeQuartcStreamDelegate : public QuartcStream::Delegate {
public:
void OnReceived(QuartcStream* stream,
const char* data,
size_t size) override {
received_data_[stream->id()] += QuicString(data, size);
}
void OnClose(QuartcStream* stream) override {
errors_[stream->id()] = stream->stream_error();
}
void OnBufferChanged(QuartcStream* stream) override {}
bool has_data() { return !received_data_.empty(); }
std::map<QuicStreamId, QuicString> data() { return received_data_; }
QuicRstStreamErrorCode stream_error(QuicStreamId id) { return errors_[id]; }
private:
std::map<QuicStreamId, QuicString> received_data_;
std::map<QuicStreamId, QuicRstStreamErrorCode> errors_;
};
class QuartcSessionTest : public QuicTest {
public:
~QuartcSessionTest() override {}
void Init() {
client_transport_ =
QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
&simulator_, "client_transport", "server_transport",
10 * kDefaultMaxPacketSize);
server_transport_ =
QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
&simulator_, "server_transport", "client_transport",
10 * kDefaultMaxPacketSize);
client_filter_ = QuicMakeUnique<simulator::CountingPacketFilter>(
&simulator_, "client_filter", client_transport_.get());
client_server_link_ = QuicMakeUnique<simulator::SymmetricLink>(
client_filter_.get(), server_transport_.get(),
QuicBandwidth::FromKBitsPerSecond(10 * 1000),
QuicTime::Delta::FromMilliseconds(10));
client_writer_ = QuicMakeUnique<QuartcPacketWriter>(client_transport_.get(),
kDefaultMaxPacketSize);
server_writer_ = QuicMakeUnique<QuartcPacketWriter>(server_transport_.get(),
kDefaultMaxPacketSize);
client_stream_delegate_ = QuicMakeUnique<FakeQuartcStreamDelegate>();
client_session_delegate_ = QuicMakeUnique<FakeQuartcSessionDelegate>(
client_stream_delegate_.get());
server_stream_delegate_ = QuicMakeUnique<FakeQuartcStreamDelegate>();
server_session_delegate_ = QuicMakeUnique<FakeQuartcSessionDelegate>(
server_stream_delegate_.get());
}
// The parameters are used to control whether the handshake will success or
// not.
void CreateClientAndServerSessions() {
Init();
client_peer_ =
CreateSession(Perspective::IS_CLIENT, std::move(client_writer_));
client_peer_->SetDelegate(client_session_delegate_.get());
server_peer_ =
CreateSession(Perspective::IS_SERVER, std::move(server_writer_));
server_peer_->SetDelegate(server_session_delegate_.get());
}
std::unique_ptr<QuartcSession> CreateSession(
Perspective perspective,
std::unique_ptr<QuartcPacketWriter> writer) {
std::unique_ptr<QuicConnection> quic_connection =
CreateConnection(perspective, writer.get());
QuicString remote_fingerprint_value = "value";
QuicConfig config;
return QuicMakeUnique<QuartcSession>(
std::move(quic_connection), config, remote_fingerprint_value,
perspective, &simulator_, simulator_.GetClock(), std::move(writer));
}
std::unique_ptr<QuicConnection> CreateConnection(Perspective perspective,
QuartcPacketWriter* writer) {
QuicIpAddress ip;
ip.FromString("0.0.0.0");
return QuicMakeUnique<QuicConnection>(
0, QuicSocketAddress(ip, 0), &simulator_, simulator_.GetAlarmFactory(),
writer, /*owns_writer=*/false, perspective, CurrentSupportedVersions());
}
// Runs all tasks scheduled in the next 200 ms.
void RunTasks() { simulator_.RunFor(QuicTime::Delta::FromMilliseconds(200)); }
void StartHandshake() {
server_peer_->StartCryptoHandshake();
client_peer_->StartCryptoHandshake();
RunTasks();
}
// Test handshake establishment and sending/receiving of data for two
// directions.
void TestStreamConnection() {
ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
ASSERT_TRUE(server_peer_->IsEncryptionEstablished());
ASSERT_TRUE(client_peer_->IsEncryptionEstablished());
// Now we can establish encrypted outgoing stream.
QuartcStream* outgoing_stream = server_peer_->CreateOutgoingDynamicStream();
QuicStreamId stream_id = outgoing_stream->id();
ASSERT_NE(nullptr, outgoing_stream);
EXPECT_TRUE(server_peer_->HasOpenDynamicStreams());
outgoing_stream->SetDelegate(server_stream_delegate_.get());
outgoing_stream->set_deliver_on_complete(false);
// Send a test message from peer 1 to peer 2.
char kTestMessage[] = "Hello";
test::QuicTestMemSliceVector data(
{std::make_pair(kTestMessage, SbStringGetLength(kTestMessage))});
outgoing_stream->WriteMemSlices(data.span(), /*fin=*/false);
RunTasks();
// Wait for peer 2 to receive messages.
ASSERT_TRUE(client_stream_delegate_->has_data());
QuartcStream* incoming = client_session_delegate_->incoming_stream();
ASSERT_TRUE(incoming);
EXPECT_EQ(incoming->id(), stream_id);
EXPECT_TRUE(client_peer_->HasOpenDynamicStreams());
EXPECT_EQ(client_stream_delegate_->data()[stream_id], kTestMessage);
// Send a test message from peer 2 to peer 1.
char kTestResponse[] = "Response";
test::QuicTestMemSliceVector response(
{std::make_pair(kTestResponse, SbStringGetLength(kTestResponse))});
incoming->WriteMemSlices(response.span(), /*fin=*/false);
RunTasks();
// Wait for peer 1 to receive messages.
ASSERT_TRUE(server_stream_delegate_->has_data());
EXPECT_EQ(server_stream_delegate_->data()[stream_id], kTestResponse);
}
// Test that client and server are not connected after handshake failure.
void TestDisconnectAfterFailedHandshake() {
EXPECT_TRUE(!client_session_delegate_->connected());
EXPECT_TRUE(!server_session_delegate_->connected());
EXPECT_FALSE(client_peer_->IsEncryptionEstablished());
EXPECT_FALSE(client_peer_->IsCryptoHandshakeConfirmed());
EXPECT_FALSE(server_peer_->IsEncryptionEstablished());
EXPECT_FALSE(server_peer_->IsCryptoHandshakeConfirmed());
}
protected:
simulator::Simulator simulator_;
std::unique_ptr<simulator::SimulatedQuartcPacketTransport> client_transport_;
std::unique_ptr<simulator::SimulatedQuartcPacketTransport> server_transport_;
std::unique_ptr<simulator::CountingPacketFilter> client_filter_;
std::unique_ptr<simulator::SymmetricLink> client_server_link_;
std::unique_ptr<QuartcPacketWriter> client_writer_;
std::unique_ptr<QuartcPacketWriter> server_writer_;
std::unique_ptr<QuartcSession> client_peer_;
std::unique_ptr<QuartcSession> server_peer_;
std::unique_ptr<FakeQuartcStreamDelegate> client_stream_delegate_;
std::unique_ptr<FakeQuartcSessionDelegate> client_session_delegate_;
std::unique_ptr<FakeQuartcStreamDelegate> server_stream_delegate_;
std::unique_ptr<FakeQuartcSessionDelegate> server_session_delegate_;
};
TEST_F(QuartcSessionTest, StreamConnection) {
CreateClientAndServerSessions();
StartHandshake();
TestStreamConnection();
}
TEST_F(QuartcSessionTest, PreSharedKeyHandshake) {
CreateClientAndServerSessions();
client_peer_->SetPreSharedKey("foo");
server_peer_->SetPreSharedKey("foo");
StartHandshake();
TestStreamConnection();
}
// Test that data streams are not created before handshake.
TEST_F(QuartcSessionTest, CannotCreateDataStreamBeforeHandshake) {
CreateClientAndServerSessions();
EXPECT_EQ(nullptr, server_peer_->CreateOutgoingDynamicStream());
EXPECT_EQ(nullptr, client_peer_->CreateOutgoingDynamicStream());
}
TEST_F(QuartcSessionTest, CancelQuartcStream) {
CreateClientAndServerSessions();
StartHandshake();
ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
QuartcStream* stream = client_peer_->CreateOutgoingDynamicStream();
ASSERT_NE(nullptr, stream);
uint32_t id = stream->id();
EXPECT_FALSE(client_peer_->IsClosedStream(id));
stream->SetDelegate(client_stream_delegate_.get());
client_peer_->CancelStream(id);
EXPECT_EQ(stream->stream_error(),
QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED);
EXPECT_TRUE(client_peer_->IsClosedStream(id));
}
// TODO(b/112561077): This is the wrong layer for this test. We should write a
// test specifically for QuartcPacketWriter with a stubbed-out
// QuartcPacketTransport and remove
// SimulatedQuartcPacketTransport::last_packet_number().
TEST_F(QuartcSessionTest, WriterGivesPacketNumberToTransport) {
CreateClientAndServerSessions();
StartHandshake();
ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
QuartcStream* stream = client_peer_->CreateOutgoingDynamicStream();
stream->SetDelegate(client_stream_delegate_.get());
char kClientMessage[] = "Hello";
test::QuicTestMemSliceVector stream_data(
{std::make_pair(kClientMessage, SbStringGetLength(kClientMessage))});
stream->WriteMemSlices(stream_data.span(), /*fin=*/false);
RunTasks();
// The transport should see the latest packet number sent by QUIC.
EXPECT_EQ(
client_transport_->last_packet_number(),
client_peer_->connection()->sent_packet_manager().GetLargestSentPacket());
}
TEST_F(QuartcSessionTest, CloseConnection) {
CreateClientAndServerSessions();
StartHandshake();
ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
client_peer_->CloseConnection("Connection closed by client");
EXPECT_FALSE(client_session_delegate_->connected());
RunTasks();
EXPECT_FALSE(server_session_delegate_->connected());
}
TEST_F(QuartcSessionTest, StreamRetransmissionEnabled) {
CreateClientAndServerSessions();
StartHandshake();
ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
QuartcStream* stream = client_peer_->CreateOutgoingDynamicStream();
QuicStreamId stream_id = stream->id();
stream->SetDelegate(client_stream_delegate_.get());
stream->set_cancel_on_loss(false);
client_filter_->set_packets_to_drop(1);
char kClientMessage[] = "Hello";
test::QuicTestMemSliceVector stream_data(
{std::make_pair(kClientMessage, SbStringGetLength(kClientMessage))});
stream->WriteMemSlices(stream_data.span(), /*fin=*/false);
RunTasks();
// Stream data should make it despite packet loss.
ASSERT_TRUE(server_stream_delegate_->has_data());
EXPECT_EQ(server_stream_delegate_->data()[stream_id], kClientMessage);
}
TEST_F(QuartcSessionTest, StreamRetransmissionDisabled) {
CreateClientAndServerSessions();
StartHandshake();
ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
QuartcStream* stream = client_peer_->CreateOutgoingDynamicStream();
QuicStreamId stream_id = stream->id();
stream->SetDelegate(client_stream_delegate_.get());
stream->set_cancel_on_loss(true);
client_filter_->set_packets_to_drop(1);
char kMessage[] = "Hello";
test::QuicTestMemSliceVector stream_data(
{std::make_pair(kMessage, SbStringGetLength(kMessage))});
stream->WriteMemSlices(stream_data.span(), /*fin=*/false);
simulator_.RunFor(QuicTime::Delta::FromMilliseconds(1));
// Send another packet to trigger loss detection.
QuartcStream* stream_1 = client_peer_->CreateOutgoingDynamicStream();
stream_1->SetDelegate(client_stream_delegate_.get());
char kMessage1[] = "Second message";
test::QuicTestMemSliceVector stream_data_1(
{std::make_pair(kMessage1, SbStringGetLength(kMessage1))});
stream_1->WriteMemSlices(stream_data_1.span(), /*fin=*/false);
RunTasks();
// QUIC should try to retransmit the first stream by loss detection. Instead,
// it will cancel itself.
EXPECT_THAT(server_stream_delegate_->data()[stream_id], testing::IsEmpty());
EXPECT_TRUE(client_peer_->IsClosedStream(stream_id));
EXPECT_TRUE(server_peer_->IsClosedStream(stream_id));
EXPECT_EQ(client_stream_delegate_->stream_error(stream_id),
QUIC_STREAM_CANCELLED);
EXPECT_EQ(server_stream_delegate_->stream_error(stream_id),
QUIC_STREAM_CANCELLED);
}
} // namespace
} // namespace quic
#endif // !defined(OS_IOS)