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

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: Use histograms instead of user actions. Created 5 years 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..3c5d45bc13b05f206630683d0a5bf564ac9bd658
--- /dev/null
+++ b/content/browser/loader/async_revalidation_driver.cc
@@ -0,0 +1,274 @@
+// 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 <utility>
+
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request_status.h"
+
+namespace content {
+
+namespace {
+// This matches the maximum allocation size of AsyncResourceHandler.
+const int kReadBufSize = 32 * 1024;
+
+// The time to wait for a response. Since this includes the time taken to
+// connect, this has been set to match the connect timeout
+// kTransportConnectJobTimeoutInSeconds.
+const int kResponseTimeoutInSeconds = 240; // 4 minutes.
+
+// This value should not be too large, as this request may be tying up a socket
+// that could be used for something better. However, if it is too small, the
+// cache entry will be truncated for no good reason.
+// TODO(ricea): Find a more scientific way to set this timeout.
+const int kReadTimeoutInSeconds = 30;
+} // namespace
+
+AsyncRevalidationDriver::AsyncRevalidationDriver(
+ scoped_ptr<net::URLRequest> request,
+ scoped_ptr<ResourceThrottle> throttle,
+ const base::Closure& completion_callback)
+ : request_(std::move(request)),
+ throttle_(std::move(throttle)),
+ completion_callback_(completion_callback),
+ weak_ptr_factory_(this) {
+ request_->set_delegate(this);
+ throttle_->set_controller(this);
+}
+
+AsyncRevalidationDriver::~AsyncRevalidationDriver() {}
+
+void AsyncRevalidationDriver::StartRequest() {
+ // Give the handler a chance to delay the URLRequest from being started.
+ bool defer_start = false;
+ throttle_->WillStartRequest(&defer_start);
+
+ if (defer_start) {
+ RecordDefer();
+ } else {
+ StartRequestInternal();
+ }
+}
+
+void AsyncRevalidationDriver::OnReceivedRedirect(
+ net::URLRequest* request,
+ const net::RedirectInfo& redirect_info,
+ bool* defer) {
+ DCHECK_EQ(request_.get(), request);
+
+ // The async revalidation should not follow redirects, because caching is
+ // a property of an individual HTTP resource.
+ DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec();
+ CancelRequestInternal(net::ERR_ABORTED, RESULT_GOT_REDIRECT);
+}
+
+void AsyncRevalidationDriver::OnAuthRequired(
+ net::URLRequest* request,
+ net::AuthChallengeInfo* auth_info) {
+ DCHECK_EQ(request_.get(), request);
+ // This error code doesn't have exactly the right semantics, but it should
+ // be sufficient to narrow down the problem in net logs.
+ CancelRequestInternal(net::ERR_ACCESS_DENIED, RESULT_AUTH_FAILED);
+}
+
+void AsyncRevalidationDriver::OnBeforeNetworkStart(net::URLRequest* request,
+ bool* defer) {
+ DCHECK_EQ(request_.get(), request);
+
+ // Verify that the ResourceScheduler does not defer here.
+ throttle_->WillStartUsingNetwork(defer);
+ DCHECK(!*defer);
+
+ // Start the response timer. This use of base::Unretained() is guaranteed safe
+ // by the semantics of base::OneShotTimer.
+ timer_.Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(kResponseTimeoutInSeconds),
+ base::Bind(&AsyncRevalidationDriver::OnTimeout,
+ base::Unretained(this), RESULT_RESPONSE_TIMEOUT));
+}
+
+void AsyncRevalidationDriver::OnResponseStarted(net::URLRequest* request) {
+ DCHECK_EQ(request_.get(), request);
+
+ DVLOG(1) << "OnResponseStarted: " << request_->url().spec();
+
+ // We have the response. No need to wait any longer.
+ timer_.Stop();
+
+ if (!request_->status().is_success()) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.AsyncRevalidation.ResponseError",
+ -request_->status().ToNetError());
+ ResponseCompleted(RESULT_NET_ERROR);
+ // |this| may be deleted after this point.
+ return;
+ }
+
+ const net::HttpResponseInfo& response_info = request_->response_info();
+ if (!response_info.response_time.is_null() && response_info.was_cached) {
+ // The cached entry was revalidated. No need to read it in.
+ ResponseCompleted(RESULT_REVALIDATED);
+ // |this| may be deleted after this point.
+ return;
+ }
+
+ bool defer = false;
+ throttle_->WillProcessResponse(&defer);
+ DCHECK(!defer);
+
+ // Set up the timer for reading the body. This use of base::Unretained() is
+ // guaranteed safe by the semantics of base::OneShotTimer.
+ timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kReadTimeoutInSeconds),
+ base::Bind(&AsyncRevalidationDriver::OnTimeout,
+ base::Unretained(this), RESULT_BODY_TIMEOUT));
+ StartReading(false); // Read the first chunk.
+}
+
+void AsyncRevalidationDriver::OnReadCompleted(net::URLRequest* request,
+ int bytes_read) {
+ // request_ could be NULL if a timeout happened while OnReadCompleted() was
+ // queued to run asynchronously.
+ if (!request_)
+ return;
+ DCHECK_EQ(request_.get(), request);
+ DCHECK(!is_deferred_);
+ DVLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\""
+ << " bytes_read = " << bytes_read;
+
+ // bytes_read == 0 is EOF.
+ if (bytes_read == 0) {
+ ResponseCompleted(RESULT_LOADED);
+ return;
+ }
+ // bytes_read == -1 is an error.
+ if (bytes_read == -1 || !request_->status().is_success()) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.AsyncRevalidation.ReadError",
+ -request_->status().ToNetError());
+ ResponseCompleted(RESULT_READ_ERROR);
+ // |this| may be deleted after this point.
+ return;
+ }
+
+ DCHECK_GT(bytes_read, 0);
+ StartReading(true); // Read the next chunk.
+}
+
+void AsyncRevalidationDriver::Resume() {
+ DCHECK(is_deferred_);
+ DCHECK(request_);
+ is_deferred_ = false;
+ request_->LogUnblocked();
+ StartRequestInternal();
+}
+
+void AsyncRevalidationDriver::Cancel() {
+ NOTREACHED();
+}
+
+void AsyncRevalidationDriver::CancelAndIgnore() {
+ NOTREACHED();
+}
+
+void AsyncRevalidationDriver::CancelWithError(int error_code) {
+ NOTREACHED();
+}
+
+void AsyncRevalidationDriver::StartRequestInternal() {
+ DCHECK(request_);
+ DCHECK(!request_->is_pending());
+
+ request_->Start();
+}
+
+void AsyncRevalidationDriver::CancelRequestInternal(
+ int error,
+ AsyncRevalidationResult result) {
+ DVLOG(1) << "CancelRequestInternal: " << request_->url().spec();
+
+ // Set the error code since this will be reported to the NetworkDelegate and
+ // recorded in the NetLog.
+ request_->CancelWithError(error);
+
+ // The ResourceScheduler needs to be able to examine the request when the
+ // ResourceThrottle is destroyed, so delete it first.
+ throttle_.reset();
+
+ // Destroy the request so that it doesn't try to send an asynchronous
+ // notification of completion.
+ request_.reset();
+
+ // Cancel timer to prevent OnTimeout() being called.
+ timer_.Stop();
+
+ ResponseCompleted(result);
+ // |this| may deleted after this point.
+}
+
+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.
+ scoped_refptr<base::SingleThreadTaskRunner> single_thread_task_runner =
+ base::ThreadTaskRunnerHandle::Get();
+ single_thread_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(&AsyncRevalidationDriver::OnReadCompleted,
+ weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read));
+ }
+}
+
+void AsyncRevalidationDriver::ReadMore(int* bytes_read) {
+ DCHECK(!is_deferred_);
+
+ if (!read_buffer_)
+ read_buffer_ = new net::IOBuffer(kReadBufSize);
+
+ 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(
+ AsyncRevalidationResult result) {
+ DVLOG(1) << "ResponseCompleted: "
+ << (request_ ? request_->url().spec() : "(request deleted)")
+ << "result = " << result;
+ UMA_HISTOGRAM_ENUMERATION("Net.AsyncRevalidation.Result", result, RESULT_MAX);
+ DCHECK(!completion_callback_.is_null());
+ base::ResetAndReturn(&completion_callback_).Run();
+ // |this| may be deleted after this point.
+}
+
+void AsyncRevalidationDriver::OnTimeout(AsyncRevalidationResult result) {
+ CancelRequestInternal(net::ERR_TIMED_OUT, result);
+}
+
+void AsyncRevalidationDriver::RecordDefer() {
+ request_->LogBlockedBy(throttle_->GetNameForLogging());
+ DCHECK(!is_deferred_);
+ is_deferred_ = true;
+}
+
+} // namespace content
« no previous file with comments | « content/browser/loader/async_revalidation_driver.h ('k') | content/browser/loader/async_revalidation_driver_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698