Commit dc916874 authored by rajendrant's avatar rajendrant Committed by Commit Bot

Refactor should redirect check to callback

The upcoming robots rules based subresource redirect check is
asynchronous. So this CL refactors the should redirect image check to a
callback. It should be noted that the callback can happen synchronously
too, i.e., while the should redirect call is still executing. This CL
handles both the cases.

Bug: 1144836
Change-Id: I7afc42239e93834ab41128e9ebe611c1fef828ca
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2533287
Commit-Queue: rajendrant <rajendrant@chromium.org>
Reviewed-by: default avatarMichael Crouse <mcrouse@chromium.org>
Cr-Commit-Position: refs/heads/master@{#828619}
parent bc2114c0
...@@ -47,7 +47,9 @@ PublicImageHintsURLLoaderThrottle::GetSubresourceRedirectHintsAgent() { ...@@ -47,7 +47,9 @@ PublicImageHintsURLLoaderThrottle::GetSubresourceRedirectHintsAgent() {
GetRenderFrame()); GetRenderFrame());
} }
bool PublicImageHintsURLLoaderThrottle::ShouldRedirectImage(const GURL& url) { base::Optional<bool> PublicImageHintsURLLoaderThrottle::ShouldRedirectImage(
const GURL& url,
SubresourceRedirectURLLoaderThrottle::RedirectDecisionCallback callback) {
if (redirect_result_ != if (redirect_result_ !=
SubresourceRedirectHintsAgent::RedirectResult::kRedirectable) { SubresourceRedirectHintsAgent::RedirectResult::kRedirectable) {
return false; return false;
......
...@@ -13,8 +13,7 @@ ...@@ -13,8 +13,7 @@
namespace subresource_redirect { namespace subresource_redirect {
// This class handles internal redirects for public subresouces on HTTPS sites // This class allows image compression based on public image hints received.
// to compressed versions of subresources.
class PublicImageHintsURLLoaderThrottle class PublicImageHintsURLLoaderThrottle
: public SubresourceRedirectURLLoaderThrottle { : public SubresourceRedirectURLLoaderThrottle {
public: public:
...@@ -23,8 +22,16 @@ class PublicImageHintsURLLoaderThrottle ...@@ -23,8 +22,16 @@ class PublicImageHintsURLLoaderThrottle
~PublicImageHintsURLLoaderThrottle() override; ~PublicImageHintsURLLoaderThrottle() override;
PublicImageHintsURLLoaderThrottle(const PublicImageHintsURLLoaderThrottle&) =
delete;
PublicImageHintsURLLoaderThrottle& operator=(
const PublicImageHintsURLLoaderThrottle&) = delete;
// SubresourceRedirectURLLoaderThrottle: // SubresourceRedirectURLLoaderThrottle:
bool ShouldRedirectImage(const GURL& url) override; base::Optional<bool> ShouldRedirectImage(
const GURL& url,
SubresourceRedirectURLLoaderThrottle::RedirectDecisionCallback callback)
override;
void OnRedirectedLoadCompleteWithError() override; void OnRedirectedLoadCompleteWithError() override;
void RecordMetricsOnLoadFinished(const GURL& url, void RecordMetricsOnLoadFinished(const GURL& url,
int64_t content_length) override; int64_t content_length) override;
......
...@@ -183,7 +183,7 @@ TEST_F(SubresourceRedirectPublicImageHintsURLLoaderThrottleTest, ...@@ -183,7 +183,7 @@ TEST_F(SubresourceRedirectPublicImageHintsURLLoaderThrottleTest,
bool defer = true; bool defer = true;
throttle->WillStartRequest(&request, &defer); throttle->WillStartRequest(&request, &defer);
EXPECT_EQ(defer, test_case.redirected_subresource_url.is_empty()); EXPECT_FALSE(defer);
if (!test_case.redirected_subresource_url.is_empty()) { if (!test_case.redirected_subresource_url.is_empty()) {
EXPECT_EQ(request.url, test_case.redirected_subresource_url); EXPECT_EQ(request.url, test_case.redirected_subresource_url);
} else { } else {
......
...@@ -77,22 +77,49 @@ void SubresourceRedirectURLLoaderThrottle::WillStartRequest( ...@@ -77,22 +77,49 @@ void SubresourceRedirectURLLoaderThrottle::WillStartRequest(
if (IsCompressionServerOrigin(request->url)) if (IsCompressionServerOrigin(request->url))
return; return;
if (!ShouldRedirectImage(request->url)) if (!ShouldCompressionServerRedirectSubresource())
return; return;
if (!ShouldCompressionServerRedirectSubresource()) auto redirect_decision = ShouldRedirectImage(
request->url,
base::BindOnce(
&SubresourceRedirectURLLoaderThrottle::NotifyRedirectDeciderDecision,
weak_ptr_factory_.GetWeakPtr()));
if (!redirect_decision) {
// Decision cannot be made yet. Defer the subresource and change the URL to
// compression server URL. The NotifyRedirectDeciderDecision callback will
// continue with compression or disable compression by resetting to original
// URL.
redirect_state_ = RedirectState::kDeciderDecisionPending;
*defer = true;
request->url = GetSubresourceURLForURL(request->url);
return; return;
}
request->url = GetSubresourceURLForURL(request->url); // The decider decision has been made.
did_redirect_compressed_origin_ = true;
*defer = false; *defer = false;
if (*redirect_decision) {
redirect_state_ = RedirectState::kRedirectAttempted;
request->url = GetSubresourceURLForURL(request->url);
StartRedirectTimeoutTimer();
} else {
redirect_state_ = RedirectState::kDeciderDisallowed;
}
}
DCHECK(!redirect_timeout_timer_); void SubresourceRedirectURLLoaderThrottle::NotifyRedirectDeciderDecision(
redirect_timeout_timer_ = std::make_unique<base::OneShotTimer>(); bool is_allowed) {
redirect_timeout_timer_->Start( DCHECK_EQ(RedirectState::kDeciderDecisionPending, redirect_state_);
FROM_HERE, GetCompressionRedirectTimeout(),
base::BindOnce(&SubresourceRedirectURLLoaderThrottle::OnRedirectTimeout, if (is_allowed) {
base::Unretained(this))); redirect_state_ = RedirectState::kRedirectAttempted;
delegate_->Resume();
StartRedirectTimeoutTimer();
} else {
// Restart the fetch to the original URL.
redirect_state_ = RedirectState::kDeciderDisallowed;
delegate_->RestartWithURLResetAndFlags(net::LOAD_NORMAL);
}
} }
void SubresourceRedirectURLLoaderThrottle::WillRedirectRequest( void SubresourceRedirectURLLoaderThrottle::WillRedirectRequest(
...@@ -102,7 +129,13 @@ void SubresourceRedirectURLLoaderThrottle::WillRedirectRequest( ...@@ -102,7 +129,13 @@ void SubresourceRedirectURLLoaderThrottle::WillRedirectRequest(
std::vector<std::string>* to_be_removed_request_headers, std::vector<std::string>* to_be_removed_request_headers,
net::HttpRequestHeaders* modified_request_headers, net::HttpRequestHeaders* modified_request_headers,
net::HttpRequestHeaders* modified_cors_exempt_request_headers) { net::HttpRequestHeaders* modified_cors_exempt_request_headers) {
if (did_redirect_compressed_origin_ && redirect_timeout_timer_) { // Check if the redirect is in some terminal state.
DCHECK((redirect_state_ == RedirectState::kNone) ||
(redirect_state_ == RedirectState::kRedirectAttempted) ||
(redirect_state_ == RedirectState::kDeciderDisallowed) ||
redirect_state_ == RedirectState::kRedirectFailed);
if (redirect_state_ == RedirectState::kRedirectAttempted &&
redirect_timeout_timer_) {
redirect_timeout_timer_->Start( redirect_timeout_timer_->Start(
FROM_HERE, GetCompressionRedirectTimeout(), FROM_HERE, GetCompressionRedirectTimeout(),
base::BindOnce(&SubresourceRedirectURLLoaderThrottle::OnRedirectTimeout, base::BindOnce(&SubresourceRedirectURLLoaderThrottle::OnRedirectTimeout,
...@@ -118,7 +151,12 @@ void SubresourceRedirectURLLoaderThrottle::BeforeWillProcessResponse( ...@@ -118,7 +151,12 @@ void SubresourceRedirectURLLoaderThrottle::BeforeWillProcessResponse(
const GURL& response_url, const GURL& response_url,
const network::mojom::URLResponseHead& response_head, const network::mojom::URLResponseHead& response_head,
bool* defer) { bool* defer) {
if (!did_redirect_compressed_origin_) // Check if the redirect is in some terminal state.
DCHECK((redirect_state_ == RedirectState::kNone) ||
(redirect_state_ == RedirectState::kRedirectAttempted) ||
(redirect_state_ == RedirectState::kDeciderDisallowed) ||
redirect_state_ == RedirectState::kRedirectFailed);
if (redirect_state_ != RedirectState::kRedirectAttempted)
return; return;
DCHECK(ShouldCompressionServerRedirectSubresource()); DCHECK(ShouldCompressionServerRedirectSubresource());
// If response was not from the compression server, don't restart it. // If response was not from the compression server, don't restart it.
...@@ -162,7 +200,7 @@ void SubresourceRedirectURLLoaderThrottle::BeforeWillProcessResponse( ...@@ -162,7 +200,7 @@ void SubresourceRedirectURLLoaderThrottle::BeforeWillProcessResponse(
// Non 2XX responses from the compression server need to have unaltered // Non 2XX responses from the compression server need to have unaltered
// requests sent to the original resource. // requests sent to the original resource.
did_redirect_compressed_origin_ = false; redirect_state_ = RedirectState::kRedirectFailed;
delegate_->RestartWithURLResetAndFlags(net::LOAD_NORMAL); delegate_->RestartWithURLResetAndFlags(net::LOAD_NORMAL);
} }
...@@ -170,7 +208,13 @@ void SubresourceRedirectURLLoaderThrottle::WillProcessResponse( ...@@ -170,7 +208,13 @@ void SubresourceRedirectURLLoaderThrottle::WillProcessResponse(
const GURL& response_url, const GURL& response_url,
network::mojom::URLResponseHead* response_head, network::mojom::URLResponseHead* response_head,
bool* defer) { bool* defer) {
// If response was not from the compression server, don't record any metrics. // Check if the redirect is in some terminal state.
DCHECK((redirect_state_ == RedirectState::kNone) ||
(redirect_state_ == RedirectState::kRedirectAttempted) ||
(redirect_state_ == RedirectState::kDeciderDisallowed) ||
redirect_state_ == RedirectState::kRedirectFailed);
// If response was not from the compression server, don't record any
// metrics.
if (!response_url.is_valid()) if (!response_url.is_valid())
return; return;
if (response_head->was_fetched_via_cache) if (response_head->was_fetched_via_cache)
...@@ -181,7 +225,7 @@ void SubresourceRedirectURLLoaderThrottle::WillProcessResponse( ...@@ -181,7 +225,7 @@ void SubresourceRedirectURLLoaderThrottle::WillProcessResponse(
RecordMetricsOnLoadFinished(response_url, content_length); RecordMetricsOnLoadFinished(response_url, content_length);
if (!did_redirect_compressed_origin_) if (redirect_state_ != RedirectState::kRedirectAttempted)
return; return;
DCHECK(ShouldCompressionServerRedirectSubresource()); DCHECK(ShouldCompressionServerRedirectSubresource());
...@@ -212,23 +256,32 @@ void SubresourceRedirectURLLoaderThrottle::WillProcessResponse( ...@@ -212,23 +256,32 @@ void SubresourceRedirectURLLoaderThrottle::WillProcessResponse(
void SubresourceRedirectURLLoaderThrottle::WillOnCompleteWithError( void SubresourceRedirectURLLoaderThrottle::WillOnCompleteWithError(
const network::URLLoaderCompletionStatus& status, const network::URLLoaderCompletionStatus& status,
bool* defer) { bool* defer) {
if (!did_redirect_compressed_origin_) if (redirect_state_ != RedirectState::kRedirectAttempted)
return; return;
DCHECK(ShouldCompressionServerRedirectSubresource()); DCHECK(ShouldCompressionServerRedirectSubresource());
OnRedirectedLoadCompleteWithError(); OnRedirectedLoadCompleteWithError();
// If the server fails, restart the request to the original resource, and // If the server fails, restart the request to the original resource, and
// record it. // record it.
did_redirect_compressed_origin_ = false; redirect_state_ = RedirectState::kRedirectFailed;
redirect_timeout_timer_.reset(); redirect_timeout_timer_.reset();
delegate_->RestartWithURLResetAndFlags(net::LOAD_NORMAL); delegate_->RestartWithURLResetAndFlags(net::LOAD_NORMAL);
UMA_HISTOGRAM_BOOLEAN( UMA_HISTOGRAM_BOOLEAN(
"SubresourceRedirect.CompressionAttempt.ServerResponded", false); "SubresourceRedirect.CompressionAttempt.ServerResponded", false);
} }
void SubresourceRedirectURLLoaderThrottle::StartRedirectTimeoutTimer() {
DCHECK(!redirect_timeout_timer_);
redirect_timeout_timer_ = std::make_unique<base::OneShotTimer>();
redirect_timeout_timer_->Start(
FROM_HERE, GetCompressionRedirectTimeout(),
base::BindOnce(&SubresourceRedirectURLLoaderThrottle::OnRedirectTimeout,
base::Unretained(this)));
}
void SubresourceRedirectURLLoaderThrottle::OnRedirectTimeout() { void SubresourceRedirectURLLoaderThrottle::OnRedirectTimeout() {
DCHECK(did_redirect_compressed_origin_); DCHECK_EQ(RedirectState::kRedirectAttempted, redirect_state_);
did_redirect_compressed_origin_ = false; redirect_state_ = RedirectState::kRedirectFailed;
delegate_->RestartWithURLResetAndFlagsNow(net::LOAD_NORMAL); delegate_->RestartWithURLResetAndFlagsNow(net::LOAD_NORMAL);
if (auto* subresource_redirect_hints_agent = if (auto* subresource_redirect_hints_agent =
SubresourceRedirectHintsAgent::Get(GetRenderFrame())) { SubresourceRedirectHintsAgent::Get(GetRenderFrame())) {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_URL_LOADER_THROTTLE_H_ #define CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_URL_LOADER_THROTTLE_H_
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame.h"
#include "third_party/blink/public/common/loader/url_loader_throttle.h" #include "third_party/blink/public/common/loader/url_loader_throttle.h"
...@@ -22,12 +23,19 @@ namespace subresource_redirect { ...@@ -22,12 +23,19 @@ namespace subresource_redirect {
// should implement the decider logic if an URL should be compressed. // should implement the decider logic if an URL should be compressed.
class SubresourceRedirectURLLoaderThrottle : public blink::URLLoaderThrottle { class SubresourceRedirectURLLoaderThrottle : public blink::URLLoaderThrottle {
public: public:
using RedirectDecisionCallback = base::OnceCallback<void(bool)>;
static std::unique_ptr<SubresourceRedirectURLLoaderThrottle> static std::unique_ptr<SubresourceRedirectURLLoaderThrottle>
MaybeCreateThrottle(const blink::WebURLRequest& request, int render_frame_id); MaybeCreateThrottle(const blink::WebURLRequest& request, int render_frame_id);
explicit SubresourceRedirectURLLoaderThrottle(int render_frame_id); explicit SubresourceRedirectURLLoaderThrottle(int render_frame_id);
~SubresourceRedirectURLLoaderThrottle() override; ~SubresourceRedirectURLLoaderThrottle() override;
SubresourceRedirectURLLoaderThrottle(
const SubresourceRedirectURLLoaderThrottle&) = delete;
SubresourceRedirectURLLoaderThrottle& operator=(
const SubresourceRedirectURLLoaderThrottle&) = delete;
// blink::URLLoaderThrottle: // blink::URLLoaderThrottle:
void WillStartRequest(network::ResourceRequest* request, void WillStartRequest(network::ResourceRequest* request,
bool* defer) override; bool* defer) override;
...@@ -50,12 +58,17 @@ class SubresourceRedirectURLLoaderThrottle : public blink::URLLoaderThrottle { ...@@ -50,12 +58,17 @@ class SubresourceRedirectURLLoaderThrottle : public blink::URLLoaderThrottle {
// Overridden to do nothing as the default implementation is NOT_REACHED() // Overridden to do nothing as the default implementation is NOT_REACHED()
void DetachFromCurrentSequence() override; void DetachFromCurrentSequence() override;
// Return whether the image url should be redirected. // Determine whether the image url should be redirected. When the
virtual bool ShouldRedirectImage(const GURL& url) = 0; // determination can be made immediately, the decision should be returned.
// Otherwise base::nullopt should be returned and the callback should be
// invoked with the decision asynchronously.
virtual base::Optional<bool> ShouldRedirectImage(
const GURL& url,
RedirectDecisionCallback callback) = 0;
// Indicates the subresource redirect failed, and the image will be fetched // Indicates the subresource redirect failed, and the image will be fetched
// directly from the origin instead. The failures can be due to non-2xx http // directly from the origin instead. The failures can be due to non-2xx
// responses or other net errors // http responses or other net errors
virtual void OnRedirectedLoadCompleteWithError() = 0; virtual void OnRedirectedLoadCompleteWithError() = 0;
// Notifies the image load finished. // Notifies the image load finished.
...@@ -67,25 +80,48 @@ class SubresourceRedirectURLLoaderThrottle : public blink::URLLoaderThrottle { ...@@ -67,25 +80,48 @@ class SubresourceRedirectURLLoaderThrottle : public blink::URLLoaderThrottle {
} }
private: private:
// Different states the subresource redirection can be in.
enum class RedirectState {
kNone,
// The redirect decision is pending from the underlying decider.
kDeciderDecisionPending,
// Redirect was disallowed by the underlying decider e.g., robots rules
// decider.
kDeciderDisallowed,
// The decider allowed redirect, and was attempted.
kRedirectAttempted,
// Failed due to http response codes, net errors, and the subresource was
// fetched from original origin.
kRedirectFailed
};
friend class TestPublicImageHintsURLLoaderThrottle; friend class TestPublicImageHintsURLLoaderThrottle;
// Callback to notify the decision of decider subclasses.
void NotifyRedirectDeciderDecision(bool is_allowed);
// Start the timer for redirect fetch timeout.
void StartRedirectTimeoutTimer();
// Callback invoked when the redirect fetch times out. // Callback invoked when the redirect fetch times out.
void OnRedirectTimeout(); void OnRedirectTimeout();
// Render frame id to get the hints agent of the render frame. // Render frame id to get the hints agent of the render frame.
const int render_frame_id_; const int render_frame_id_;
// Whether this resource was actually redirected to compressed server origin. // The current state of redirect.
// This will be true when the redirect was attempted. Will be false when RedirectState redirect_state_ = RedirectState::kNone;
// redirect failed due to neterrors, or redirect was not attempted (but
// coverage metrics recorded), or redirect was not needed when the initial URL
// itself is compressed origin.
bool did_redirect_compressed_origin_ = false;
// Timer to detect whether the response from compression server has timed out. // Timer to detect whether the response from compression server has timed out.
std::unique_ptr<base::OneShotTimer> redirect_timeout_timer_; std::unique_ptr<base::OneShotTimer> redirect_timeout_timer_;
DISALLOW_COPY_AND_ASSIGN(SubresourceRedirectURLLoaderThrottle); // Used to get a weak pointer to |this|.
base::WeakPtrFactory<SubresourceRedirectURLLoaderThrottle> weak_ptr_factory_{
this};
}; };
} // namespace subresource_redirect } // namespace subresource_redirect
......
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