| // 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/quic/quartc/quartc_session.h" |
| |
| #include "net/quic/core/crypto/crypto_server_config_protobuf.h" |
| #include "net/quic/core/quic_simple_buffer_allocator.h" |
| #include "net/quic/core/quic_types.h" |
| #include "net/quic/core/tls_client_handshaker.h" |
| #include "net/quic/core/tls_server_handshaker.h" |
| #include "net/quic/platform/api/quic_ptr_util.h" |
| #include "net/quic/quartc/quartc_factory.h" |
| #include "net/quic/quartc/quartc_factory_interface.h" |
| #include "net/quic/quartc/quartc_packet_writer.h" |
| #include "net/quic/quartc/quartc_stream_interface.h" |
| #include "net/quic/test_tools/mock_clock.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using std::string; |
| |
| namespace net { |
| |
| namespace { |
| |
| static const char kExporterLabel[] = "label"; |
| static const uint8_t kExporterContext[] = "context"; |
| static const size_t kExporterContextLen = sizeof(kExporterContext); |
| static const size_t kOutputKeyLength = 20; |
| static QuartcStreamInterface::WriteParameters kDefaultWriteParam; |
| static QuartcSessionInterface::OutgoingStreamParameters kDefaultStreamParam; |
| static QuicByteCount kDefaultMaxPacketSize = 1200; |
| |
| // Single-threaded scheduled task runner based on a MockClock. |
| // |
| // Simulates asynchronous execution on a single thread by holding scheduled |
| // tasks until Run() is called. Performs no synchronization, assumes that |
| // Schedule() and Run() are called on the same thread. |
| class FakeTaskRunner : public QuartcTaskRunnerInterface { |
| public: |
| explicit FakeTaskRunner(MockClock* clock) |
| : tasks_([this](const TaskType& l, const TaskType& r) { |
| // Items at a later time should run after items at an earlier time. |
| // Priority queue comparisons should return true if l appears after r. |
| return l->time() > r->time(); |
| }), |
| clock_(clock) {} |
| |
| ~FakeTaskRunner() override {} |
| |
| // Runs all tasks scheduled in the next total_ms milliseconds. Advances the |
| // clock by total_ms. Runs tasks in time order. Executes tasks scheduled at |
| // the same in an arbitrary order. |
| void Run(uint32_t total_ms) { |
| for (uint32_t i = 0; i < total_ms; ++i) { |
| while (!tasks_.empty() && tasks_.top()->time() <= clock_->Now()) { |
| tasks_.top()->Run(); |
| tasks_.pop(); |
| } |
| clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); |
| } |
| } |
| |
| private: |
| class InnerTask { |
| public: |
| InnerTask(std::function<void()> task, QuicTime time) |
| : task_(std::move(task)), time_(time) {} |
| |
| void Cancel() { cancelled_ = true; } |
| |
| void Run() { |
| if (!cancelled_) { |
| task_(); |
| } |
| } |
| |
| QuicTime time() const { return time_; } |
| |
| private: |
| bool cancelled_ = false; |
| std::function<void()> task_; |
| QuicTime time_; |
| }; |
| |
| public: |
| // Hook for cancelling a scheduled task. |
| class ScheduledTask : public QuartcTaskRunnerInterface::ScheduledTask { |
| public: |
| explicit ScheduledTask(std::shared_ptr<InnerTask> inner) |
| : inner_(std::move(inner)) {} |
| |
| // Cancel if the caller deletes the ScheduledTask. This behavior is |
| // consistent with the actual task runner Quartc uses. |
| ~ScheduledTask() override { Cancel(); } |
| |
| // ScheduledTask implementation. |
| void Cancel() override { inner_->Cancel(); } |
| |
| private: |
| std::shared_ptr<InnerTask> inner_; |
| }; |
| |
| // See QuartcTaskRunnerInterface. |
| std::unique_ptr<QuartcTaskRunnerInterface::ScheduledTask> Schedule( |
| Task* task, |
| uint64_t delay_ms) override { |
| auto inner = std::shared_ptr<InnerTask>(new InnerTask( |
| [task] { task->Run(); }, |
| clock_->Now() + QuicTime::Delta::FromMilliseconds(delay_ms))); |
| tasks_.push(inner); |
| return std::unique_ptr<QuartcTaskRunnerInterface::ScheduledTask>( |
| new ScheduledTask(inner)); |
| } |
| |
| // Schedules a function to run immediately. |
| void Schedule(std::function<void()> task) { |
| tasks_.push(std::shared_ptr<InnerTask>( |
| new InnerTask(std::move(task), clock_->Now()))); |
| } |
| |
| private: |
| // InnerTasks are shared by the queue and ScheduledTask (which hooks into it |
| // to implement Cancel()). |
| using TaskType = std::shared_ptr<InnerTask>; |
| std::priority_queue<TaskType, |
| std::vector<TaskType>, |
| std::function<bool(const TaskType&, const TaskType&)>> |
| tasks_; |
| MockClock* clock_; |
| }; |
| |
| // QuartcClock that wraps a MockClock. |
| // |
| // This is silly because Quartc wraps it as a QuicClock, and MockClock is |
| // already a QuicClock. But we don't have much choice. We need to pass a |
| // QuartcClockInterface into the Quartc wrappers. |
| class MockQuartcClock : public QuartcClockInterface { |
| public: |
| explicit MockQuartcClock(MockClock* clock) : clock_(clock) {} |
| |
| int64_t NowMicroseconds() override { |
| return clock_->WallNow().ToUNIXMicroseconds(); |
| } |
| |
| private: |
| MockClock* clock_; |
| }; |
| |
| // Used by QuicCryptoServerConfig to provide server credentials, returning a |
| // canned response equal to |success|. |
| class FakeProofSource : public ProofSource { |
| public: |
| explicit FakeProofSource(bool success) : success_(success) {} |
| |
| // ProofSource override. |
| void GetProof(const QuicSocketAddress& server_ip, |
| const string& hostname, |
| const string& server_config, |
| QuicTransportVersion transport_version, |
| QuicStringPiece chlo_hash, |
| std::unique_ptr<Callback> callback) override { |
| QuicReferenceCountedPointer<ProofSource::Chain> chain; |
| QuicCryptoProof proof; |
| if (success_) { |
| std::vector<string> certs; |
| certs.push_back("Required to establish handshake"); |
| chain = new ProofSource::Chain(certs); |
| proof.signature = "Signature"; |
| proof.leaf_cert_scts = "Time"; |
| } |
| callback->Run(success_, chain, proof, nullptr /* details */); |
| } |
| |
| QuicReferenceCountedPointer<Chain> GetCertChain( |
| const QuicSocketAddress& server_address, |
| const string& hostname) override { |
| return QuicReferenceCountedPointer<Chain>(); |
| } |
| |
| void ComputeTlsSignature( |
| const QuicSocketAddress& server_address, |
| const string& hostname, |
| uint16_t signature_algorithm, |
| QuicStringPiece in, |
| std::unique_ptr<SignatureCallback> callback) override { |
| callback->Run(true, "Signature"); |
| } |
| |
| private: |
| // Whether or not obtaining proof source succeeds. |
| bool success_; |
| }; |
| |
| // Used by QuicCryptoClientConfig to verify server credentials, returning a |
| // canned response of QUIC_SUCCESS if |success| is true. |
| class FakeProofVerifier : public ProofVerifier { |
| public: |
| explicit FakeProofVerifier(bool success) : success_(success) {} |
| |
| // ProofVerifier override |
| QuicAsyncStatus VerifyProof( |
| const string& hostname, |
| const uint16_t port, |
| const string& server_config, |
| QuicTransportVersion transport_version, |
| QuicStringPiece chlo_hash, |
| const std::vector<string>& certs, |
| const string& cert_sct, |
| const string& signature, |
| const ProofVerifyContext* context, |
| string* error_details, |
| std::unique_ptr<ProofVerifyDetails>* verify_details, |
| std::unique_ptr<ProofVerifierCallback> callback) override { |
| return success_ ? QUIC_SUCCESS : QUIC_FAILURE; |
| } |
| |
| QuicAsyncStatus VerifyCertChain( |
| const string& hostname, |
| const std::vector<string>& certs, |
| const ProofVerifyContext* context, |
| string* error_details, |
| std::unique_ptr<ProofVerifyDetails>* details, |
| std::unique_ptr<ProofVerifierCallback> callback) override { |
| LOG(INFO) << "VerifyProof() ignoring credentials and returning success"; |
| return success_ ? QUIC_SUCCESS : QUIC_FAILURE; |
| } |
| |
| private: |
| // Whether or not proof verification succeeds. |
| bool success_; |
| }; |
| |
| // Used by the FakeTransportChannel. |
| class FakeTransportChannelObserver { |
| public: |
| virtual ~FakeTransportChannelObserver() {} |
| |
| // Called when the other peer is trying to send message. |
| virtual void OnTransportChannelReadPacket(const string& data) = 0; |
| }; |
| |
| // Simulate the P2P communication transport. Used by the |
| // QuartcSessionInterface::Transport. |
| class FakeTransportChannel { |
| public: |
| explicit FakeTransportChannel(FakeTaskRunner* task_runner, MockClock* clock) |
| : task_runner_(task_runner), clock_(clock) {} |
| |
| void SetDestination(FakeTransportChannel* dest) { |
| if (!dest_) { |
| dest_ = dest; |
| dest_->SetDestination(this); |
| } |
| } |
| |
| int SendPacket(const char* data, size_t len) { |
| // If the destination is not set. |
| if (!dest_) { |
| return -1; |
| } |
| // Advance the time 10us to ensure the RTT is never 0ms. |
| clock_->AdvanceTime(QuicTime::Delta::FromMicroseconds(10)); |
| if (async_ && task_runner_) { |
| string packet(data, len); |
| task_runner_->Schedule([this, packet] { send(packet); }); |
| } else { |
| send(string(data, len)); |
| } |
| return static_cast<int>(len); |
| } |
| |
| void send(const string& data) { |
| DCHECK(dest_); |
| DCHECK(dest_->observer()); |
| dest_->observer()->OnTransportChannelReadPacket(data); |
| } |
| |
| FakeTransportChannelObserver* observer() { return observer_; } |
| |
| void SetObserver(FakeTransportChannelObserver* observer) { |
| observer_ = observer; |
| } |
| |
| void SetAsync(bool async) { async_ = async; } |
| |
| private: |
| // The writing destination of this channel. |
| FakeTransportChannel* dest_ = nullptr; |
| // The observer of this channel. Called when the received the data. |
| FakeTransportChannelObserver* observer_ = nullptr; |
| // If async, will send packets by running asynchronous tasks. |
| bool async_ = false; |
| // Used to send data asynchronously. |
| FakeTaskRunner* task_runner_; |
| // The test clock. Used to ensure the RTT is not 0. |
| MockClock* clock_; |
| }; |
| |
| // Used by the QuartcPacketWriter. |
| class FakeTransport : public QuartcSessionInterface::PacketTransport { |
| public: |
| explicit FakeTransport(FakeTransportChannel* channel) : channel_(channel) {} |
| |
| int Write(const char* buffer, size_t buf_len) override { |
| DCHECK(channel_); |
| return channel_->SendPacket(buffer, buf_len); |
| } |
| |
| private: |
| FakeTransportChannel* channel_; |
| }; |
| |
| class FakeQuartcSessionDelegate : public QuartcSessionInterface::Delegate { |
| public: |
| explicit FakeQuartcSessionDelegate( |
| QuartcStreamInterface::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(int error_code, bool from_remote) override { |
| connected_ = false; |
| } |
| // Called when an incoming QUIC stream is created. |
| void OnIncomingStream(QuartcStreamInterface* quartc_stream) override { |
| last_incoming_stream_ = quartc_stream; |
| last_incoming_stream_->SetDelegate(stream_delegate_); |
| } |
| |
| QuartcStreamInterface* incoming_stream() { return last_incoming_stream_; } |
| |
| bool connected() { return connected_; } |
| |
| private: |
| QuartcStreamInterface* last_incoming_stream_; |
| bool connected_ = true; |
| QuartcStream::Delegate* stream_delegate_; |
| }; |
| |
| class FakeQuartcStreamDelegate : public QuartcStreamInterface::Delegate { |
| public: |
| void OnReceived(QuartcStreamInterface* stream, |
| const char* data, |
| size_t size) override { |
| last_received_data_ = string(data, size); |
| } |
| |
| void OnClose(QuartcStreamInterface* stream) override {} |
| |
| void OnCanWrite(QuartcStreamInterface* stream) override {} |
| |
| string data() { return last_received_data_; } |
| |
| private: |
| string last_received_data_; |
| }; |
| |
| class QuartcSessionForTest : public QuartcSession, |
| public FakeTransportChannelObserver { |
| public: |
| QuartcSessionForTest(std::unique_ptr<QuicConnection> connection, |
| const QuicConfig& config, |
| const string& remote_fingerprint_value, |
| Perspective perspective, |
| QuicConnectionHelperInterface* helper, |
| QuicClock* clock) |
| : QuartcSession(std::move(connection), |
| config, |
| remote_fingerprint_value, |
| perspective, |
| helper, |
| clock) { |
| stream_delegate_ = QuicMakeUnique<FakeQuartcStreamDelegate>(); |
| session_delegate_ = |
| QuicMakeUnique<FakeQuartcSessionDelegate>((stream_delegate_.get())); |
| |
| SetDelegate(session_delegate_.get()); |
| } |
| |
| // QuartcPacketWriter override. |
| void OnTransportChannelReadPacket(const string& data) override { |
| OnTransportReceived(data.c_str(), data.length()); |
| } |
| |
| string data() { return stream_delegate_->data(); } |
| |
| bool has_data() { return !data().empty(); } |
| |
| FakeQuartcSessionDelegate* session_delegate() { |
| return session_delegate_.get(); |
| } |
| |
| FakeQuartcStreamDelegate* stream_delegate() { return stream_delegate_.get(); } |
| |
| private: |
| std::unique_ptr<FakeQuartcStreamDelegate> stream_delegate_; |
| std::unique_ptr<FakeQuartcSessionDelegate> session_delegate_; |
| }; |
| |
| class QuartcSessionTest : public ::testing::Test, |
| public QuicConnectionHelperInterface { |
| public: |
| ~QuartcSessionTest() override {} |
| |
| void Init() { |
| // Quic crashes if packets are sent at time 0, and the clock defaults to 0. |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000)); |
| client_channel_ = |
| QuicMakeUnique<FakeTransportChannel>(&task_runner_, &clock_); |
| server_channel_ = |
| QuicMakeUnique<FakeTransportChannel>(&task_runner_, &clock_); |
| // Make the channel asynchronous so that two peer will not keep calling each |
| // other when they exchange information. |
| client_channel_->SetAsync(true); |
| client_channel_->SetDestination(server_channel_.get()); |
| |
| client_transport_ = QuicMakeUnique<FakeTransport>(client_channel_.get()); |
| server_transport_ = QuicMakeUnique<FakeTransport>(server_channel_.get()); |
| |
| client_writer_ = QuicMakeUnique<QuartcPacketWriter>(client_transport_.get(), |
| kDefaultMaxPacketSize); |
| server_writer_ = QuicMakeUnique<QuartcPacketWriter>(server_transport_.get(), |
| kDefaultMaxPacketSize); |
| |
| client_writer_->SetWritable(); |
| server_writer_->SetWritable(); |
| } |
| |
| // The parameters are used to control whether the handshake will success or |
| // not. |
| void CreateClientAndServerSessions(bool client_handshake_success = true, |
| bool server_handshake_success = true) { |
| Init(); |
| client_peer_ = CreateSession(Perspective::IS_CLIENT); |
| server_peer_ = CreateSession(Perspective::IS_SERVER); |
| |
| client_channel_->SetObserver(client_peer_.get()); |
| server_channel_->SetObserver(server_peer_.get()); |
| |
| client_peer_->SetClientCryptoConfig(new QuicCryptoClientConfig( |
| std::unique_ptr<ProofVerifier>( |
| new FakeProofVerifier(client_handshake_success)), |
| TlsClientHandshaker::CreateSslCtx())); |
| |
| QuicCryptoServerConfig* server_config = new QuicCryptoServerConfig( |
| "TESTING", QuicRandom::GetInstance(), |
| std::unique_ptr<FakeProofSource>( |
| new FakeProofSource(server_handshake_success)), |
| TlsServerHandshaker::CreateSslCtx()); |
| // Provide server with serialized config string to prove ownership. |
| QuicCryptoServerConfig::ConfigOptions options; |
| std::unique_ptr<QuicServerConfigProtobuf> primary_config( |
| server_config->GenerateConfig(QuicRandom::GetInstance(), &clock_, |
| options)); |
| std::unique_ptr<CryptoHandshakeMessage> message( |
| server_config->AddConfig(std::move(primary_config), clock_.WallNow())); |
| |
| server_peer_->SetServerCryptoConfig(server_config); |
| } |
| |
| std::unique_ptr<QuartcSessionForTest> CreateSession(Perspective perspective) { |
| std::unique_ptr<QuicConnection> quic_connection = |
| CreateConnection(perspective); |
| string remote_fingerprint_value = "value"; |
| QuicConfig config; |
| return std::unique_ptr<QuartcSessionForTest>(new QuartcSessionForTest( |
| std::move(quic_connection), config, remote_fingerprint_value, |
| perspective, this, &clock_)); |
| } |
| |
| std::unique_ptr<QuicConnection> CreateConnection(Perspective perspective) { |
| QuartcPacketWriter* writer = perspective == Perspective::IS_CLIENT |
| ? client_writer_.get() |
| : server_writer_.get(); |
| QuicIpAddress ip; |
| ip.FromString("0.0.0.0"); |
| bool owns_writer = false; |
| if (!alarm_factory_) { |
| // QuartcFactory is only used as an alarm factory. |
| QuartcFactoryConfig config; |
| config.clock = &quartc_clock_; |
| config.task_runner = &task_runner_; |
| alarm_factory_ = QuicMakeUnique<QuartcFactory>(config); |
| } |
| return std::unique_ptr<QuicConnection>(new QuicConnection( |
| 0, QuicSocketAddress(ip, 0), this /*QuicConnectionHelperInterface*/, |
| alarm_factory_.get(), writer, owns_writer, perspective, |
| AllSupportedVersions())); |
| } |
| |
| // Runs all tasks scheduled in the next 200 ms. |
| void RunTasks() { task_runner_.Run(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()); |
| |
| uint8_t server_key[kOutputKeyLength]; |
| uint8_t client_key[kOutputKeyLength]; |
| bool use_context = true; |
| bool server_success = server_peer_->ExportKeyingMaterial( |
| kExporterLabel, kExporterContext, kExporterContextLen, use_context, |
| server_key, kOutputKeyLength); |
| ASSERT_TRUE(server_success); |
| bool client_success = client_peer_->ExportKeyingMaterial( |
| kExporterLabel, kExporterContext, kExporterContextLen, use_context, |
| client_key, kOutputKeyLength); |
| ASSERT_TRUE(client_success); |
| EXPECT_EQ(0, memcmp(server_key, client_key, sizeof(server_key))); |
| |
| // Now we can establish encrypted outgoing stream. |
| QuartcStreamInterface* outgoing_stream = |
| server_peer_->CreateOutgoingStream(kDefaultStreamParam); |
| ASSERT_NE(nullptr, outgoing_stream); |
| EXPECT_TRUE(server_peer_->HasOpenDynamicStreams()); |
| |
| outgoing_stream->SetDelegate(server_peer_->stream_delegate()); |
| |
| // Send a test message from peer 1 to peer 2. |
| const char kTestMessage[] = "Hello"; |
| outgoing_stream->Write(kTestMessage, strlen(kTestMessage), |
| kDefaultWriteParam); |
| RunTasks(); |
| |
| // Wait for peer 2 to receive messages. |
| ASSERT_TRUE(client_peer_->has_data()); |
| |
| QuartcStreamInterface* incoming = |
| client_peer_->session_delegate()->incoming_stream(); |
| ASSERT_TRUE(incoming); |
| EXPECT_TRUE(client_peer_->HasOpenDynamicStreams()); |
| |
| EXPECT_EQ(client_peer_->data(), kTestMessage); |
| // Send a test message from peer 2 to peer 1. |
| const char kTestResponse[] = "Response"; |
| incoming->Write(kTestResponse, strlen(kTestResponse), kDefaultWriteParam); |
| RunTasks(); |
| // Wait for peer 1 to receive messages. |
| ASSERT_TRUE(server_peer_->has_data()); |
| |
| EXPECT_EQ(server_peer_->data(), kTestResponse); |
| } |
| |
| // Test that client and server are not connected after handshake failure. |
| void TestDisconnectAfterFailedHandshake() { |
| EXPECT_TRUE(!client_peer_->session_delegate()->connected()); |
| EXPECT_TRUE(!server_peer_->session_delegate()->connected()); |
| |
| EXPECT_FALSE(client_peer_->IsEncryptionEstablished()); |
| EXPECT_FALSE(client_peer_->IsCryptoHandshakeConfirmed()); |
| |
| EXPECT_FALSE(server_peer_->IsEncryptionEstablished()); |
| EXPECT_FALSE(server_peer_->IsCryptoHandshakeConfirmed()); |
| } |
| |
| const QuicClock* GetClock() const override { return &clock_; } |
| |
| QuicRandom* GetRandomGenerator() override { |
| return QuicRandom::GetInstance(); |
| } |
| |
| QuicBufferAllocator* GetStreamSendBufferAllocator() override { |
| return &buffer_allocator_; |
| } |
| |
| protected: |
| std::unique_ptr<QuicAlarmFactory> alarm_factory_; |
| SimpleBufferAllocator buffer_allocator_; |
| MockClock clock_; |
| MockQuartcClock quartc_clock_{&clock_}; |
| |
| std::unique_ptr<FakeTransportChannel> client_channel_; |
| std::unique_ptr<FakeTransportChannel> server_channel_; |
| std::unique_ptr<FakeTransport> client_transport_; |
| std::unique_ptr<FakeTransport> server_transport_; |
| std::unique_ptr<QuartcPacketWriter> client_writer_; |
| std::unique_ptr<QuartcPacketWriter> server_writer_; |
| std::unique_ptr<QuartcSessionForTest> client_peer_; |
| std::unique_ptr<QuartcSessionForTest> server_peer_; |
| |
| FakeTaskRunner task_runner_{&clock_}; |
| }; |
| |
| TEST_F(QuartcSessionTest, StreamConnection) { |
| CreateClientAndServerSessions(); |
| StartHandshake(); |
| TestStreamConnection(); |
| } |
| |
| TEST_F(QuartcSessionTest, ClientRejection) { |
| CreateClientAndServerSessions(false /*client_handshake_success*/, |
| true /*server_handshake_success*/); |
| StartHandshake(); |
| TestDisconnectAfterFailedHandshake(); |
| } |
| |
| TEST_F(QuartcSessionTest, ServerRejection) { |
| CreateClientAndServerSessions(true /*client_handshake_success*/, |
| false /*server_handshake_success*/); |
| StartHandshake(); |
| TestDisconnectAfterFailedHandshake(); |
| } |
| |
| // Test that data streams are not created before handshake. |
| TEST_F(QuartcSessionTest, CannotCreateDataStreamBeforeHandshake) { |
| CreateClientAndServerSessions(); |
| EXPECT_EQ(nullptr, server_peer_->CreateOutgoingStream(kDefaultStreamParam)); |
| EXPECT_EQ(nullptr, client_peer_->CreateOutgoingStream(kDefaultStreamParam)); |
| } |
| |
| TEST_F(QuartcSessionTest, CloseQuartcStream) { |
| CreateClientAndServerSessions(); |
| StartHandshake(); |
| ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed()); |
| ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed()); |
| QuartcStreamInterface* stream = |
| client_peer_->CreateOutgoingStream(kDefaultStreamParam); |
| ASSERT_NE(nullptr, stream); |
| |
| uint32_t id = stream->stream_id(); |
| EXPECT_FALSE(client_peer_->IsClosedStream(id)); |
| stream->SetDelegate(client_peer_->stream_delegate()); |
| stream->Close(); |
| RunTasks(); |
| EXPECT_TRUE(client_peer_->IsClosedStream(id)); |
| } |
| |
| TEST_F(QuartcSessionTest, CancelQuartcStream) { |
| CreateClientAndServerSessions(); |
| StartHandshake(); |
| ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed()); |
| ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed()); |
| |
| QuartcStreamInterface* stream = |
| client_peer_->CreateOutgoingStream(kDefaultStreamParam); |
| ASSERT_NE(nullptr, stream); |
| |
| uint32_t id = stream->stream_id(); |
| EXPECT_FALSE(client_peer_->IsClosedStream(id)); |
| stream->SetDelegate(client_peer_->stream_delegate()); |
| client_peer_->CancelStream(id); |
| EXPECT_EQ(stream->stream_error(), |
| QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED); |
| EXPECT_TRUE(client_peer_->IsClosedStream(id)); |
| } |
| |
| TEST_F(QuartcSessionTest, GetStats) { |
| CreateClientAndServerSessions(); |
| StartHandshake(); |
| ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed()); |
| ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed()); |
| |
| QuartcSessionStats stats = server_peer_->GetStats(); |
| EXPECT_GT(stats.bandwidth_estimate, QuicBandwidth::Zero()); |
| EXPECT_GT(stats.smoothed_rtt, QuicTime::Delta::Zero()); |
| } |
| |
| 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_peer_->session_delegate()->connected()); |
| RunTasks(); |
| EXPECT_FALSE(server_peer_->session_delegate()->connected()); |
| } |
| |
| } // namespace |
| |
| } // namespace net |