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

Side by Side 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: Fixes from davidben review. 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/loader/async_revalidation_driver.h"
6
7 #include <utility>
8
9 #include "base/callback_helpers.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/metrics/user_metrics_action.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/time/time.h"
17 #include "content/public/browser/user_metrics.h"
18 #include "net/base/net_errors.h"
19 #include "net/url_request/url_request_status.h"
20
21 namespace content {
22
23 namespace {
24 // This matches the maximum allocation size of AsyncResourceHandler.
25 const int kReadBufSize = 32 * 1024;
26
27 // The time to wait for a response. Since this includes the time taken to
28 // connect, this has been set to match the connect timeout
29 // kTransportConnectJobTimeoutInSeconds.
30 const int kResponseTimeoutInSeconds = 240; // 4 minutes.
31
32 // This value should not be too large, as this request may be tying up a socket
33 // that could be used for something better. However, if it is too small, the
34 // cache entry will be truncated for no good reason.
35 // TODO(ricea): Find a more scientific way to set this timeout.
36 const int kReadTimeoutInSeconds = 30;
37 } // namespace
38
39 // The use of base::Unretained() in the initialisation of read_timer_ is safe
40 // because base::Timer guarantees not to call the callback after being
41 // destroyed.
kinuko 2015/11/27 08:50:01 nit: this comment seems obsolete now
Adam Rice 2015/11/27 14:45:13 Thanks. Removed.
42 AsyncRevalidationDriver::AsyncRevalidationDriver(
43 scoped_ptr<net::URLRequest> request,
44 scoped_ptr<ResourceThrottle> throttle,
45 const base::Closure& completion_callback)
46 : request_(std::move(request)),
47 throttle_(std::move(throttle)),
48 completion_callback_(completion_callback),
49 weak_ptr_factory_(this) {
50 request_->set_delegate(this);
51 throttle_->set_controller(this);
52 }
53
54 AsyncRevalidationDriver::~AsyncRevalidationDriver() {}
55
56 void AsyncRevalidationDriver::StartRequest() {
57 RecordAction(base::UserMetricsAction("AsyncRevalidationCreated"));
58 // Give the handler a chance to delay the URLRequest from being started.
59 bool defer_start = false;
60 throttle_->WillStartRequest(&defer_start);
61
62 if (defer_start) {
63 RecordDefer();
64 } else {
65 StartRequestInternal();
66 }
67 }
68
69 void AsyncRevalidationDriver::CancelRequest() {
70 CancelRequestInternal(net::ERR_ABORTED);
71 }
72
73 void AsyncRevalidationDriver::OnReceivedRedirect(
74 net::URLRequest* request,
75 const net::RedirectInfo& redirect_info,
76 bool* defer) {
77 DCHECK_EQ(request_.get(), request);
78
79 // The async revalidation should not follow redirects, because caching is
80 // a property of an individual HTTP resource.
81 DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec();
82 RecordAction(base::UserMetricsAction("AsyncRevalidationRedirected"));
83 CancelRequest();
84 }
85
86 void AsyncRevalidationDriver::OnAuthRequired(
87 net::URLRequest* request,
88 net::AuthChallengeInfo* auth_info) {
89 DCHECK_EQ(request_.get(), request);
90 // This error code doesn't have exactly the right semantics, but it should
91 // be sufficient to narrow down the problem in net logs.
92 request_->CancelWithError(net::ERR_ACCESS_DENIED);
93 }
94
95 void AsyncRevalidationDriver::OnBeforeNetworkStart(net::URLRequest* request,
96 bool* defer) {
97 DCHECK_EQ(request_.get(), request);
98
99 // Verify that the ResourceScheduler does not defer here.
100 throttle_->WillStartUsingNetwork(defer);
101 DCHECK(!*defer);
102
103 // Start the response timer. This use of base::Unretained() is guaranteed safe
104 // by the semantics of base::OneShotTimer.
105 timer_.Start(
106 FROM_HERE, base::TimeDelta::FromSeconds(kResponseTimeoutInSeconds),
107 base::Bind(&AsyncRevalidationDriver::OnTimeout, base::Unretained(this),
108 "AsyncRevalidationResponseTimeout"));
109 }
110
111 void AsyncRevalidationDriver::OnResponseStarted(net::URLRequest* request) {
112 DCHECK_EQ(request_.get(), request);
113
114 DVLOG(1) << "OnResponseStarted: " << request_->url().spec();
115
116 // We have the response. No need to wait any longer.
117 timer_.Stop();
118
119 if (!request_->status().is_success()) {
120 ResponseCompleted();
121 return;
122 }
123
124 const net::HttpResponseInfo& response_info = request_->response_info();
125 if (!response_info.response_time.is_null() && response_info.was_cached) {
126 // The cached entry was revalidated. No need to read it in.
127 ResponseCompleted();
128 return;
129 }
130
131 bool defer = false;
132 throttle_->WillProcessResponse(&defer);
133 DCHECK(!defer);
134
135 // Set up the timer for reading the body. This use of base::Unretained() is
136 // guaranteed safe by the semantics of base::OneShotTimer.
137 timer_.Start(
138 FROM_HERE, base::TimeDelta::FromSeconds(kReadTimeoutInSeconds),
139 base::Bind(&AsyncRevalidationDriver::OnTimeout, base::Unretained(this),
140 "AsyncRevalidationReadTimeout"));
141 StartReading(false); // Read the first chunk.
142 }
143
144 void AsyncRevalidationDriver::OnReadCompleted(net::URLRequest* request,
145 int bytes_read) {
146 DCHECK_EQ(request_.get(), request);
147 DCHECK(!is_deferred_);
148 DVLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\""
149 << " bytes_read = " << bytes_read;
150
151 // bytes_read == -1 is an error.
152 // bytes_read == 0 is EOF.
153 if (bytes_read == -1 || bytes_read == 0 || !request_->status().is_success()) {
154 ResponseCompleted();
155 return;
156 }
157
158 DCHECK_GT(bytes_read, 0);
159 StartReading(true); // Read the next chunk.
160 }
161
162 void AsyncRevalidationDriver::Resume() {
163 DCHECK(is_deferred_);
164 is_deferred_ = false;
165 request_->LogUnblocked();
166 StartRequestInternal();
167 }
168
169 void AsyncRevalidationDriver::Cancel() {
170 NOTREACHED();
171 }
172
173 void AsyncRevalidationDriver::CancelAndIgnore() {
174 NOTREACHED();
175 }
176
177 void AsyncRevalidationDriver::CancelWithError(int error_code) {
178 NOTREACHED();
179 }
180
181 void AsyncRevalidationDriver::StartRequestInternal() {
182 DCHECK(!request_->is_pending());
183
184 // This can happen if Resume() is called after CancelRequest().
185 // Since CancelRequest() will have called ResponseCompleted() asynchronously,
186 // it's not necessary to call it again.
187 if (!request_->status().is_success())
188 return;
189
190 request_->Start();
191 }
192
193 void AsyncRevalidationDriver::CancelRequestInternal(int error) {
194 DVLOG(1) << "CancelRequestInternal: " << request_->url().spec();
195
196 bool was_pending = request_->is_pending();
197
198 request_->CancelWithError(error);
199
200 if (!was_pending) {
201 // If the request isn't in flight, then we won't get an asynchronous
202 // notification from the request, so we have to signal ourselves to finish
203 // this request.
204 scoped_refptr<base::SingleThreadTaskRunner> single_thread_task_runner =
205 base::ThreadTaskRunnerHandle::Get();
206 single_thread_task_runner->PostTask(
207 FROM_HERE, base::Bind(&AsyncRevalidationDriver::ResponseCompleted,
208 weak_ptr_factory_.GetWeakPtr()));
209 }
210 }
211
212 void AsyncRevalidationDriver::StartReading(bool is_continuation) {
213 int bytes_read = 0;
214 ReadMore(&bytes_read);
215
216 // If IO is pending, wait for the URLRequest to call OnReadCompleted.
217 if (request_->status().is_io_pending())
218 return;
219
220 if (!is_continuation || bytes_read <= 0) {
221 OnReadCompleted(request_.get(), bytes_read);
222 } else {
223 // Else, trigger OnReadCompleted asynchronously to avoid starving the IO
224 // thread in case the URLRequest can provide data synchronously.
225 scoped_refptr<base::SingleThreadTaskRunner> single_thread_task_runner =
226 base::ThreadTaskRunnerHandle::Get();
227 single_thread_task_runner->PostTask(
228 FROM_HERE,
229 base::Bind(&AsyncRevalidationDriver::OnReadCompleted,
230 weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read));
231 }
232 }
233
234 void AsyncRevalidationDriver::ReadMore(int* bytes_read) {
235 DCHECK(!is_deferred_);
236
237 if (!read_buffer_)
238 read_buffer_ = new net::IOBuffer(kReadBufSize);
239
240 timer_.Reset();
241 request_->Read(read_buffer_.get(), kReadBufSize, bytes_read);
242
243 // No need to check the return value here as we'll detect errors by
244 // inspecting the URLRequest's status.
245 }
246
247 void AsyncRevalidationDriver::ResponseCompleted() {
248 DVLOG(1) << "ResponseCompleted: " << request_->url().spec();
249 // When this class cancels a redirect, URLRequest calls both the
250 // OnResponseStarted() and OnReadCompleted() callbacks. This class should not
251 // run |completion_callback_| twice.
252 //
253 // TODO(ricea): Work out why URLRequest calls both methods on cancellation and
254 // make it stop.
255 if (completion_callback_.is_null())
256 return;
257 base::ResetAndReturn(&completion_callback_).Run();
258 // |this| may be deleted after this point.
259 }
260
261 void AsyncRevalidationDriver::OnTimeout(const char* action_name) {
262 RecordAction(base::UserMetricsAction(action_name));
263 CancelRequestInternal(net::ERR_TIMED_OUT);
264 }
265
266 void AsyncRevalidationDriver::RecordDefer() {
267 request_->LogBlockedBy(throttle_->GetNameForLogging());
268 DCHECK(!is_deferred_);
269 is_deferred_ = true;
270 }
271
272 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698