Commit a08134d8 authored by dalyk's avatar dalyk Committed by Commit Bot

Make ResolveErrorInfo accessible from NavigationHandle.

The detailed host resolution error information in ResolveErrorInfo will
be obtained via NavigationHandle in a future cl to trigger captive portal
probes when navigations finish with secure DNS errors. The top-level error
code that currently exists in NavigationHandle does not contain sufficient
information to know whether certain errors (e.g. CERT_COMMON_NAME_INVALID)
occurred during a DNS-over-HTTPS lookup or when establishing a connection
to the desired endpoint.

Bug: 1016325
Change-Id: I3c28a2582aaf9a884f43924aa6c65867ac771453
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1954849
Commit-Queue: Katharine Daly <dalyk@google.com>
Reviewed-by: default avatarAlex Moshchuk <alexmos@chromium.org>
Reviewed-by: default avatarEric Orth <ericorth@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#727463}
parent 57f7f642
......@@ -1854,6 +1854,7 @@ void NavigationRequest::OnRequestFailedInternal(
pending_entry_ref_.reset();
net_error_ = static_cast<net::Error>(status.error_code);
resolve_error_info_ = status.resolve_error_info;
// If the request was canceled by the user do not show an error page.
if (status.error_code == net::ERR_ABORTED) {
......@@ -3635,6 +3636,10 @@ NavigationRequest::GetAuthChallengeInfo() {
return auth_challenge_info_;
}
net::ResolveErrorInfo NavigationRequest::GetResolveErrorInfo() {
return resolve_error_info_;
}
net::NetworkIsolationKey NavigationRequest::GetNetworkIsolationKey() {
if (network_isolation_key_)
return network_isolation_key_.value();
......
......@@ -39,6 +39,7 @@
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "net/base/proxy_server.h"
#include "net/dns/public/resolve_error_info.h"
#include "services/network/public/cpp/origin_policy.h"
#if defined(OS_ANDROID)
......@@ -233,6 +234,7 @@ class CONTENT_EXPORT NavigationRequest
net::HttpResponseInfo::ConnectionInfo GetConnectionInfo() override;
const base::Optional<net::SSLInfo>& GetSSLInfo() override;
const base::Optional<net::AuthChallengeInfo>& GetAuthChallengeInfo() override;
net::ResolveErrorInfo GetResolveErrorInfo() override;
net::NetworkIsolationKey GetNetworkIsolationKey() override;
void RegisterThrottleForTesting(
std::unique_ptr<NavigationThrottle> navigation_throttle) override;
......@@ -915,6 +917,13 @@ class CONTENT_EXPORT NavigationRequest
// checks are performed by the NavigationHandle.
bool has_stale_copy_in_cache_;
net::Error net_error_ = net::OK;
// Detailed host resolution error information. The error code in
// |resolve_error_info_.error| should be consistent with (but not necessarily
// the same as) |net_error_|. In the case of a host resolution error, for
// example, |net_error_| should be ERR_NAME_NOT_RESOLVED while
// |resolve_error_info_.error| may give a more detailed error such as
// ERR_DNS_TIMED_OUT.
net::ResolveErrorInfo resolve_error_info_;
// Identifies in which RenderProcessHost this navigation is expected to
// commit.
......
......@@ -1625,6 +1625,28 @@ IN_PROC_BROWSER_TEST_F(NavigationRequestBrowserTest,
}
}
class NavigationRequestHostResolutionFailureTest : public ContentBrowserTest {
protected:
void SetUpOnMainThread() override {
host_resolver()->AddSimulatedTimeoutFailure("*");
ASSERT_TRUE(embedded_test_server()->Start());
}
};
IN_PROC_BROWSER_TEST_F(NavigationRequestHostResolutionFailureTest,
HostResolutionFailure) {
GURL url(embedded_test_server()->GetURL("example.com", "/title1.html"));
NavigationHandleObserver observer(shell()->web_contents(), url);
EXPECT_FALSE(NavigateToURL(shell(), url));
EXPECT_TRUE(observer.has_committed());
EXPECT_TRUE(observer.is_error());
EXPECT_EQ(net::ERR_DNS_TIMED_OUT, observer.net_error_code());
EXPECT_EQ(net::ERR_DNS_TIMED_OUT, observer.resolve_error_info().error);
}
// Record and list the navigations that are started and finished.
class NavigationLogger : public WebContentsObserver {
public:
......
......@@ -18,6 +18,7 @@
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/network_isolation_key.h"
#include "net/dns/public/resolve_error_info.h"
#include "net/http/http_response_info.h"
#include "services/network/public/cpp/resource_request_body.h"
#include "ui/base/page_transition_types.h"
......@@ -285,6 +286,9 @@ class CONTENT_EXPORT NavigationHandle {
virtual const base::Optional<net::AuthChallengeInfo>&
GetAuthChallengeInfo() = 0;
// Returns host resolution error info associated with the request.
virtual net::ResolveErrorInfo GetResolveErrorInfo() = 0;
// Gets the NetworkIsolationKey associated with the navigation. Updated as
// redirects are followed. When one of the origins used to construct the
// NetworkIsolationKey is opaque, the returned NetworkIsolationKey will not be
......
......@@ -748,13 +748,18 @@ void BrowserTestBase::InitializeNetworkProcess() {
// TODO(jam: expand this when we try to make browser_tests and
// components_browsertests work.
if (rule.resolver_type ==
net::RuleBasedHostResolverProc::Rule::kResolverTypeFail) {
net::RuleBasedHostResolverProc::Rule::kResolverTypeFail ||
rule.resolver_type ==
net::RuleBasedHostResolverProc::Rule::kResolverTypeFailTimeout) {
// The host "wpad" is added automatically in TestHostResolver, so we don't
// need to send it to NetworkServiceTest.
if (rule.host_pattern != "wpad") {
network::mojom::RulePtr mojo_rule = network::mojom::Rule::New();
mojo_rule->resolver_type =
network::mojom::ResolverType::kResolverTypeFail;
(rule.resolver_type ==
net::RuleBasedHostResolverProc::Rule::kResolverTypeFail)
? network::mojom::ResolverType::kResolverTypeFail
: network::mojom::ResolverType::kResolverTypeFailTimeout;
mojo_rule->host_pattern = rule.host_pattern;
mojo_rules.push_back(std::move(mojo_rule));
}
......
......@@ -90,6 +90,9 @@ class MockNavigationHandle : public NavigationHandle {
override {
return auth_challenge_info_;
}
net::ResolveErrorInfo GetResolveErrorInfo() override {
return resolve_error_info_;
}
MOCK_METHOD0(GetNetworkIsolationKey, net::NetworkIsolationKey());
MOCK_METHOD0(GetGlobalRequestID, const GlobalRequestID&());
MOCK_METHOD0(IsDownload, bool());
......@@ -173,6 +176,7 @@ class MockNavigationHandle : public NavigationHandle {
scoped_refptr<net::HttpResponseHeaders> response_headers_;
base::Optional<net::SSLInfo> ssl_info_;
base::Optional<net::AuthChallengeInfo> auth_challenge_info_;
net::ResolveErrorInfo resolve_error_info_;
bool is_form_submission_ = false;
bool was_response_cached_ = false;
net::ProxyServer proxy_server_;
......
......@@ -53,6 +53,7 @@ void NavigationHandleObserver::DidFinishNavigation(
net_error_code_ = navigation_handle->GetNetErrorCode();
is_download_ = navigation_handle->IsDownload();
auth_challenge_info_ = navigation_handle->GetAuthChallengeInfo();
resolve_error_info_ = navigation_handle->GetResolveErrorInfo();
if (navigation_handle->HasCommitted()) {
has_committed_ = true;
......
......@@ -37,6 +37,9 @@ class NavigationHandleObserver : public WebContentsObserver {
base::Optional<net::AuthChallengeInfo> auth_challenge_info() {
return auth_challenge_info_;
}
const net::ResolveErrorInfo& resolve_error_info() {
return resolve_error_info_;
}
base::TimeTicks navigation_start() { return navigation_start_; }
private:
......@@ -59,6 +62,7 @@ class NavigationHandleObserver : public WebContentsObserver {
int64_t navigation_id_ = -1;
bool is_download_ = false;
base::Optional<net::AuthChallengeInfo> auth_challenge_info_;
net::ResolveErrorInfo resolve_error_info_;
base::TimeTicks navigation_start_;
DISALLOW_COPY_AND_ASSIGN(NavigationHandleObserver);
......
......@@ -108,6 +108,9 @@ class NetworkServiceTestHelper::NetworkServiceTestImpl
case network::mojom::ResolverType::kResolverTypeFail:
host_resolver->AddSimulatedFailure(rule->host_pattern);
break;
case network::mojom::ResolverType::kResolverTypeFailTimeout:
host_resolver->AddSimulatedTimeoutFailure(rule->host_pattern);
break;
case network::mojom::ResolverType::kResolverTypeIPLiteral:
host_resolver->AddIPLiteralRule(rule->host_pattern, rule->replacement,
std::string());
......
......@@ -1247,6 +1247,50 @@ TEST_F(NetworkContextTest, CertReporting) {
}
}
// Test that host resolution error information is available.
TEST_F(NetworkContextTest, HostResolutionFailure) {
std::unique_ptr<net::MockHostResolver> resolver =
std::make_unique<net::MockHostResolver>();
std::unique_ptr<net::TestURLRequestContext> url_request_context =
std::make_unique<net::TestURLRequestContext>(
true /* delay_initialization */);
url_request_context->set_host_resolver(resolver.get());
resolver->rules()->AddSimulatedTimeoutFailure("*");
url_request_context->Init();
network_context_remote_.reset();
std::unique_ptr<NetworkContext> network_context =
std::make_unique<NetworkContext>(
network_service_.get(),
network_context_remote_.BindNewPipeAndPassReceiver(),
url_request_context.get(),
/*cors_exempt_header_list=*/std::vector<std::string>());
mojo::Remote<mojom::URLLoaderFactory> loader_factory;
mojom::URLLoaderFactoryParamsPtr params =
mojom::URLLoaderFactoryParams::New();
params->process_id = mojom::kBrowserProcessId;
params->is_corb_enabled = false;
network_context->CreateURLLoaderFactory(
loader_factory.BindNewPipeAndPassReceiver(), std::move(params));
ResourceRequest request;
request.url = GURL("http://example.test");
mojo::PendingRemote<mojom::URLLoader> loader;
TestURLLoaderClient client;
loader_factory->CreateLoaderAndStart(
loader.InitWithNewPipeAndPassReceiver(), 0 /* routing_id */,
0 /* request_id */, 0 /* options */, request, client.CreateRemote(),
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
client.RunUntilComplete();
EXPECT_TRUE(client.has_received_completion());
EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, client.completion_status().error_code);
EXPECT_EQ(net::ERR_DNS_TIMED_OUT,
client.completion_status().resolve_error_info.error);
}
// Test that valid referrers are allowed, while invalid ones result in errors.
TEST_F(NetworkContextTest, Referrers) {
const GURL kReferrer = GURL("http://referrer/");
......
......@@ -314,6 +314,20 @@ void ParamTraits<net::OCSPVerifyResult>::Log(const param_type& p,
l->append("<OCSPVerifyResult>");
}
void ParamTraits<net::ResolveErrorInfo>::Write(base::Pickle* m,
const param_type& p) {
WriteParam(m, p.error);
}
bool ParamTraits<net::ResolveErrorInfo>::Read(const base::Pickle* m,
base::PickleIterator* iter,
param_type* r) {
return ReadParam(m, iter, &r->error);
}
void ParamTraits<net::ResolveErrorInfo>::Log(const param_type& p,
std::string* l) {
l->append("<ResolveErrorInfo>");
}
void ParamTraits<scoped_refptr<net::SSLCertRequestInfo>>::Write(
base::Pickle* m,
const param_type& p) {
......
......@@ -154,6 +154,16 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ParamTraits<net::OCSPVerifyResult> {
static void Log(const param_type& p, std::string* l);
};
template <>
struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ParamTraits<net::ResolveErrorInfo> {
typedef net::ResolveErrorInfo param_type;
static void Write(base::Pickle* m, const param_type& p);
static bool Read(const base::Pickle* m,
base::PickleIterator* iter,
param_type* r);
static void Log(const param_type& p, std::string* l);
};
template <>
struct COMPONENT_EXPORT(NETWORK_CPP_BASE)
ParamTraits<scoped_refptr<net::SSLCertRequestInfo>> {
......
......@@ -107,6 +107,8 @@ IPC_STRUCT_TRAITS_BEGIN(network::URLLoaderCompletionStatus)
IPC_STRUCT_TRAITS_MEMBER(ssl_info)
IPC_STRUCT_TRAITS_MEMBER(should_report_corb_blocking)
IPC_STRUCT_TRAITS_MEMBER(proxy_server)
IPC_STRUCT_TRAITS_MEMBER(resolve_error_info)
IPC_STRUCT_TRAITS_END()
IPC_ENUM_TRAITS_MAX_VALUE(network::mojom::FetchResponseType,
......
......@@ -12,6 +12,7 @@
#include "base/optional.h"
#include "base/time/time.h"
#include "net/base/proxy_server.h"
#include "net/dns/public/resolve_error_info.h"
#include "net/ssl/ssl_info.h"
#include "services/network/public/cpp/cors/cors_error_status.h"
#include "services/network/public/mojom/cors.mojom-shared.h"
......@@ -70,6 +71,9 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) URLLoaderCompletionStatus {
// The proxy server used for this request, if any.
net::ProxyServer proxy_server;
// Host resolution error info for this request.
net::ResolveErrorInfo resolve_error_info;
};
} // namespace network
......
......@@ -1382,6 +1382,8 @@ void URLLoader::NotifyCompleted(int error_code) {
status.encoded_body_length = url_request_->GetRawBodyBytes();
status.decoded_body_length = total_written_bytes_;
status.proxy_server = url_request_->proxy_server();
status.resolve_error_info =
url_request_->response_info().resolve_error_info;
if ((options_ & mojom::kURLLoadOptionSendSSLInfoForCertificateError) &&
net::IsCertStatusError(url_request_->ssl_info().cert_status)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment