Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(256)

Unified Diff: content/browser/loader/async_revalidation_driver.cc

Issue 1041993004: content::ResourceDispatcherHostImpl changes for stale-while-revalidate (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@s-w-r-yhirano-patch
Patch Set: Workaround iwyu bug in prerender_resource Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/loader/async_revalidation_driver.cc
diff --git a/content/browser/loader/async_revalidation_driver.cc b/content/browser/loader/async_revalidation_driver.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c371bb16892784b0a18862dd52233152233d4c72
--- /dev/null
+++ b/content/browser/loader/async_revalidation_driver.cc
@@ -0,0 +1,323 @@
+// Copyright 2015 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 "content/browser/loader/async_revalidation_driver.h"
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/user_metrics_action.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "content/public/browser/user_metrics.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/http/http_transaction_factory.h"
+#include "net/http/http_util.h"
+#include "net/ssl/ssl_info.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_status.h"
+
+using base::TimeDelta;
+using base::TimeTicks;
+
+namespace content {
+
+namespace {
+// This matches the maximum allocation size of AsyncResourceHandler.
+const int kReadBufSize = 32 * 1024;
+const int kReadTimeoutSeconds = 5 * 60;
+}
+
+AsyncRevalidationKey::AsyncRevalidationKey(
+ const ResourceContext* resource_context,
+ const net::HttpCache* http_cache,
+ const GURL& url)
+ : resource_context(resource_context),
+ http_cache(http_cache),
+ url_key(net::HttpUtil::SpecForRequest(url)) {}
+
+AsyncRevalidationKey::AsyncRevalidationKey(
+ const ResourceContext* resource_context)
+ : resource_context(resource_context), http_cache(nullptr), url_key() {}
+
+AsyncRevalidationKey::AsyncRevalidationKey(const AsyncRevalidationKey& rhs)
+ : resource_context(rhs.resource_context),
+ http_cache(rhs.http_cache),
+ url_key(rhs.url_key) {}
+
+AsyncRevalidationKey::~AsyncRevalidationKey() {}
+
+// The use of base::Unretained() in the initialisation of read_timer_ is safe
+// because base::Timer guarantees not to call the callback after being
+// destroyed.
+AsyncRevalidationDriver::AsyncRevalidationDriver(
+ scoped_ptr<net::URLRequest> request,
+ scoped_ptr<ResourceThrottle> throttle,
+ const base::Closure& completion_callback)
+ : read_timer_(FROM_HERE,
+ TimeDelta::FromSeconds(kReadTimeoutSeconds),
+ base::Bind(&AsyncRevalidationDriver::OnReadTimeout,
+ base::Unretained(this)),
+ false),
+ request_(request.Pass()),
+ throttle_(throttle.Pass()),
+ completion_callback_(completion_callback),
+ weak_ptr_factory_(this) {
+ request_->set_delegate(this);
+ throttle_->set_controller(this);
+}
+
+AsyncRevalidationDriver::~AsyncRevalidationDriver() {
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ // Run ResourceThrottle destructor before we tear-down the rest of our state
+ // as the ResourceThrottle may want to inspect the URLRequest and other state.
+ throttle_.reset();
+}
+
+void AsyncRevalidationDriver::StartRequest() {
+ RecordAction(base::UserMetricsAction("AsyncRevalidationCreated"));
+ // Give the handler a chance to delay the URLRequest from being started.
+ bool defer_start = false;
+ throttle_->WillStartRequest(&defer_start);
+
+ if (defer_start) {
+ RecordDefer(DEFERRED_START);
+ } else {
+ StartRequestInternal();
+ }
+}
+
+void AsyncRevalidationDriver::CancelRequest() {
+ CancelRequestInternal(net::ERR_ABORTED);
+}
+
+void AsyncRevalidationDriver::OnReceivedRedirect(
+ net::URLRequest* unused,
+ const net::RedirectInfo& redirect_info,
+ bool* defer) {
+ DCHECK_EQ(request_.get(), unused);
+
+ DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec();
+ RecordAction(base::UserMetricsAction("AsyncRevalidationRedirected"));
+ CancelRequest();
+}
+
+void AsyncRevalidationDriver::OnAuthRequired(
+ net::URLRequest* unused,
+ net::AuthChallengeInfo* auth_info) {
+ DCHECK_EQ(request_.get(), unused);
+ request_->CancelAuth();
davidben 2015/10/08 21:57:51 This needs to cancel the request; CancelAuth is to
Adam Rice 2015/10/13 22:53:16 Done.
+}
+
+void AsyncRevalidationDriver::OnCertificateRequested(
+ net::URLRequest* unused,
+ net::SSLCertRequestInfo* cert_info) {
+ DCHECK_EQ(request_.get(), unused);
+ CancelRequestInternal(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
davidben 2015/10/08 21:57:51 The default implementation already cancels.
Adam Rice 2015/10/13 22:53:16 Removed.
+}
+
+void AsyncRevalidationDriver::OnSSLCertificateError(
+ net::URLRequest* unused,
+ const net::SSLInfo& ssl_info,
+ bool fatal) {
+ DCHECK_EQ(request_.get(), unused);
+ CancelRequestInternal(net::MapCertStatusToNetError(ssl_info.cert_status));
davidben 2015/10/08 21:57:51 The default implementation already cancels.
Adam Rice 2015/10/13 22:53:16 Removed.
+}
+
+void AsyncRevalidationDriver::OnBeforeNetworkStart(net::URLRequest* unused,
+ bool* defer) {
+ DCHECK_EQ(request_.get(), unused);
+
+ // Give the ResourceThrottle a chance to delay the URLRequest from using the
+ // network.
+ throttle_->WillStartUsingNetwork(defer);
+ if (*defer)
+ RecordDefer(DEFERRED_NETWORK_START);
+}
+
+void AsyncRevalidationDriver::OnResponseStarted(net::URLRequest* unused) {
+ DCHECK_EQ(request_.get(), unused);
+
+ DVLOG(1) << "OnResponseStarted: " << request_->url().spec();
+
+ if (!request_->status().is_success()) {
+ DCHECK(!has_already_failed_);
+ has_already_failed_ = true;
+ ResponseCompleted();
+ return;
+ }
+
+ bool defer = false;
+ throttle_->WillProcessResponse(&defer);
+ if (defer) {
+ RecordDefer(DEFERRED_READ); // Read first chunk when resumed.
+ return;
+ }
+
+ if (request_->status().is_success()) {
+ StartReading(false); // Read the first chunk.
+ } else {
+ DCHECK(!has_already_failed_);
+ has_already_failed_ = true;
+ ResponseCompleted();
+ }
+}
+
+void AsyncRevalidationDriver::OnReadCompleted(net::URLRequest* unused,
+ int bytes_read) {
+ DCHECK_EQ(request_.get(), unused);
+ DVLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\""
+ << " bytes_read = " << bytes_read;
+
+ // bytes_read == -1 always implies an error.
+ if (bytes_read == -1 || !request_->status().is_success()) {
+ if (!has_already_failed_) {
+ has_already_failed_ = true;
+ ResponseCompleted();
+ }
+ return;
+ }
+
+ DCHECK_GE(bytes_read, 0);
+ DCHECK(request_->status().is_success());
+ DCHECK(!is_deferred());
+
+ if (bytes_read > 0) {
+ StartReading(true); // Read the next chunk.
+ } else {
+ // URLRequest reported an EOF. Call ResponseCompleted.
+ DCHECK_EQ(0, bytes_read);
+ ResponseCompleted();
+ }
+}
+
+void AsyncRevalidationDriver::Resume() {
+ DeferredStage stage = deferred_stage_;
+ deferred_stage_ = DEFERRED_NONE;
+ switch (stage) {
+ case DEFERRED_NONE:
+ NOTREACHED();
+ break;
+ case DEFERRED_START:
+ StartRequestInternal();
+ break;
+ case DEFERRED_NETWORK_START:
+ request_->ResumeNetworkStart();
+ break;
+ case DEFERRED_READ:
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&AsyncRevalidationDriver::ResumeReading,
+ weak_ptr_factory_.GetWeakPtr()));
+ break;
+ }
+}
+
+void AsyncRevalidationDriver::Cancel() {
+ NOTREACHED();
+}
+
+void AsyncRevalidationDriver::CancelAndIgnore() {
+ NOTREACHED();
+}
+
+void AsyncRevalidationDriver::CancelWithError(int error_code) {
+ NOTREACHED();
+}
+
+void AsyncRevalidationDriver::StartRequestInternal() {
+ DCHECK(!request_->is_pending());
+
+ // TODO(ricea): This seems like it will never call ResponseCompleted().
+ if (!request_->status().is_success())
+ return;
davidben 2015/10/08 21:57:51 This isn't possible, unless you implement Cancel,
Adam Rice 2015/10/13 22:53:16 Thanks. Removed.
Adam Rice 2015/10/22 18:16:12 This turned out to be possible with the sequence o
+
+ request_->Start();
+}
+
+void AsyncRevalidationDriver::CancelRequestInternal(int error) {
+ DVLOG(1) << "CancelRequestInternal: " << request_->url().spec();
+
+ bool was_pending = request_->is_pending();
+
+ request_->CancelWithError(error);
+
+ if (!was_pending) {
+ // If the request isn't in flight, then we won't get an asynchronous
+ // notification from the request, so we have to signal ourselves to finish
+ // this request.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&AsyncRevalidationDriver::ResponseCompleted,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+void AsyncRevalidationDriver::CompleteResponseStarted() {
+ bool defer = false;
+ throttle_->WillProcessResponse(&defer);
+ if (defer)
+ RecordDefer(DEFERRED_READ); // Read first chunk when resumed.
+}
+
+void AsyncRevalidationDriver::StartReading(bool is_continuation) {
+ int bytes_read = 0;
+ ReadMore(&bytes_read);
+
+ // If IO is pending, wait for the URLRequest to call OnReadCompleted.
+ if (request_->status().is_io_pending())
+ return;
+
+ if (!is_continuation || bytes_read <= 0) {
+ OnReadCompleted(request_.get(), bytes_read);
+ } else {
+ // Else, trigger OnReadCompleted asynchronously to avoid starving the IO
+ // thread in case the URLRequest can provide data synchronously.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&AsyncRevalidationDriver::OnReadCompleted,
+ weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read));
+ }
+}
+
+void AsyncRevalidationDriver::ResumeReading() {
+ DCHECK(!is_deferred());
+
+ if (request_->status().is_success()) {
+ StartReading(false); // Read the next chunk (OK to complete synchronously).
+ } else {
+ ResponseCompleted();
+ }
+}
+
+void AsyncRevalidationDriver::ReadMore(int* bytes_read) {
+ DCHECK(!is_deferred());
+
+ if (!read_buffer_)
+ read_buffer_ = new net::IOBuffer(kReadBufSize);
+
+ read_timer_.Reset();
+ request_->Read(read_buffer_.get(), kReadBufSize, bytes_read);
+
+ // No need to check the return value here as we'll detect errors by
+ // inspecting the URLRequest's status.
+}
+
+void AsyncRevalidationDriver::ResponseCompleted() {
+ DVLOG(1) << "ResponseCompleted: " << request_->url().spec();
+ completion_callback_.Run();
+}
+
+void AsyncRevalidationDriver::OnReadTimeout() {
+ RecordAction(base::UserMetricsAction("AsyncRevalidationTimeout"));
+ CancelRequestInternal(net::ERR_TIMED_OUT);
+}
+
+void AsyncRevalidationDriver::RecordDefer(DeferredStage deferred_stage) {
+ request_->LogBlockedBy(throttle_->GetNameForLogging());
+ DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
+ deferred_stage_ = deferred_stage;
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698