blob: 30a3baf1d5888b1ccbe5f0380293cace148fb987 [file] [log] [blame]
// Copyright 2014 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 <memory>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/unguessable_token.h"
#include "content/browser/loader/navigation_url_loader.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigation_request_info.h"
#include "content/browser/web_package/prefetched_signed_exchange_cache.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_ui_data.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "content/test/test_navigation_url_loader_delegate.h"
#include "ipc/ipc_message.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_status_flags.h"
#include "net/http/http_response_headers.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/redirect_info.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/navigation/navigation_params.h"
#include "third_party/blink/public/mojom/loader/mixed_content.mojom.h"
#include "url/origin.h"
namespace content {
class NavigationURLLoaderTest : public testing::Test {
public:
NavigationURLLoaderTest()
: task_environment_(BrowserTaskEnvironment::IO_MAINLOOP),
browser_context_(new TestBrowserContext) {
base::RunLoop().RunUntilIdle();
}
std::unique_ptr<NavigationURLLoader> MakeTestLoader(
const GURL& url,
NavigationURLLoaderDelegate* delegate) {
return CreateTestLoader(url, delegate);
}
std::unique_ptr<NavigationURLLoader> CreateTestLoader(
const GURL& url,
NavigationURLLoaderDelegate* delegate) {
blink::mojom::BeginNavigationParamsPtr begin_params =
blink::mojom::BeginNavigationParams::New(
absl::nullopt /* initiator_frame_token */,
std::string() /* headers */, net::LOAD_NORMAL,
false /* skip_service_worker */,
blink::mojom::RequestContextType::LOCATION,
network::mojom::RequestDestination::kDocument,
blink::mojom::MixedContentContextType::kBlockable,
false /* is_form_submission */,
false /* was_initiated_by_link_click */,
GURL() /* searchable_form_url */,
std::string() /* searchable_form_encoding */,
GURL() /* client_side_redirect_url */,
absl::nullopt /* devtools_initiator_info */,
nullptr /* trust_token_params */, absl::nullopt /* impression */,
base::TimeTicks() /* renderer_before_unload_start */,
base::TimeTicks() /* renderer_before_unload_end */,
absl::nullopt /* web_bundle_token */);
auto common_params = blink::CreateCommonNavigationParams();
common_params->url = url;
common_params->initiator_origin = url::Origin::Create(url);
StoragePartition* storage_partition =
browser_context_->GetDefaultStoragePartition();
url::Origin origin = url::Origin::Create(url);
std::unique_ptr<NavigationRequestInfo> request_info(
std::make_unique<NavigationRequestInfo>(
std::move(common_params), std::move(begin_params),
net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kMainFrame, origin, origin,
net::SiteForCookies::FromUrl(url)),
true /* is_main_frame */, false /* are_ancestors_secure */,
FrameTreeNode::kFrameTreeNodeInvalidId /* frame_tree_node_id */,
false /* report_raw_headers */, false /* upgrade_if_insecure */,
nullptr /* blob_url_loader_factory */,
base::UnguessableToken::Create() /* devtools_navigation_token */,
base::UnguessableToken::Create() /* devtools_frame_token */,
false /* obey_origin_policy */,
net::HttpRequestHeaders() /* cors_exempt_headers */,
nullptr /* client_security_state */,
absl::nullopt /* devtools_accepted_stream_types */,
false /* is_pdf */));
return NavigationURLLoader::Create(
browser_context_.get(), storage_partition, std::move(request_info),
nullptr, nullptr, nullptr, nullptr, delegate,
NavigationURLLoader::LoaderType::kRegular, mojo::NullRemote(),
storage_partition->CreateURLLoaderNetworkObserverForNavigationRequest(
FrameTreeNode::kFrameTreeNodeInvalidId /* frame_tree_node_id */),
/*devtools_observer=*/mojo::NullRemote());
}
protected:
BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestBrowserContext> browser_context_;
};
// Tests that request failures are propagated correctly.
TEST_F(NavigationURLLoaderTest, RequestFailedNoCertError) {
TestNavigationURLLoaderDelegate delegate;
std::unique_ptr<NavigationURLLoader> loader =
MakeTestLoader(GURL("bogus:bogus"), &delegate);
loader->Start();
// Wait for the request to fail as expected.
delegate.WaitForRequestFailed();
EXPECT_EQ(net::ERR_ABORTED, delegate.net_error());
EXPECT_FALSE(delegate.ssl_info().is_valid());
EXPECT_EQ(1, delegate.on_request_handled_counter());
}
// Tests that request failures are propagated correctly for a (non-fatal) cert
// error:
// - |ssl_info| has the expected values.
TEST_F(NavigationURLLoaderTest, RequestFailedCertError) {
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
ASSERT_TRUE(https_server.Start());
TestNavigationURLLoaderDelegate delegate;
std::unique_ptr<NavigationURLLoader> loader =
MakeTestLoader(https_server.GetURL("/"), &delegate);
loader->Start();
// Wait for the request to fail as expected.
delegate.WaitForRequestFailed();
ASSERT_EQ(net::ERR_CERT_COMMON_NAME_INVALID, delegate.net_error());
net::SSLInfo ssl_info = delegate.ssl_info();
EXPECT_TRUE(ssl_info.is_valid());
EXPECT_TRUE(
https_server.GetCertificate()->EqualsExcludingChain(ssl_info.cert.get()));
EXPECT_EQ(net::ERR_CERT_COMMON_NAME_INVALID,
net::MapCertStatusToNetError(ssl_info.cert_status));
EXPECT_FALSE(ssl_info.is_fatal_cert_error);
EXPECT_EQ(1, delegate.on_request_handled_counter());
}
// Tests that request failures are propagated correctly for a fatal cert error:
// - |ssl_info| has the expected values.
TEST_F(NavigationURLLoaderTest, RequestFailedCertErrorFatal) {
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
ASSERT_TRUE(https_server.Start());
GURL url = https_server.GetURL("/");
// Set HSTS for the test domain in order to make SSL errors fatal.
base::Time expiry = base::Time::Now() + base::Days(1000);
bool include_subdomains = false;
auto* storage_partition = browser_context_->GetDefaultStoragePartition();
base::RunLoop run_loop;
storage_partition->GetNetworkContext()->AddHSTS(
url.host(), expiry, include_subdomains, run_loop.QuitClosure());
run_loop.Run();
TestNavigationURLLoaderDelegate delegate;
std::unique_ptr<NavigationURLLoader> loader = MakeTestLoader(url, &delegate);
loader->Start();
// Wait for the request to fail as expected.
delegate.WaitForRequestFailed();
ASSERT_EQ(net::ERR_CERT_COMMON_NAME_INVALID, delegate.net_error());
net::SSLInfo ssl_info = delegate.ssl_info();
EXPECT_TRUE(ssl_info.is_valid());
EXPECT_TRUE(
https_server.GetCertificate()->EqualsExcludingChain(ssl_info.cert.get()));
EXPECT_EQ(net::ERR_CERT_COMMON_NAME_INVALID,
net::MapCertStatusToNetError(ssl_info.cert_status));
EXPECT_TRUE(ssl_info.is_fatal_cert_error);
EXPECT_EQ(1, delegate.on_request_handled_counter());
}
} // namespace content