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

Side by Side Diff: content/browser/renderer_host/resource_loader.cc

Issue 10501004: Refactor the guts of ResourceDispatcherHostImpl into a new class named (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 6 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/renderer_host/resource_loader.h"
6
7 #include "base/message_loop.h"
8 #include "base/time.h"
9 #include "content/browser/child_process_security_policy_impl.h"
10 #include "content/browser/renderer_host/doomed_resource_handler.h"
11 #include "content/browser/renderer_host/resource_loader_delegate.h"
12 #include "content/browser/renderer_host/resource_request_info_impl.h"
13 #include "content/browser/ssl/ssl_client_auth_handler.h"
14 #include "content/browser/ssl/ssl_manager.h"
15 #include "content/common/ssl_status_serialization.h"
16 #include "content/public/browser/cert_store.h"
17 #include "content/public/browser/resource_dispatcher_host_login_delegate.h"
18 #include "content/public/common/resource_response.h"
19 #include "net/base/load_flags.h"
20 #include "net/http/http_response_headers.h"
21 #include "webkit/appcache/appcache_interceptor.h"
22
23 using base::TimeDelta;
24 using base::TimeTicks;
25
26 namespace content {
27 namespace {
28
29 void PopulateResourceResponse(net::URLRequest* request,
30 ResourceResponse* response) {
31 response->status = request->status();
32 response->request_time = request->request_time();
33 response->response_time = request->response_time();
34 response->headers = request->response_headers();
35 request->GetCharset(&response->charset);
36 response->content_length = request->GetExpectedContentSize();
37 request->GetMimeType(&response->mime_type);
38 net::HttpResponseInfo response_info = request->response_info();
39 response->was_fetched_via_spdy = response_info.was_fetched_via_spdy;
40 response->was_npn_negotiated = response_info.was_npn_negotiated;
41 response->npn_negotiated_protocol = response_info.npn_negotiated_protocol;
42 response->was_fetched_via_proxy = request->was_fetched_via_proxy();
43 response->socket_address = request->GetSocketAddress();
44 appcache::AppCacheInterceptor::GetExtraResponseInfo(
45 request,
46 &response->appcache_id,
47 &response->appcache_manifest_url);
48 }
49
50 } // namespace
51
52 ResourceLoader::ResourceLoader(scoped_ptr<net::URLRequest> request,
53 scoped_ptr<ResourceHandler> handler,
54 ResourceLoaderDelegate* delegate)
55 : deferred_stage_(DEFERRED_NONE),
56 request_(request.Pass()),
57 handler_(handler.Pass()),
58 delegate_(delegate),
59 last_upload_position_(0),
60 waiting_for_upload_progress_ack_(false),
61 called_on_response_started_(false),
62 has_started_reading_(false),
63 is_paused_(false),
64 pause_count_(0),
65 paused_read_bytes_(0),
66 is_transferring_(false) {
67 request_->set_delegate(this);
68 handler_->SetController(this);
69 }
70
71 ResourceLoader::~ResourceLoader() {
72 if (login_delegate_)
73 login_delegate_->OnRequestCancelled();
74 if (ssl_client_auth_handler_)
75 ssl_client_auth_handler_->OnRequestCancelled();
76
77 // Run ResourceHandler destructor before we tear-down the rest of our state
78 // as the ResourceHandler may want to inspect the URLRequest and other state.
79 handler_.reset();
80 }
81
82 void ResourceLoader::StartRequest() {
83 if (delegate_->HandleExternalProtocol(this, request_->url())) {
84 CancelRequestInternal(net::ERR_UNKNOWN_URL_SCHEME, false);
85 return;
86 }
87
88 // Give the handler a chance to delay the URLRequest from being started.
89 bool defer_start = false;
90 if (!handler_->OnWillStart(GetRequestInfo()->GetRequestID(), request_->url(),
91 &defer_start)) {
92 Cancel();
93 return;
94 }
95
96 if (defer_start) {
97 deferred_stage_ = DEFERRED_START;
98 } else {
99 StartRequestInternal();
100 }
101 }
102
103 void ResourceLoader::CancelRequest(bool from_renderer) {
104 CancelRequestInternal(net::ERR_ABORTED, from_renderer);
105 }
106
107 void ResourceLoader::ReportUploadProgress() {
108 ResourceRequestInfoImpl* info = GetRequestInfo();
109
110 if (waiting_for_upload_progress_ack_)
111 return; // Send one progress event at a time.
112
113 uint64 size = info->GetUploadSize();
114 if (!size)
115 return; // Nothing to upload.
116
117 uint64 position = request_->GetUploadProgress();
118 if (position == last_upload_position_)
119 return; // No progress made since last time.
120
121 const uint64 kHalfPercentIncrements = 200;
122 const TimeDelta kOneSecond = TimeDelta::FromMilliseconds(1000);
123
124 uint64 amt_since_last = position - last_upload_position_;
125 TimeDelta time_since_last = TimeTicks::Now() - last_upload_ticks_;
126
127 bool is_finished = (size == position);
128 bool enough_new_progress = (amt_since_last > (size / kHalfPercentIncrements));
129 bool too_much_time_passed = time_since_last > kOneSecond;
130
131 if (is_finished || enough_new_progress || too_much_time_passed) {
132 if (request_->load_flags() & net::LOAD_ENABLE_UPLOAD_PROGRESS) {
133 handler_->OnUploadProgress(info->GetRequestID(), position, size);
134 waiting_for_upload_progress_ack_ = true;
135 }
136 last_upload_ticks_ = TimeTicks::Now();
137 last_upload_position_ = position;
138 }
139 }
140
141 void ResourceLoader::MarkAsTransferring() {
142 is_transferring_ = true;
143
144 // When an URLRequest is transferred to a new RenderViewHost, its
145 // ResourceHandler should not receive any notifications because it may depend
146 // on the state of the old RVH. We set a ResourceHandler that only allows
147 // canceling requests, because on shutdown of the RDH all pending requests
148 // are canceled. The RVH of requests that are being transferred may be gone
149 // by that time. In CompleteTransfer, the ResoureHandlers are substituted
150 // again.
151 handler_.reset(new DoomedResourceHandler(handler_.Pass()));
152 }
153
154 void ResourceLoader::CompleteTransfer(scoped_ptr<ResourceHandler> new_handler) {
155 DCHECK_EQ(DEFERRED_REDIRECT, deferred_stage_);
156
157 handler_ = new_handler.Pass();
158 handler_->SetController(this);
159 is_transferring_ = false;
160
161 Resume();
162 }
163
164 ResourceRequestInfoImpl* ResourceLoader::GetRequestInfo() {
165 return ResourceRequestInfoImpl::ForRequest(request_.get());
166 }
167
168 void ResourceLoader::ClearLoginDelegate() {
169 login_delegate_ = NULL;
170 }
171
172 void ResourceLoader::ClearSSLClientAuthHandler() {
173 ssl_client_auth_handler_ = NULL;
174 }
175
176 void ResourceLoader::OnUploadProgressACK() {
177 waiting_for_upload_progress_ack_ = false;
178 }
179
180 void ResourceLoader::OnFollowRedirect(bool has_new_first_party_for_cookies,
181 const GURL& new_first_party_for_cookies) {
182 if (!request_->status().is_success()) {
183 DVLOG(1) << "OnDeferredRedirect for invalid request";
184 return;
185 }
186
187 if (has_new_first_party_for_cookies)
188 request_->set_first_party_for_cookies(new_first_party_for_cookies);
189 request_->FollowDeferredRedirect();
190 }
191
192 void ResourceLoader::OnReceivedRedirect(net::URLRequest* unused,
193 const GURL& new_url,
194 bool* defer) {
195 DCHECK_EQ(request_.get(), unused);
196
197 VLOG(1) << "OnReceivedRedirect: " << request_->url().spec();
198 DCHECK(request_->status().is_success());
199
200 ResourceRequestInfoImpl* info = GetRequestInfo();
201
202 if (info->process_type() != PROCESS_TYPE_PLUGIN &&
203 !ChildProcessSecurityPolicyImpl::GetInstance()->
204 CanRequestURL(info->GetChildID(), new_url)) {
205 VLOG(1) << "Denied unauthorized request for "
206 << new_url.possibly_invalid_spec();
207
208 // Tell the renderer that this request was disallowed.
209 Cancel();
210 return;
211 }
212
213 delegate_->DidReceiveRedirect(this, new_url);
214
215 if (delegate_->HandleExternalProtocol(this, new_url)) {
216 // The request is complete so we can remove it.
217 CancelRequestInternal(net::ERR_UNKNOWN_URL_SCHEME, false);
218 return;
219 }
220
221 scoped_refptr<ResourceResponse> response(new ResourceResponse());
222 PopulateResourceResponse(request_.get(), response);
223
224 if (!handler_->OnRequestRedirected(info->GetRequestID(), new_url, response,
225 defer)) {
226 Cancel();
227 }
228
229 if (*defer)
230 deferred_stage_ = DEFERRED_REDIRECT;
231 }
232
233 void ResourceLoader::OnAuthRequired(net::URLRequest* unused,
234 net::AuthChallengeInfo* auth_info) {
235 DCHECK_EQ(request_.get(), unused);
236
237 if (request_->load_flags() & net::LOAD_DO_NOT_PROMPT_FOR_LOGIN) {
238 request_->CancelAuth();
239 return;
240 }
241
242 if (!delegate_->AcceptAuthRequest(this, auth_info)) {
243 request_->CancelAuth();
244 return;
245 }
246
247 // Create a login dialog on the UI thread to get authentication data, or pull
248 // from cache and continue on the IO thread.
249
250 DCHECK(!login_delegate_) <<
251 "OnAuthRequired called with login_delegate pending";
252 login_delegate_ = delegate_->CreateLoginDelegate(this, auth_info);
253 if (!login_delegate_)
254 request_->CancelAuth();
255 }
256
257 void ResourceLoader::OnCertificateRequested(
258 net::URLRequest* unused,
259 net::SSLCertRequestInfo* cert_info) {
260 DCHECK_EQ(request_.get(), unused);
261
262 if (!delegate_->AcceptSSLClientCertificateRequest(this, cert_info)) {
263 request_->Cancel();
264 return;
265 }
266
267 if (cert_info->client_certs.empty()) {
268 // No need to query the user if there are no certs to choose from.
269 request_->ContinueWithCertificate(NULL);
270 return;
271 }
272
273 DCHECK(!ssl_client_auth_handler_) <<
274 "OnCertificateRequested called with ssl_client_auth_handler pending";
275 ssl_client_auth_handler_ = new SSLClientAuthHandler(request_.get(),
276 cert_info);
277 ssl_client_auth_handler_->SelectCertificate();
278 }
279
280 void ResourceLoader::OnSSLCertificateError(net::URLRequest* request,
281 const net::SSLInfo& ssl_info,
282 bool fatal) {
283 ResourceRequestInfoImpl* info = GetRequestInfo();
284
285 int render_process_id;
286 int render_view_id;
287 if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id))
288 NOTREACHED();
289
290 SSLManager::OnSSLCertificateError(
291 AsWeakPtr(),
292 info->GetGlobalRequestID(),
293 info->GetResourceType(),
294 request_->url(),
295 render_process_id,
296 render_view_id,
297 ssl_info,
298 fatal);
299 }
300
301 void ResourceLoader::OnResponseStarted(net::URLRequest* unused) {
302 DCHECK_EQ(request_.get(), unused);
303
304 VLOG(1) << "OnResponseStarted: " << request_->url().spec();
305
306 if (request_->status().is_success()) {
307 if (PauseRequestIfNeeded()) {
308 VLOG(1) << "OnResponseStarted pausing: " << request_->url().spec();
309 return;
310 }
311
312 // We want to send a final upload progress message prior to sending the
313 // response complete message even if we're waiting for an ack to to a
314 // previous upload progress message.
315 waiting_for_upload_progress_ack_ = false;
316 ReportUploadProgress();
317
318 if (!CompleteResponseStarted()) {
319 Cancel();
320 } else {
321 // Check if the handler paused the request in their OnResponseStarted.
322 if (PauseRequestIfNeeded()) {
323 VLOG(1) << "OnResponseStarted pausing2: " << request_->url().spec();
324 return;
325 }
326
327 StartReading();
328 }
329 } else {
330 ResponseCompleted();
331 }
332 }
333
334 void ResourceLoader::OnReadCompleted(net::URLRequest* unused, int bytes_read) {
335 DCHECK_EQ(request_.get(), unused);
336 VLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\""
337 << " bytes_read = " << bytes_read;
338
339 // bytes_read == -1 always implies an error, so we want to skip the pause
340 // checks and just call ResponseCompleted.
341 if (bytes_read == -1) {
342 DCHECK(!request_->status().is_success());
343
344 ResponseCompleted();
345 return;
346 }
347
348 // OnReadCompleted can be called without Read (e.g., for chrome:// URLs).
349 // Make sure we know that a read has begun.
350 has_started_reading_ = true;
351
352 if (PauseRequestIfNeeded()) {
353 paused_read_bytes_ = bytes_read;
354 VLOG(1) << "OnReadCompleted pausing: \"" << request_->url().spec() << "\""
355 << " bytes_read = " << bytes_read;
356 return;
357 }
358
359 if (request_->status().is_success() && CompleteRead(&bytes_read)) {
360 // The request can be paused if we realize that the renderer is not
361 // servicing messages fast enough.
362 if (pause_count_ == 0 && ReadMore(&bytes_read) &&
363 request_->status().is_success()) {
364 if (bytes_read == 0) {
365 CompleteRead(&bytes_read);
366 } else {
367 // Force the next CompleteRead / Read pair to run as a separate task.
368 // This avoids a fast, large network request from monopolizing the IO
369 // thread and starving other IO operations from running.
370 VLOG(1) << "OnReadCompleted postponing: \""
371 << request_->url().spec() << "\""
372 << " bytes_read = " << bytes_read;
373
374 paused_read_bytes_ = bytes_read;
375 is_paused_ = true;
376
377 MessageLoop::current()->PostTask(
378 FROM_HERE, base::Bind(&ResourceLoader::ResumeRequest, AsWeakPtr()));
379 return;
380 }
381 }
382 }
383
384 if (PauseRequestIfNeeded()) {
385 paused_read_bytes_ = bytes_read;
386 VLOG(1) << "OnReadCompleted (CompleteRead) pausing: \""
387 << request_->url().spec() << "\""
388 << " bytes_read = " << bytes_read;
389 return;
390 }
391
392 // If the status is not IO pending then we've either finished (success) or we
393 // had an error. Either way, we're done!
394 if (!request_->status().is_io_pending())
395 ResponseCompleted();
396 }
397
398 void ResourceLoader::CancelSSLRequest(const GlobalRequestID& id,
399 int error,
400 const net::SSLInfo* ssl_info) {
401 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
402
403 // The request can be NULL if it was cancelled by the renderer (as the
404 // request of the user navigating to a new page from the location bar).
405 if (!request_->is_pending())
406 return;
407 DVLOG(1) << "CancelSSLRequest() url: " << request_->url().spec();
408
409 if (ssl_info) {
410 request_->CancelWithSSLError(error, *ssl_info);
411 } else {
412 request_->CancelWithError(error);
413 }
414 }
415
416 void ResourceLoader::ContinueSSLRequest(const GlobalRequestID& id) {
417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
418
419 DVLOG(1) << "ContinueSSLRequest() url: " << request_->url().spec();
420
421 request_->ContinueDespiteLastError();
422 }
423
424 void ResourceLoader::Resume() {
425 DCHECK(!is_transferring_);
426
427 DeferredStage stage = deferred_stage_;
428 deferred_stage_ = DEFERRED_NONE;
429 switch (stage) {
430 case DEFERRED_NONE:
431 NOTREACHED();
432 break;
433 case DEFERRED_START:
434 StartRequestInternal();
435 break;
436 case DEFERRED_REDIRECT:
437 request_->FollowDeferredRedirect();
438 break;
439 case DEFERRED_RESPONSE:
440 case DEFERRED_READ:
441 PauseRequest(false);
442 break;
443 case DEFERRED_FINISH:
444 // Delay self-destruction since we don't know how we were reached.
445 MessageLoop::current()->PostTask(
446 FROM_HERE,
447 base::Bind(&ResourceLoader::CallDidFinishLoading, AsWeakPtr()));
448 break;
449 }
450 }
451
452 void ResourceLoader::Cancel() {
453 CancelRequest(false);
454 }
455
456 void ResourceLoader::StartRequestInternal() {
457 DCHECK(!request_->is_pending());
458 request_->Start();
459
460 delegate_->DidStartRequest(this);
461 }
462
463 void ResourceLoader::CancelRequestInternal(int error, bool from_renderer) {
464 VLOG(1) << "CancelRequestInternal: " << request_->url().spec();
465
466 ResourceRequestInfoImpl* info = GetRequestInfo();
467
468 // WebKit will send us a cancel for downloads since it no longer handles
469 // them. In this case, ignore the cancel since we handle downloads in the
470 // browser.
471 if (from_renderer && info->is_download())
472 return;
473
474 bool was_pending = request_->is_pending();
475
476 if (login_delegate_) {
477 login_delegate_->OnRequestCancelled();
478 login_delegate_ = NULL;
479 }
480 if (ssl_client_auth_handler_) {
481 ssl_client_auth_handler_->OnRequestCancelled();
482 ssl_client_auth_handler_ = NULL;
483 }
484
485 request_->CancelWithError(error);
486
487 if (!was_pending) {
488 // If the request isn't in flight, then we won't get an asynchronous
489 // notification from the request, so we have to signal ourselves to finish
490 // this request.
491 MessageLoop::current()->PostTask(
492 FROM_HERE, base::Bind(&ResourceLoader::ResponseCompleted, AsWeakPtr()));
493 }
494 }
495
496 bool ResourceLoader::CompleteResponseStarted() {
497 ResourceRequestInfoImpl* info = GetRequestInfo();
498
499 scoped_refptr<ResourceResponse> response(new ResourceResponse());
500 PopulateResourceResponse(request_.get(), response);
501
502 if (request_->ssl_info().cert) {
503 int cert_id =
504 CertStore::GetInstance()->StoreCert(request_->ssl_info().cert,
505 info->GetChildID());
506 response->security_info = SerializeSecurityInfo(
507 cert_id,
508 request_->ssl_info().cert_status,
509 request_->ssl_info().security_bits,
510 request_->ssl_info().connection_status);
511 } else {
512 // We should not have any SSL state.
513 DCHECK(!request_->ssl_info().cert_status &&
514 request_->ssl_info().security_bits == -1 &&
515 !request_->ssl_info().connection_status);
516 }
517
518 delegate_->DidReceiveResponse(this);
519 called_on_response_started_ = true;
520
521 bool defer = false;
522 if (!handler_->OnResponseStarted(info->GetRequestID(), response, &defer))
523 return false;
524
525 if (defer) {
526 deferred_stage_ = DEFERRED_RESPONSE;
527 PauseRequest(true);
528 }
529
530 return true;
531 }
532
533 void ResourceLoader::StartReading() {
534 int bytes_read = 0;
535 if (ReadMore(&bytes_read)) {
536 OnReadCompleted(request_.get(), bytes_read);
537 } else if (!request_->status().is_io_pending()) {
538 DCHECK(!is_paused_);
539 // If the error is not an IO pending, then we're done reading.
540 ResponseCompleted();
541 }
542 }
543
544 bool ResourceLoader::ReadMore(int* bytes_read) {
545 ResourceRequestInfoImpl* info = GetRequestInfo();
546 DCHECK(!is_paused_);
547
548 net::IOBuffer* buf;
549 int buf_size;
550 if (!handler_->OnWillRead(info->GetRequestID(), &buf, &buf_size, -1))
551 return false;
552
553 DCHECK(buf);
554 DCHECK(buf_size > 0);
555
556 has_started_reading_ = true;
557 return request_->Read(buf, buf_size, bytes_read);
558 }
559
560 bool ResourceLoader::CompleteRead(int* bytes_read) {
561 if (!request_.get() || !request_->status().is_success()) {
562 NOTREACHED();
563 return false;
564 }
565
566 ResourceRequestInfoImpl* info = GetRequestInfo();
567
568 bool defer = false;
569 if (!handler_->OnReadCompleted(info->GetRequestID(), bytes_read, &defer)) {
570 Cancel();
571 return false;
572 }
573
574 if (defer) {
575 deferred_stage_ = DEFERRED_READ;
576 PauseRequest(true);
577 }
578
579 return *bytes_read != 0;
580 }
581
582 void ResourceLoader::ResponseCompleted() {
583 VLOG(1) << "ResponseCompleted: " << request_->url().spec();
584 ResourceRequestInfoImpl* info = GetRequestInfo();
585
586 std::string security_info;
587 const net::SSLInfo& ssl_info = request_->ssl_info();
588 if (ssl_info.cert != NULL) {
589 int cert_id = CertStore::GetInstance()->StoreCert(ssl_info.cert,
590 info->GetChildID());
591 security_info = SerializeSecurityInfo(
592 cert_id, ssl_info.cert_status, ssl_info.security_bits,
593 ssl_info.connection_status);
594 }
595
596 if (handler_->OnResponseCompleted(info->GetRequestID(), request_->status(),
597 security_info)) {
598 // This will result in our destruction.
599 CallDidFinishLoading();
600 } else {
601 // The handler is not ready to die yet. We will call DidFinishLoading when
602 // we resume.
603 deferred_stage_ = DEFERRED_FINISH;
604 }
605 }
606
607 bool ResourceLoader::PauseRequestIfNeeded() {
608 if (pause_count_ > 0)
609 is_paused_ = true;
610 return is_paused_;
611 }
612
613 void ResourceLoader::PauseRequest(bool pause) {
614 int pause_count = pause_count_ + (pause ? 1 : -1);
615 if (pause_count < 0) {
616 NOTREACHED(); // Unbalanced call to pause.
617 return;
618 }
619 pause_count_ = pause_count;
620
621 VLOG(1) << "To pause (" << pause << "): " << request_->url().spec();
622
623 // If we're resuming, kick the request to start reading again. Run the read
624 // asynchronously to avoid recursion problems.
625 if (pause_count_ == 0) {
626 MessageLoop::current()->PostTask(
627 FROM_HERE, base::Bind(&ResourceLoader::ResumeRequest, AsWeakPtr()));
628 }
629 }
630
631 void ResourceLoader::ResumeRequest() {
632 // We may already be unpaused, or the pause count may have increased since we
633 // posted the task to call ResumeRequest.
634 if (!is_paused_)
635 return;
636 is_paused_ = false;
637 if (PauseRequestIfNeeded())
638 return;
639
640 VLOG(1) << "Resuming: \"" << request_->url().spec() << "\""
641 << " paused_read_bytes = " << paused_read_bytes_
642 << " called response started = " << called_on_response_started_
643 << " started reading = " << has_started_reading_;
644
645 if (called_on_response_started_) {
646 if (has_started_reading_) {
647 OnReadCompleted(request_.get(), paused_read_bytes_);
648 } else {
649 StartReading();
650 }
651 } else {
652 OnResponseStarted(request_.get());
653 }
654 }
655
656 void ResourceLoader::CallDidFinishLoading() {
657 delegate_->DidFinishLoading(this);
658 }
659
660 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698