blob: 697eb5e8efbc0e3ce23d1fe61ce6101cffedbddc [file] [log] [blame]
// Copyright 2018 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 "components/search/url_validity_checker_impl.h"
#include "base/bind.h"
#include "base/time/default_tick_clock.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
namespace {
// Request timeout duration.
constexpr base::TimeDelta kRequestTimeout = base::TimeDelta::FromSeconds(10);
} // namespace
// Stores the pending request and associated metadata. Deleted once the request
// finishes.
struct UrlValidityCheckerImpl::PendingRequest {
PendingRequest(const GURL& url,
const base::TimeTicks& time_created,
const base::TickClock* clock,
UrlValidityCheckerCallback callback)
: url(url),
time_created(time_created),
timeout_timer(clock),
callback(std::move(callback)) {}
GURL url;
base::TimeTicks time_created;
base::OneShotTimer timeout_timer;
UrlValidityCheckerCallback callback;
std::unique_ptr<network::SimpleURLLoader> loader;
DISALLOW_COPY_AND_ASSIGN(PendingRequest);
};
UrlValidityCheckerImpl::UrlValidityCheckerImpl(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const base::TickClock* tick_clock)
: url_loader_factory_(std::move(url_loader_factory)), clock_(tick_clock) {}
UrlValidityCheckerImpl::~UrlValidityCheckerImpl() = default;
void UrlValidityCheckerImpl::DoesUrlResolve(
const GURL& url,
net::NetworkTrafficAnnotationTag traffic_annotation,
UrlValidityCheckerCallback callback) {
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = url;
resource_request->method = "HEAD";
resource_request->allow_credentials = false;
auto request_iter = pending_requests_.emplace(pending_requests_.begin(), url,
clock_->NowTicks(), clock_,
std::move(callback));
request_iter->loader = network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation);
// Don't follow redirects to prevent leaking URL data to HTTP sites.
request_iter->loader->SetOnRedirectCallback(
base::BindRepeating(&UrlValidityCheckerImpl::OnSimpleLoaderRedirect,
weak_ptr_factory_.GetWeakPtr(), request_iter));
request_iter->loader->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(&UrlValidityCheckerImpl::OnSimpleLoaderComplete,
weak_ptr_factory_.GetWeakPtr(), request_iter),
/*max_body_size=*/1);
request_iter->timeout_timer.Start(
FROM_HERE, kRequestTimeout,
base::BindOnce(&UrlValidityCheckerImpl::OnSimpleLoaderHandler,
weak_ptr_factory_.GetWeakPtr(), request_iter, false));
}
void UrlValidityCheckerImpl::OnSimpleLoaderRedirect(
std::list<PendingRequest>::iterator request_iter,
const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& response_head,
std::vector<std::string>* to_be_removed_headers) {
// Assume the URL is valid if a redirect is returned.
OnSimpleLoaderHandler(request_iter, true);
}
void UrlValidityCheckerImpl::OnSimpleLoaderComplete(
std::list<PendingRequest>::iterator request_iter,
std::unique_ptr<std::string> response_body) {
// |response_body| is null for non-2xx responses.
OnSimpleLoaderHandler(request_iter, response_body.get() != nullptr);
}
void UrlValidityCheckerImpl::OnSimpleLoaderHandler(
std::list<PendingRequest>::iterator request_iter,
bool valid) {
base::TimeDelta elapsed_time =
clock_->NowTicks() - request_iter->time_created;
std::move(request_iter->callback).Run(valid, elapsed_time);
pending_requests_.erase(request_iter);
}