597 lines
24 KiB
C++
597 lines
24 KiB
C++
// Copyright 2019 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/cert/coalescing_cert_verifier.h"
|
|
|
|
#include <memory>
|
|
|
|
#include "base/functional/bind.h"
|
|
#include "base/test/bind.h"
|
|
#include "base/test/metrics/histogram_tester.h"
|
|
#include "net/base/net_errors.h"
|
|
#include "net/base/test_completion_callback.h"
|
|
#include "net/cert/mock_cert_verifier.h"
|
|
#include "net/cert/x509_certificate.h"
|
|
#include "net/log/net_log_with_source.h"
|
|
#include "net/test/cert_test_util.h"
|
|
#include "net/test/gtest_util.h"
|
|
#include "net/test/test_data_directory.h"
|
|
#include "net/test/test_with_task_environment.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
using net::test::IsError;
|
|
using net::test::IsOk;
|
|
|
|
namespace net {
|
|
|
|
using CoalescingCertVerifierTest = TestWithTaskEnvironment;
|
|
|
|
// Tests that synchronous completion does not cause any issues.
|
|
TEST_F(CoalescingCertVerifierTest, SyncCompletion) {
|
|
scoped_refptr<X509Certificate> test_cert(
|
|
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
|
|
ASSERT_TRUE(test_cert);
|
|
|
|
CertVerifyResult fake_result;
|
|
fake_result.verified_cert = test_cert;
|
|
|
|
std::unique_ptr<MockCertVerifier> mock_verifier_owner =
|
|
std::make_unique<MockCertVerifier>();
|
|
MockCertVerifier* mock_verifier = mock_verifier_owner.get();
|
|
mock_verifier->set_async(false); // Force sync completion.
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, OK);
|
|
|
|
CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
|
|
|
|
CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
|
|
/*ocsp_response=*/std::string(),
|
|
/*sct_list=*/std::string());
|
|
|
|
CertVerifyResult result1, result2;
|
|
TestCompletionCallback callback1, callback2;
|
|
std::unique_ptr<CertVerifier::Request> request1, request2;
|
|
|
|
// Start an (asynchronous) initial request.
|
|
int error = verifier.Verify(request_params, &result1, callback1.callback(),
|
|
&request1, NetLogWithSource());
|
|
ASSERT_THAT(error, IsOk());
|
|
ASSERT_FALSE(request1);
|
|
ASSERT_TRUE(result1.verified_cert);
|
|
}
|
|
|
|
// Test that requests with identical parameters only result in a single
|
|
// underlying verification; that is, the second Request is joined to the
|
|
// in-progress first Request.
|
|
TEST_F(CoalescingCertVerifierTest, InflightJoin) {
|
|
scoped_refptr<X509Certificate> test_cert(
|
|
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
|
|
ASSERT_TRUE(test_cert);
|
|
|
|
base::HistogramTester histograms;
|
|
|
|
CertVerifyResult fake_result;
|
|
fake_result.verified_cert = test_cert;
|
|
|
|
std::unique_ptr<MockCertVerifier> mock_verifier_owner =
|
|
std::make_unique<MockCertVerifier>();
|
|
MockCertVerifier* mock_verifier = mock_verifier_owner.get();
|
|
mock_verifier->set_async(true); // Always complete via PostTask
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, OK);
|
|
|
|
CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
|
|
|
|
CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
|
|
/*ocsp_response=*/std::string(),
|
|
/*sct_list=*/std::string());
|
|
|
|
CertVerifyResult result1, result2;
|
|
TestCompletionCallback callback1, callback2;
|
|
std::unique_ptr<CertVerifier::Request> request1, request2;
|
|
|
|
// Start an (asynchronous) initial request.
|
|
int error = verifier.Verify(request_params, &result1, callback1.callback(),
|
|
&request1, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request1);
|
|
|
|
// Simulate the underlying verifier returning different results if another
|
|
// verification is done.
|
|
mock_verifier->ClearRules();
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, ERR_CERT_REVOKED);
|
|
|
|
// Start a second request; this should join the first request.
|
|
error = verifier.Verify(request_params, &result2, callback2.callback(),
|
|
&request2, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request2);
|
|
|
|
// Ensure only one request was ever started.
|
|
EXPECT_EQ(2u, verifier.requests_for_testing());
|
|
EXPECT_EQ(1u, verifier.inflight_joins_for_testing());
|
|
|
|
// Make sure both results completed.
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
|
|
// There should only have been one Job started.
|
|
histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
|
|
histograms.ExpectTotalCount("Net.CertVerifier_First_Job_Latency", 1);
|
|
}
|
|
|
|
// Test that changing configurations between Requests prevents the second
|
|
// Request from being attached to the first Request. There should be two
|
|
// Requests to the underlying CertVerifier, and the correct results should be
|
|
// received by each.
|
|
TEST_F(CoalescingCertVerifierTest, DoesNotJoinAfterConfigChange) {
|
|
scoped_refptr<X509Certificate> test_cert(
|
|
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
|
|
ASSERT_TRUE(test_cert);
|
|
|
|
base::HistogramTester histograms;
|
|
|
|
CertVerifyResult fake_result;
|
|
fake_result.verified_cert = test_cert;
|
|
|
|
std::unique_ptr<MockCertVerifier> mock_verifier_owner =
|
|
std::make_unique<MockCertVerifier>();
|
|
MockCertVerifier* mock_verifier = mock_verifier_owner.get();
|
|
mock_verifier->set_async(true); // Always complete via PostTask
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, OK);
|
|
|
|
CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
|
|
|
|
CertVerifier::Config config1;
|
|
verifier.SetConfig(config1);
|
|
|
|
CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
|
|
/*ocsp_response=*/std::string(),
|
|
/*sct_list=*/std::string());
|
|
|
|
CertVerifyResult result1, result2;
|
|
TestCompletionCallback callback1, callback2;
|
|
std::unique_ptr<CertVerifier::Request> request1, request2;
|
|
|
|
// Start an (asynchronous) initial request.
|
|
int error = verifier.Verify(request_params, &result1, callback1.callback(),
|
|
&request1, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request1);
|
|
|
|
// Change the configuration, and change the result to to simulate the
|
|
// configuration change affecting behavior.
|
|
CertVerifier::Config config2;
|
|
config2.enable_rev_checking = !config1.enable_rev_checking;
|
|
verifier.SetConfig(config2);
|
|
mock_verifier->ClearRules();
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, ERR_CERT_REVOKED);
|
|
|
|
// Start a second request; this should not join the first request, as the
|
|
// config is different.
|
|
error = verifier.Verify(request_params, &result2, callback2.callback(),
|
|
&request2, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request2);
|
|
|
|
// Ensure a total of two requests were started, and neither were joined.
|
|
EXPECT_EQ(2u, verifier.requests_for_testing());
|
|
EXPECT_EQ(0u, verifier.inflight_joins_for_testing());
|
|
|
|
// Make sure both results completed.
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback2.WaitForResult(), IsError(ERR_CERT_REVOKED));
|
|
|
|
// There should have been two separate Jobs.
|
|
histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency", 2);
|
|
histograms.ExpectTotalCount("Net.CertVerifier_First_Job_Latency", 1);
|
|
}
|
|
|
|
// Test that the underlying CertVerifier changing configurations and triggering
|
|
// an OnCertVerifierChanged notification between Requests prevents the second
|
|
// Request from being attached to the first Request. There should be two
|
|
// Requests to the underlying CertVerifier, and the correct results should be
|
|
// received by each.
|
|
TEST_F(CoalescingCertVerifierTest, DoesNotJoinAfterUnderlyingVerifierChange) {
|
|
scoped_refptr<X509Certificate> test_cert(
|
|
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
|
|
ASSERT_TRUE(test_cert);
|
|
|
|
base::HistogramTester histograms;
|
|
|
|
CertVerifyResult fake_result;
|
|
fake_result.verified_cert = test_cert;
|
|
|
|
std::unique_ptr<MockCertVerifier> mock_verifier_owner =
|
|
std::make_unique<MockCertVerifier>();
|
|
MockCertVerifier* mock_verifier = mock_verifier_owner.get();
|
|
mock_verifier->set_async(true); // Always complete via PostTask
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, OK);
|
|
|
|
CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
|
|
|
|
mock_verifier->SimulateOnCertVerifierChanged();
|
|
|
|
CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
|
|
/*ocsp_response=*/std::string(),
|
|
/*sct_list=*/std::string());
|
|
|
|
CertVerifyResult result1, result2;
|
|
TestCompletionCallback callback1, callback2;
|
|
std::unique_ptr<CertVerifier::Request> request1, request2;
|
|
|
|
// Start an (asynchronous) initial request.
|
|
int error = verifier.Verify(request_params, &result1, callback1.callback(),
|
|
&request1, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request1);
|
|
|
|
// Change the configuration, and change the result to to simulate the
|
|
// configuration change affecting behavior.
|
|
mock_verifier->SimulateOnCertVerifierChanged();
|
|
mock_verifier->ClearRules();
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, ERR_CERT_REVOKED);
|
|
|
|
// Start a second request; this should not join the first request, as the
|
|
// config is different.
|
|
error = verifier.Verify(request_params, &result2, callback2.callback(),
|
|
&request2, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request2);
|
|
|
|
// Ensure a total of two requests were started, and neither were joined.
|
|
EXPECT_EQ(2u, verifier.requests_for_testing());
|
|
EXPECT_EQ(0u, verifier.inflight_joins_for_testing());
|
|
|
|
// Make sure both results completed.
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback2.WaitForResult(), IsError(ERR_CERT_REVOKED));
|
|
|
|
// There should have been two separate Jobs.
|
|
histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency", 2);
|
|
histograms.ExpectTotalCount("Net.CertVerifier_First_Job_Latency", 1);
|
|
}
|
|
|
|
TEST_F(CoalescingCertVerifierTest, ObserverIsForwarded) {
|
|
auto mock_cert_verifier_owner = std::make_unique<MockCertVerifier>();
|
|
MockCertVerifier* mock_cert_verifier = mock_cert_verifier_owner.get();
|
|
CoalescingCertVerifier verifier(std::move(mock_cert_verifier_owner));
|
|
|
|
CertVerifierObserverCounter observer_(&verifier);
|
|
EXPECT_EQ(observer_.change_count(), 0u);
|
|
// A CertVerifierChanged event on the wrapped verifier should be forwarded to
|
|
// observers registered on CoalescingCertVerifier.
|
|
mock_cert_verifier->SimulateOnCertVerifierChanged();
|
|
EXPECT_EQ(observer_.change_count(), 1u);
|
|
}
|
|
|
|
// Test that when two Requests are attached to the same Job, it's safe to
|
|
// delete the second Request while processing the response to the first. The
|
|
// second Request should not cause the second callback to be called.
|
|
TEST_F(CoalescingCertVerifierTest, DeleteSecondRequestDuringFirstCompletion) {
|
|
scoped_refptr<X509Certificate> test_cert(
|
|
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
|
|
ASSERT_TRUE(test_cert);
|
|
|
|
CertVerifyResult fake_result;
|
|
fake_result.verified_cert = test_cert;
|
|
|
|
std::unique_ptr<MockCertVerifier> mock_verifier_owner =
|
|
std::make_unique<MockCertVerifier>();
|
|
MockCertVerifier* mock_verifier = mock_verifier_owner.get();
|
|
mock_verifier->set_async(true); // Always complete via PostTask
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, OK);
|
|
|
|
CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
|
|
|
|
CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
|
|
/*ocsp_response=*/std::string(),
|
|
/*sct_list=*/std::string());
|
|
|
|
CertVerifyResult result1, result2;
|
|
TestCompletionCallback callback1, callback2;
|
|
std::unique_ptr<CertVerifier::Request> request1, request2;
|
|
|
|
// Start an (asynchronous) initial request. When this request is completed,
|
|
// it will delete (reset) |request2|, which should prevent it from being
|
|
// called.
|
|
int error = verifier.Verify(
|
|
request_params, &result1,
|
|
base::BindLambdaForTesting([&callback1, &request2](int result) {
|
|
request2.reset();
|
|
callback1.callback().Run(result);
|
|
}),
|
|
&request1, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request1);
|
|
|
|
// Start a second request; this should join the first request.
|
|
error = verifier.Verify(request_params, &result2, callback2.callback(),
|
|
&request2, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request2);
|
|
|
|
// Ensure only one underlying verification was started.
|
|
ASSERT_EQ(2u, verifier.requests_for_testing());
|
|
ASSERT_EQ(1u, verifier.inflight_joins_for_testing());
|
|
|
|
// Make sure that only the first callback is invoked; because the second
|
|
// CertVerifier::Request was deleted during processing the first's callback,
|
|
// the second callback should not be invoked.
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
ASSERT_FALSE(callback2.have_result());
|
|
ASSERT_FALSE(request2);
|
|
|
|
// While CoalescingCertVerifier doesn't use PostTask, make sure to flush the
|
|
// tasks as well, in case the implementation changes in the future.
|
|
RunUntilIdle();
|
|
ASSERT_FALSE(callback2.have_result());
|
|
ASSERT_FALSE(request2);
|
|
}
|
|
|
|
// Test that it's safe to delete the CoalescingCertVerifier during completion,
|
|
// even when there are outstanding Requests to be processed. The additional
|
|
// Requests should not invoke the user callback once the
|
|
// CoalescingCertVerifier is deleted.
|
|
TEST_F(CoalescingCertVerifierTest, DeleteVerifierDuringCompletion) {
|
|
scoped_refptr<X509Certificate> test_cert(
|
|
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
|
|
ASSERT_TRUE(test_cert);
|
|
|
|
CertVerifyResult fake_result;
|
|
fake_result.verified_cert = test_cert;
|
|
|
|
std::unique_ptr<MockCertVerifier> mock_verifier_owner =
|
|
std::make_unique<MockCertVerifier>();
|
|
MockCertVerifier* mock_verifier = mock_verifier_owner.get();
|
|
mock_verifier->set_async(true); // Always complete via PostTask
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, OK);
|
|
|
|
auto verifier =
|
|
std::make_unique<CoalescingCertVerifier>(std::move(mock_verifier_owner));
|
|
|
|
CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
|
|
/*ocsp_response=*/std::string(),
|
|
/*sct_list=*/std::string());
|
|
|
|
CertVerifyResult result1, result2;
|
|
TestCompletionCallback callback1, callback2;
|
|
std::unique_ptr<CertVerifier::Request> request1, request2;
|
|
|
|
// Start an (asynchronous) initial request. When this request is completed,
|
|
// it will delete (reset) |request2|, which should prevent it from being
|
|
// called.
|
|
int error = verifier->Verify(
|
|
request_params, &result1,
|
|
base::BindLambdaForTesting([&callback1, &verifier](int result) {
|
|
verifier.reset();
|
|
callback1.callback().Run(result);
|
|
}),
|
|
&request1, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request1);
|
|
|
|
// Start a second request; this should join the first request.
|
|
error = verifier->Verify(request_params, &result2, callback2.callback(),
|
|
&request2, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request2);
|
|
|
|
// Ensure only one underlying verification was started.
|
|
ASSERT_EQ(2u, verifier->requests_for_testing());
|
|
ASSERT_EQ(1u, verifier->inflight_joins_for_testing());
|
|
|
|
// Make sure that only the first callback is invoked. This will delete the
|
|
// underlying CoalescingCertVerifier, which should prevent the second
|
|
// request's callback from being invoked.
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
ASSERT_FALSE(callback2.have_result());
|
|
ASSERT_TRUE(request2);
|
|
|
|
// While CoalescingCertVerifier doesn't use PostTask, make sure to flush the
|
|
// tasks as well, in case the implementation changes in the future.
|
|
RunUntilIdle();
|
|
ASSERT_FALSE(callback2.have_result());
|
|
ASSERT_TRUE(request2);
|
|
}
|
|
|
|
// Test that it's safe to delete a Request before the underlying verifier has
|
|
// completed. This is a guard against memory safety (e.g. when this Request
|
|
// is the last/only Request remaining).
|
|
TEST_F(CoalescingCertVerifierTest, DeleteRequestBeforeCompletion) {
|
|
scoped_refptr<X509Certificate> test_cert(
|
|
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
|
|
ASSERT_TRUE(test_cert);
|
|
|
|
CertVerifyResult fake_result;
|
|
fake_result.verified_cert = test_cert;
|
|
|
|
std::unique_ptr<MockCertVerifier> mock_verifier_owner =
|
|
std::make_unique<MockCertVerifier>();
|
|
MockCertVerifier* mock_verifier = mock_verifier_owner.get();
|
|
mock_verifier->set_async(true); // Always complete via PostTask
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, OK);
|
|
|
|
CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
|
|
|
|
CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
|
|
/*ocsp_response=*/std::string(),
|
|
/*sct_list=*/std::string());
|
|
|
|
CertVerifyResult result1;
|
|
TestCompletionCallback callback1;
|
|
std::unique_ptr<CertVerifier::Request> request1;
|
|
|
|
// Start an (asynchronous) initial request.
|
|
int error = verifier.Verify(request_params, &result1, callback1.callback(),
|
|
&request1, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request1);
|
|
|
|
// Abandon the request before it's completed.
|
|
request1.reset();
|
|
EXPECT_FALSE(callback1.have_result());
|
|
|
|
// Make sure the request never completes / the callback is never invoked.
|
|
RunUntilIdle();
|
|
EXPECT_FALSE(callback1.have_result());
|
|
}
|
|
|
|
// Test that it's safe to delete a Request before the underlying verifier has
|
|
// completed. This is a correctness test, to ensure that other Requests are
|
|
// still notified.
|
|
TEST_F(CoalescingCertVerifierTest,
|
|
DeleteFirstRequestBeforeCompletionStillCompletesSecondRequest) {
|
|
scoped_refptr<X509Certificate> test_cert(
|
|
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
|
|
ASSERT_TRUE(test_cert);
|
|
|
|
CertVerifyResult fake_result;
|
|
fake_result.verified_cert = test_cert;
|
|
|
|
std::unique_ptr<MockCertVerifier> mock_verifier_owner =
|
|
std::make_unique<MockCertVerifier>();
|
|
MockCertVerifier* mock_verifier = mock_verifier_owner.get();
|
|
mock_verifier->set_async(true); // Always complete via PostTask
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, OK);
|
|
|
|
CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
|
|
|
|
CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
|
|
/*ocsp_response=*/std::string(),
|
|
/*sct_list=*/std::string());
|
|
|
|
CertVerifyResult result1, result2;
|
|
TestCompletionCallback callback1, callback2;
|
|
std::unique_ptr<CertVerifier::Request> request1, request2;
|
|
|
|
// Start an (asynchronous) initial request.
|
|
int error = verifier.Verify(request_params, &result1, callback1.callback(),
|
|
&request1, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request1);
|
|
|
|
// Start a second request; this should join the first request.
|
|
error = verifier.Verify(request_params, &result2, callback2.callback(),
|
|
&request2, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request2);
|
|
|
|
// Ensure only one underlying verification was started.
|
|
ASSERT_EQ(2u, verifier.requests_for_testing());
|
|
ASSERT_EQ(1u, verifier.inflight_joins_for_testing());
|
|
|
|
// Abandon the first request before it's completed.
|
|
request1.reset();
|
|
|
|
// Make sure the first request never completes / the callback is never
|
|
// invoked, while the second request completes normally.
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
EXPECT_FALSE(callback1.have_result());
|
|
|
|
// Simulate the second request going away during processing.
|
|
request2.reset();
|
|
|
|
// Flush any events, although there should not be any.
|
|
RunUntilIdle();
|
|
EXPECT_FALSE(callback1.have_result());
|
|
}
|
|
|
|
TEST_F(CoalescingCertVerifierTest, DeleteRequestDuringCompletion) {
|
|
scoped_refptr<X509Certificate> test_cert(
|
|
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
|
|
ASSERT_TRUE(test_cert);
|
|
|
|
CertVerifyResult fake_result;
|
|
fake_result.verified_cert = test_cert;
|
|
|
|
std::unique_ptr<MockCertVerifier> mock_verifier_owner =
|
|
std::make_unique<MockCertVerifier>();
|
|
MockCertVerifier* mock_verifier = mock_verifier_owner.get();
|
|
mock_verifier->set_async(true); // Always complete via PostTask
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, OK);
|
|
|
|
CoalescingCertVerifier verifier(std::move(mock_verifier_owner));
|
|
|
|
CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
|
|
/*ocsp_response=*/std::string(),
|
|
/*sct_list=*/std::string());
|
|
|
|
CertVerifyResult result1;
|
|
TestCompletionCallback callback1;
|
|
std::unique_ptr<CertVerifier::Request> request1;
|
|
|
|
// Start an (asynchronous) initial request.
|
|
int error = verifier.Verify(
|
|
request_params, &result1,
|
|
base::BindLambdaForTesting([&callback1, &request1](int result) {
|
|
// Delete the Request during the completion callback. This should be
|
|
// perfectly safe, and not cause any memory trouble, because the
|
|
// Request was already detached from the Job prior to being invoked.
|
|
request1.reset();
|
|
callback1.callback().Run(result);
|
|
}),
|
|
&request1, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request1);
|
|
|
|
// The result should be available, even though the request is deleted
|
|
// during the result processing. This should not cause any memory errors.
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
}
|
|
|
|
TEST_F(CoalescingCertVerifierTest, DeleteVerifierBeforeRequest) {
|
|
scoped_refptr<X509Certificate> test_cert(
|
|
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
|
|
ASSERT_TRUE(test_cert);
|
|
|
|
base::HistogramTester histograms;
|
|
|
|
CertVerifyResult fake_result;
|
|
fake_result.verified_cert = test_cert;
|
|
|
|
std::unique_ptr<MockCertVerifier> mock_verifier_owner =
|
|
std::make_unique<MockCertVerifier>();
|
|
MockCertVerifier* mock_verifier = mock_verifier_owner.get();
|
|
mock_verifier->set_async(true); // Always complete via PostTask
|
|
mock_verifier->AddResultForCert(test_cert, fake_result, OK);
|
|
|
|
auto verifier =
|
|
std::make_unique<CoalescingCertVerifier>(std::move(mock_verifier_owner));
|
|
|
|
CertVerifier::RequestParams request_params(test_cert, "www.example.com", 0,
|
|
/*ocsp_response=*/std::string(),
|
|
/*sct_list=*/std::string());
|
|
|
|
CertVerifyResult result1;
|
|
TestCompletionCallback callback1;
|
|
std::unique_ptr<CertVerifier::Request> request1;
|
|
|
|
// Start an (asynchronous) initial request.
|
|
int error = verifier->Verify(request_params, &result1, callback1.callback(),
|
|
&request1, NetLogWithSource());
|
|
ASSERT_THAT(error, IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request1);
|
|
|
|
// Delete the CoalescingCertVerifier first. This should orphan all
|
|
// outstanding Requests and delete all associated Jobs.
|
|
verifier.reset();
|
|
|
|
// Flush any pending tasks; there should not be any, at this point, but use
|
|
// it in case the implementation changes.
|
|
RunUntilIdle();
|
|
|
|
// Make sure the callback was never called.
|
|
EXPECT_FALSE(callback1.have_result());
|
|
|
|
// Delete the Request. This should be a no-op as the Request was orphaned
|
|
// when the CoalescingCertVerifier was deleted.
|
|
request1.reset();
|
|
|
|
// There should not have been any histograms logged.
|
|
histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency", 0);
|
|
histograms.ExpectTotalCount("Net.CertVerifier_First_Job_Latency", 0);
|
|
}
|
|
|
|
} // namespace net
|