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

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: AsyncRevalidationDriver test reorganisation. Comment nits. 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 AsyncRevalidationDriver::AsyncRevalidationDriver(
40 scoped_ptr<net::URLRequest> request,
41 scoped_ptr<ResourceThrottle> throttle,
42 const base::Closure& completion_callback)
43 : request_(std::move(request)),
44 throttle_(std::move(throttle)),
45 completion_callback_(completion_callback),
46 weak_ptr_factory_(this) {
47 request_->set_delegate(this);
48 throttle_->set_controller(this);
49 }
50
51 AsyncRevalidationDriver::~AsyncRevalidationDriver() {}
52
53 void AsyncRevalidationDriver::StartRequest() {
54 RecordAction(base::UserMetricsAction("AsyncRevalidationCreated"));
55 // Give the handler a chance to delay the URLRequest from being started.
56 bool defer_start = false;
57 throttle_->WillStartRequest(&defer_start);
58
59 if (defer_start) {
60 RecordDefer();
61 } else {
62 StartRequestInternal();
63 }
64 }
65
66 void AsyncRevalidationDriver::CancelRequest() {
67 CancelRequestInternal(net::ERR_ABORTED);
68 }
69
70 void AsyncRevalidationDriver::OnReceivedRedirect(
71 net::URLRequest* request,
72 const net::RedirectInfo& redirect_info,
73 bool* defer) {
74 DCHECK_EQ(request_.get(), request);
75
76 // The async revalidation should not follow redirects, because caching is
77 // a property of an individual HTTP resource.
78 DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec();
79 RecordAction(base::UserMetricsAction("AsyncRevalidationRedirected"));
80 CancelRequest();
81 }
82
83 void AsyncRevalidationDriver::OnAuthRequired(
84 net::URLRequest* request,
85 net::AuthChallengeInfo* auth_info) {
86 DCHECK_EQ(request_.get(), request);
87 // This error code doesn't have exactly the right semantics, but it should
88 // be sufficient to narrow down the problem in net logs.
89 request_->CancelWithError(net::ERR_ACCESS_DENIED);
90 }
91
92 void AsyncRevalidationDriver::OnBeforeNetworkStart(net::URLRequest* request,
93 bool* defer) {
94 DCHECK_EQ(request_.get(), request);
95
96 // Verify that the ResourceScheduler does not defer here.
97 throttle_->WillStartUsingNetwork(defer);
98 DCHECK(!*defer);
99
100 // Start the response timer. This use of base::Unretained() is guaranteed safe
101 // by the semantics of base::OneShotTimer.
102 timer_.Start(FROM_HERE,
103 base::TimeDelta::FromSeconds(kResponseTimeoutInSeconds),
104 base::Bind(&AsyncRevalidationDriver::OnTimeout,
105 base::Unretained(this), RESPONSE_TIMEOUT));
106 }
107
108 void AsyncRevalidationDriver::OnResponseStarted(net::URLRequest* request) {
109 DCHECK_EQ(request_.get(), request);
110
111 DVLOG(1) << "OnResponseStarted: " << request_->url().spec();
112
113 // We have the response. No need to wait any longer.
114 timer_.Stop();
115
116 if (!request_->status().is_success()) {
117 ResponseCompleted();
118 return;
119 }
120
121 const net::HttpResponseInfo& response_info = request_->response_info();
122 if (!response_info.response_time.is_null() && response_info.was_cached) {
123 // The cached entry was revalidated. No need to read it in.
124 ResponseCompleted();
125 return;
126 }
127
128 bool defer = false;
129 throttle_->WillProcessResponse(&defer);
130 DCHECK(!defer);
131
132 // Set up the timer for reading the body. This use of base::Unretained() is
133 // guaranteed safe by the semantics of base::OneShotTimer.
134 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kReadTimeoutInSeconds),
135 base::Bind(&AsyncRevalidationDriver::OnTimeout,
136 base::Unretained(this), READ_TIMEOUT));
137 StartReading(false); // Read the first chunk.
138 }
139
140 void AsyncRevalidationDriver::OnReadCompleted(net::URLRequest* request,
141 int bytes_read) {
142 DCHECK_EQ(request_.get(), request);
143 DCHECK(!is_deferred_);
144 DVLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\""
145 << " bytes_read = " << bytes_read;
146
147 // bytes_read == -1 is an error.
148 // bytes_read == 0 is EOF.
149 if (bytes_read == -1 || bytes_read == 0 || !request_->status().is_success()) {
150 ResponseCompleted();
151 return;
152 }
153
154 DCHECK_GT(bytes_read, 0);
155 StartReading(true); // Read the next chunk.
156 }
157
158 void AsyncRevalidationDriver::Resume() {
159 DCHECK(is_deferred_);
160 is_deferred_ = false;
161 request_->LogUnblocked();
162 StartRequestInternal();
163 }
164
165 void AsyncRevalidationDriver::Cancel() {
166 NOTREACHED();
167 }
168
169 void AsyncRevalidationDriver::CancelAndIgnore() {
170 NOTREACHED();
171 }
172
173 void AsyncRevalidationDriver::CancelWithError(int error_code) {
174 NOTREACHED();
175 }
176
177 void AsyncRevalidationDriver::StartRequestInternal() {
178 DCHECK(!request_->is_pending());
179
180 // This can happen if Resume() is called after CancelRequest().
181 // Since CancelRequest() will have called ResponseCompleted() asynchronously,
182 // it's not necessary to call it again.
183 if (!request_->status().is_success())
184 return;
185
186 request_->Start();
187 }
188
189 void AsyncRevalidationDriver::CancelRequestInternal(int error) {
190 DVLOG(1) << "CancelRequestInternal: " << request_->url().spec();
191
192 bool was_pending = request_->is_pending();
193
194 request_->CancelWithError(error);
195
196 if (!was_pending) {
197 // If the request isn't in flight, then we won't get an asynchronous
198 // notification from the request, so we have to signal ourselves to finish
199 // this request.
200 scoped_refptr<base::SingleThreadTaskRunner> single_thread_task_runner =
201 base::ThreadTaskRunnerHandle::Get();
davidben 2015/12/07 23:56:03 Nit: Why the change? The old one seems shorter. (b
Adam Rice 2015/12/08 18:05:35 I got tired of people asking me why I need the sin
202 single_thread_task_runner->PostTask(
203 FROM_HERE, base::Bind(&AsyncRevalidationDriver::ResponseCompleted,
204 weak_ptr_factory_.GetWeakPtr()));
205 }
206 }
207
208 void AsyncRevalidationDriver::StartReading(bool is_continuation) {
209 int bytes_read = 0;
210 ReadMore(&bytes_read);
211
212 // If IO is pending, wait for the URLRequest to call OnReadCompleted.
213 if (request_->status().is_io_pending())
214 return;
215
216 if (!is_continuation || bytes_read <= 0) {
217 OnReadCompleted(request_.get(), bytes_read);
218 } else {
219 // Else, trigger OnReadCompleted asynchronously to avoid starving the IO
220 // thread in case the URLRequest can provide data synchronously.
221 scoped_refptr<base::SingleThreadTaskRunner> single_thread_task_runner =
222 base::ThreadTaskRunnerHandle::Get();
davidben 2015/12/07 23:56:03 Ditto.
223 single_thread_task_runner->PostTask(
224 FROM_HERE,
225 base::Bind(&AsyncRevalidationDriver::OnReadCompleted,
226 weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read));
227 }
228 }
229
230 void AsyncRevalidationDriver::ReadMore(int* bytes_read) {
231 DCHECK(!is_deferred_);
232
233 if (!read_buffer_)
234 read_buffer_ = new net::IOBuffer(kReadBufSize);
235
236 timer_.Reset();
237 request_->Read(read_buffer_.get(), kReadBufSize, bytes_read);
238
239 // No need to check the return value here as we'll detect errors by
240 // inspecting the URLRequest's status.
241 }
242
243 void AsyncRevalidationDriver::ResponseCompleted() {
244 DVLOG(1) << "ResponseCompleted: " << request_->url().spec();
245 DCHECK(!completion_callback_.is_null());
246 base::ResetAndReturn(&completion_callback_).Run();
247 // |this| may be deleted after this point.
248 }
249
250 void AsyncRevalidationDriver::OnTimeout(TimeoutType type) {
davidben 2015/12/07 23:56:03 Since you allow for the object to be in a canceled
Adam Rice 2015/12/08 18:05:35 The object can still be in a zombie state if it ca
251 // UserMetricsAction must be passed a literal string otherwise
252 // //tools/metrics/actions/extract_actions.py won't understand it.
253 switch (type) {
254 case RESPONSE_TIMEOUT:
255 RecordAction(base::UserMetricsAction("AsyncRevalidationResponseTimeout"));
256 break;
257
258 case READ_TIMEOUT:
259 RecordAction(base::UserMetricsAction("AsyncRevalidationReadTimeout"));
260 break;
261 }
262 CancelRequestInternal(net::ERR_TIMED_OUT);
263 }
264
265 void AsyncRevalidationDriver::RecordDefer() {
266 request_->LogBlockedBy(throttle_->GetNameForLogging());
267 DCHECK(!is_deferred_);
268 is_deferred_ = true;
269 }
270
271 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698