OLD | NEW |
---|---|
(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 "base/location.h" | |
8 #include "base/logging.h" | |
9 #include "base/metrics/user_metrics_action.h" | |
10 #include "base/single_thread_task_runner.h" | |
11 #include "base/thread_task_runner_handle.h" | |
12 #include "base/time/time.h" | |
13 #include "content/public/browser/user_metrics.h" | |
14 #include "net/base/net_errors.h" | |
15 #include "net/cert/cert_status_flags.h" | |
16 #include "net/http/http_transaction_factory.h" | |
17 #include "net/http/http_util.h" | |
18 #include "net/ssl/ssl_info.h" | |
19 #include "net/url_request/url_request.h" | |
20 #include "net/url_request/url_request_context.h" | |
21 #include "net/url_request/url_request_status.h" | |
22 | |
23 using base::TimeDelta; | |
24 using base::TimeTicks; | |
25 | |
26 namespace content { | |
27 | |
28 namespace { | |
29 // This matches the maximum allocation size of AsyncResourceHandler. | |
30 const int kReadBufSize = 32 * 1024; | |
31 const int kReadTimeoutSeconds = 5 * 60; | |
32 } | |
33 | |
34 AsyncRevalidationKey::AsyncRevalidationKey( | |
35 const ResourceContext* resource_context, | |
36 const net::HttpCache* http_cache, | |
37 const GURL& url) | |
38 : resource_context(resource_context), | |
39 http_cache(http_cache), | |
40 url_key(net::HttpUtil::SpecForRequest(url)) {} | |
41 | |
42 AsyncRevalidationKey::AsyncRevalidationKey( | |
43 const ResourceContext* resource_context) | |
44 : resource_context(resource_context), http_cache(nullptr), url_key() {} | |
45 | |
46 AsyncRevalidationKey::AsyncRevalidationKey(const AsyncRevalidationKey& rhs) | |
47 : resource_context(rhs.resource_context), | |
48 http_cache(rhs.http_cache), | |
49 url_key(rhs.url_key) {} | |
50 | |
51 AsyncRevalidationKey::~AsyncRevalidationKey() {} | |
52 | |
53 // The use of base::Unretained() in the initialisation of read_timer_ is safe | |
54 // because base::Timer guarantees not to call the callback after being | |
55 // destroyed. | |
56 AsyncRevalidationDriver::AsyncRevalidationDriver( | |
57 scoped_ptr<net::URLRequest> request, | |
58 scoped_ptr<ResourceThrottle> throttle, | |
59 const base::Closure& completion_callback) | |
60 : read_timer_(FROM_HERE, | |
61 TimeDelta::FromSeconds(kReadTimeoutSeconds), | |
62 base::Bind(&AsyncRevalidationDriver::OnReadTimeout, | |
63 base::Unretained(this)), | |
64 false), | |
65 request_(request.Pass()), | |
66 throttle_(throttle.Pass()), | |
67 completion_callback_(completion_callback), | |
68 weak_ptr_factory_(this) { | |
69 request_->set_delegate(this); | |
70 throttle_->set_controller(this); | |
71 } | |
72 | |
73 AsyncRevalidationDriver::~AsyncRevalidationDriver() { | |
74 weak_ptr_factory_.InvalidateWeakPtrs(); | |
75 // Run ResourceThrottle destructor before we tear-down the rest of our state | |
76 // as the ResourceThrottle may want to inspect the URLRequest and other state. | |
77 throttle_.reset(); | |
78 } | |
79 | |
80 void AsyncRevalidationDriver::StartRequest() { | |
81 RecordAction(base::UserMetricsAction("AsyncRevalidationCreated")); | |
82 // Give the handler a chance to delay the URLRequest from being started. | |
83 bool defer_start = false; | |
84 throttle_->WillStartRequest(&defer_start); | |
85 | |
86 if (defer_start) { | |
87 RecordDefer(DEFERRED_START); | |
88 } else { | |
89 StartRequestInternal(); | |
90 } | |
91 } | |
92 | |
93 void AsyncRevalidationDriver::CancelRequest() { | |
94 CancelRequestInternal(net::ERR_ABORTED); | |
95 } | |
96 | |
97 void AsyncRevalidationDriver::OnReceivedRedirect( | |
98 net::URLRequest* unused, | |
99 const net::RedirectInfo& redirect_info, | |
100 bool* defer) { | |
101 DCHECK_EQ(request_.get(), unused); | |
102 | |
103 DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec(); | |
104 RecordAction(base::UserMetricsAction("AsyncRevalidationRedirected")); | |
105 CancelRequest(); | |
106 } | |
107 | |
108 void AsyncRevalidationDriver::OnAuthRequired( | |
109 net::URLRequest* unused, | |
110 net::AuthChallengeInfo* auth_info) { | |
111 DCHECK_EQ(request_.get(), unused); | |
112 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.
| |
113 } | |
114 | |
115 void AsyncRevalidationDriver::OnCertificateRequested( | |
116 net::URLRequest* unused, | |
117 net::SSLCertRequestInfo* cert_info) { | |
118 DCHECK_EQ(request_.get(), unused); | |
119 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.
| |
120 } | |
121 | |
122 void AsyncRevalidationDriver::OnSSLCertificateError( | |
123 net::URLRequest* unused, | |
124 const net::SSLInfo& ssl_info, | |
125 bool fatal) { | |
126 DCHECK_EQ(request_.get(), unused); | |
127 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.
| |
128 } | |
129 | |
130 void AsyncRevalidationDriver::OnBeforeNetworkStart(net::URLRequest* unused, | |
131 bool* defer) { | |
132 DCHECK_EQ(request_.get(), unused); | |
133 | |
134 // Give the ResourceThrottle a chance to delay the URLRequest from using the | |
135 // network. | |
136 throttle_->WillStartUsingNetwork(defer); | |
137 if (*defer) | |
138 RecordDefer(DEFERRED_NETWORK_START); | |
139 } | |
140 | |
141 void AsyncRevalidationDriver::OnResponseStarted(net::URLRequest* unused) { | |
142 DCHECK_EQ(request_.get(), unused); | |
143 | |
144 DVLOG(1) << "OnResponseStarted: " << request_->url().spec(); | |
145 | |
146 if (!request_->status().is_success()) { | |
147 DCHECK(!has_already_failed_); | |
148 has_already_failed_ = true; | |
149 ResponseCompleted(); | |
150 return; | |
151 } | |
152 | |
153 bool defer = false; | |
154 throttle_->WillProcessResponse(&defer); | |
155 if (defer) { | |
156 RecordDefer(DEFERRED_READ); // Read first chunk when resumed. | |
157 return; | |
158 } | |
159 | |
160 if (request_->status().is_success()) { | |
161 StartReading(false); // Read the first chunk. | |
162 } else { | |
163 DCHECK(!has_already_failed_); | |
164 has_already_failed_ = true; | |
165 ResponseCompleted(); | |
166 } | |
167 } | |
168 | |
169 void AsyncRevalidationDriver::OnReadCompleted(net::URLRequest* unused, | |
170 int bytes_read) { | |
171 DCHECK_EQ(request_.get(), unused); | |
172 DVLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\"" | |
173 << " bytes_read = " << bytes_read; | |
174 | |
175 // bytes_read == -1 always implies an error. | |
176 if (bytes_read == -1 || !request_->status().is_success()) { | |
177 if (!has_already_failed_) { | |
178 has_already_failed_ = true; | |
179 ResponseCompleted(); | |
180 } | |
181 return; | |
182 } | |
183 | |
184 DCHECK_GE(bytes_read, 0); | |
185 DCHECK(request_->status().is_success()); | |
186 DCHECK(!is_deferred()); | |
187 | |
188 if (bytes_read > 0) { | |
189 StartReading(true); // Read the next chunk. | |
190 } else { | |
191 // URLRequest reported an EOF. Call ResponseCompleted. | |
192 DCHECK_EQ(0, bytes_read); | |
193 ResponseCompleted(); | |
194 } | |
195 } | |
196 | |
197 void AsyncRevalidationDriver::Resume() { | |
198 DeferredStage stage = deferred_stage_; | |
199 deferred_stage_ = DEFERRED_NONE; | |
200 switch (stage) { | |
201 case DEFERRED_NONE: | |
202 NOTREACHED(); | |
203 break; | |
204 case DEFERRED_START: | |
205 StartRequestInternal(); | |
206 break; | |
207 case DEFERRED_NETWORK_START: | |
208 request_->ResumeNetworkStart(); | |
209 break; | |
210 case DEFERRED_READ: | |
211 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
212 FROM_HERE, base::Bind(&AsyncRevalidationDriver::ResumeReading, | |
213 weak_ptr_factory_.GetWeakPtr())); | |
214 break; | |
215 } | |
216 } | |
217 | |
218 void AsyncRevalidationDriver::Cancel() { | |
219 NOTREACHED(); | |
220 } | |
221 | |
222 void AsyncRevalidationDriver::CancelAndIgnore() { | |
223 NOTREACHED(); | |
224 } | |
225 | |
226 void AsyncRevalidationDriver::CancelWithError(int error_code) { | |
227 NOTREACHED(); | |
228 } | |
229 | |
230 void AsyncRevalidationDriver::StartRequestInternal() { | |
231 DCHECK(!request_->is_pending()); | |
232 | |
233 // TODO(ricea): This seems like it will never call ResponseCompleted(). | |
234 if (!request_->status().is_success()) | |
235 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
| |
236 | |
237 request_->Start(); | |
238 } | |
239 | |
240 void AsyncRevalidationDriver::CancelRequestInternal(int error) { | |
241 DVLOG(1) << "CancelRequestInternal: " << request_->url().spec(); | |
242 | |
243 bool was_pending = request_->is_pending(); | |
244 | |
245 request_->CancelWithError(error); | |
246 | |
247 if (!was_pending) { | |
248 // If the request isn't in flight, then we won't get an asynchronous | |
249 // notification from the request, so we have to signal ourselves to finish | |
250 // this request. | |
251 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
252 FROM_HERE, base::Bind(&AsyncRevalidationDriver::ResponseCompleted, | |
253 weak_ptr_factory_.GetWeakPtr())); | |
254 } | |
255 } | |
256 | |
257 void AsyncRevalidationDriver::CompleteResponseStarted() { | |
258 bool defer = false; | |
259 throttle_->WillProcessResponse(&defer); | |
260 if (defer) | |
261 RecordDefer(DEFERRED_READ); // Read first chunk when resumed. | |
262 } | |
263 | |
264 void AsyncRevalidationDriver::StartReading(bool is_continuation) { | |
265 int bytes_read = 0; | |
266 ReadMore(&bytes_read); | |
267 | |
268 // If IO is pending, wait for the URLRequest to call OnReadCompleted. | |
269 if (request_->status().is_io_pending()) | |
270 return; | |
271 | |
272 if (!is_continuation || bytes_read <= 0) { | |
273 OnReadCompleted(request_.get(), bytes_read); | |
274 } else { | |
275 // Else, trigger OnReadCompleted asynchronously to avoid starving the IO | |
276 // thread in case the URLRequest can provide data synchronously. | |
277 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
278 FROM_HERE, | |
279 base::Bind(&AsyncRevalidationDriver::OnReadCompleted, | |
280 weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read)); | |
281 } | |
282 } | |
283 | |
284 void AsyncRevalidationDriver::ResumeReading() { | |
285 DCHECK(!is_deferred()); | |
286 | |
287 if (request_->status().is_success()) { | |
288 StartReading(false); // Read the next chunk (OK to complete synchronously). | |
289 } else { | |
290 ResponseCompleted(); | |
291 } | |
292 } | |
293 | |
294 void AsyncRevalidationDriver::ReadMore(int* bytes_read) { | |
295 DCHECK(!is_deferred()); | |
296 | |
297 if (!read_buffer_) | |
298 read_buffer_ = new net::IOBuffer(kReadBufSize); | |
299 | |
300 read_timer_.Reset(); | |
301 request_->Read(read_buffer_.get(), kReadBufSize, bytes_read); | |
302 | |
303 // No need to check the return value here as we'll detect errors by | |
304 // inspecting the URLRequest's status. | |
305 } | |
306 | |
307 void AsyncRevalidationDriver::ResponseCompleted() { | |
308 DVLOG(1) << "ResponseCompleted: " << request_->url().spec(); | |
309 completion_callback_.Run(); | |
310 } | |
311 | |
312 void AsyncRevalidationDriver::OnReadTimeout() { | |
313 RecordAction(base::UserMetricsAction("AsyncRevalidationTimeout")); | |
314 CancelRequestInternal(net::ERR_TIMED_OUT); | |
315 } | |
316 | |
317 void AsyncRevalidationDriver::RecordDefer(DeferredStage deferred_stage) { | |
318 request_->LogBlockedBy(throttle_->GetNameForLogging()); | |
319 DCHECK_EQ(DEFERRED_NONE, deferred_stage_); | |
320 deferred_stage_ = deferred_stage; | |
321 } | |
322 | |
323 } // namespace content | |
OLD | NEW |