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.
5 #include "content/browser/loader/resource_loader.h"
7 #include "base/command_line.h"
8 #include "base/message_loop.h"
10 #include "content/browser/child_process_security_policy_impl.h"
11 #include "content/browser/loader/doomed_resource_handler.h"
12 #include "content/browser/loader/resource_loader_delegate.h"
13 #include "content/browser/loader/resource_request_info_impl.h"
14 #include "content/browser/ssl/ssl_client_auth_handler.h"
15 #include "content/browser/ssl/ssl_manager.h"
16 #include "content/common/ssl_status_serialization.h"
17 #include "content/public/browser/cert_store.h"
18 #include "content/public/browser/resource_dispatcher_host_login_delegate.h"
19 #include "content/public/common/content_client.h"
20 #include "content/public/common/content_switches.h"
21 #include "content/public/common/resource_response.h"
22 #include "net/base/load_flags.h"
23 #include "net/http/http_response_headers.h"
24 #include "webkit/appcache/appcache_interceptor.h"
26 using base::TimeDelta
;
27 using base::TimeTicks
;
32 void PopulateResourceResponse(net::URLRequest
* request
,
33 ResourceResponse
* response
) {
34 response
->head
.error_code
= request
->status().error();
35 response
->head
.request_time
= request
->request_time();
36 response
->head
.response_time
= request
->response_time();
37 response
->head
.headers
= request
->response_headers();
38 request
->GetCharset(&response
->head
.charset
);
39 response
->head
.content_length
= request
->GetExpectedContentSize();
40 request
->GetMimeType(&response
->head
.mime_type
);
41 net::HttpResponseInfo response_info
= request
->response_info();
42 response
->head
.was_fetched_via_spdy
= response_info
.was_fetched_via_spdy
;
43 response
->head
.was_npn_negotiated
= response_info
.was_npn_negotiated
;
44 response
->head
.npn_negotiated_protocol
=
45 response_info
.npn_negotiated_protocol
;
46 response
->head
.was_fetched_via_proxy
= request
->was_fetched_via_proxy();
47 response
->head
.socket_address
= request
->GetSocketAddress();
48 appcache::AppCacheInterceptor::GetExtraResponseInfo(
50 &response
->head
.appcache_id
,
51 &response
->head
.appcache_manifest_url
);
56 ResourceLoader::ResourceLoader(scoped_ptr
<net::URLRequest
> request
,
57 scoped_ptr
<ResourceHandler
> handler
,
58 ResourceLoaderDelegate
* delegate
)
59 : deferred_stage_(DEFERRED_NONE
),
60 request_(request
.Pass()),
61 handler_(handler
.Pass()),
63 last_upload_position_(0),
64 waiting_for_upload_progress_ack_(false),
65 is_transferring_(false),
66 weak_ptr_factory_(this) {
67 request_
->set_delegate(this);
68 handler_
->SetController(this);
71 ResourceLoader::~ResourceLoader() {
73 login_delegate_
->OnRequestCancelled();
74 if (ssl_client_auth_handler_
)
75 ssl_client_auth_handler_
->OnRequestCancelled();
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.
82 void ResourceLoader::StartRequest() {
83 if (delegate_
->HandleExternalProtocol(this, request_
->url())) {
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(),
97 deferred_stage_
= DEFERRED_START
;
99 StartRequestInternal();
103 void ResourceLoader::CancelRequest(bool from_renderer
) {
104 CancelRequestInternal(net::ERR_ABORTED
, from_renderer
);
107 void ResourceLoader::CancelAndIgnore() {
108 ResourceRequestInfoImpl
* info
= GetRequestInfo();
109 info
->set_was_ignored_by_handler(true);
110 CancelRequest(false);
113 void ResourceLoader::CancelWithError(int error_code
) {
114 CancelRequestInternal(error_code
, false);
117 void ResourceLoader::ReportUploadProgress() {
118 ResourceRequestInfoImpl
* info
= GetRequestInfo();
120 if (waiting_for_upload_progress_ack_
)
121 return; // Send one progress event at a time.
123 net::UploadProgress progress
= request_
->GetUploadProgress();
124 if (!progress
.size())
125 return; // Nothing to upload.
127 if (progress
.position() == last_upload_position_
)
128 return; // No progress made since last time.
130 const uint64 kHalfPercentIncrements
= 200;
131 const TimeDelta kOneSecond
= TimeDelta::FromMilliseconds(1000);
133 uint64 amt_since_last
= progress
.position() - last_upload_position_
;
134 TimeDelta time_since_last
= TimeTicks::Now() - last_upload_ticks_
;
136 bool is_finished
= (progress
.size() == progress
.position());
137 bool enough_new_progress
=
138 (amt_since_last
> (progress
.size() / kHalfPercentIncrements
));
139 bool too_much_time_passed
= time_since_last
> kOneSecond
;
141 if (is_finished
|| enough_new_progress
|| too_much_time_passed
) {
142 if (request_
->load_flags() & net::LOAD_ENABLE_UPLOAD_PROGRESS
) {
143 handler_
->OnUploadProgress(
144 info
->GetRequestID(), progress
.position(), progress
.size());
145 waiting_for_upload_progress_ack_
= true;
147 last_upload_ticks_
= TimeTicks::Now();
148 last_upload_position_
= progress
.position();
152 void ResourceLoader::MarkAsTransferring() {
153 is_transferring_
= true;
155 // When an URLRequest is transferred to a new RenderViewHost, its
156 // ResourceHandler should not receive any notifications because it may depend
157 // on the state of the old RVH. We set a ResourceHandler that only allows
158 // canceling requests, because on shutdown of the RDH all pending requests
159 // are canceled. The RVH of requests that are being transferred may be gone
160 // by that time. In CompleteTransfer, the ResoureHandlers are substituted
162 handler_
.reset(new DoomedResourceHandler(handler_
.Pass()));
165 void ResourceLoader::WillCompleteTransfer() {
169 void ResourceLoader::CompleteTransfer(scoped_ptr
<ResourceHandler
> new_handler
) {
170 DCHECK_EQ(DEFERRED_REDIRECT
, deferred_stage_
);
171 DCHECK(!handler_
.get());
173 handler_
= new_handler
.Pass();
174 handler_
->SetController(this);
175 is_transferring_
= false;
180 ResourceRequestInfoImpl
* ResourceLoader::GetRequestInfo() {
181 return ResourceRequestInfoImpl::ForRequest(request_
.get());
184 void ResourceLoader::ClearLoginDelegate() {
185 login_delegate_
= NULL
;
188 void ResourceLoader::ClearSSLClientAuthHandler() {
189 ssl_client_auth_handler_
= NULL
;
192 void ResourceLoader::OnUploadProgressACK() {
193 waiting_for_upload_progress_ack_
= false;
196 void ResourceLoader::OnReceivedRedirect(net::URLRequest
* unused
,
199 DCHECK_EQ(request_
.get(), unused
);
201 VLOG(1) << "OnReceivedRedirect: " << request_
->url().spec();
202 DCHECK(request_
->status().is_success());
204 ResourceRequestInfoImpl
* info
= GetRequestInfo();
206 if (info
->process_type() != PROCESS_TYPE_PLUGIN
&&
207 !ChildProcessSecurityPolicyImpl::GetInstance()->
208 CanRequestURL(info
->GetChildID(), new_url
)) {
209 VLOG(1) << "Denied unauthorized request for "
210 << new_url
.possibly_invalid_spec();
212 // Tell the renderer that this request was disallowed.
217 delegate_
->DidReceiveRedirect(this, new_url
);
219 if (delegate_
->HandleExternalProtocol(this, new_url
)) {
220 // The request is complete so we can remove it.
225 scoped_refptr
<ResourceResponse
> response(new ResourceResponse());
226 PopulateResourceResponse(request_
.get(), response
);
228 if (!handler_
->OnRequestRedirected(info
->GetRequestID(), new_url
, response
,
232 deferred_stage_
= DEFERRED_REDIRECT
; // Follow redirect when resumed.
236 void ResourceLoader::OnAuthRequired(net::URLRequest
* unused
,
237 net::AuthChallengeInfo
* auth_info
) {
238 DCHECK_EQ(request_
.get(), unused
);
240 if (request_
->load_flags() & net::LOAD_DO_NOT_PROMPT_FOR_LOGIN
) {
241 request_
->CancelAuth();
245 if (!delegate_
->AcceptAuthRequest(this, auth_info
)) {
246 request_
->CancelAuth();
250 // Create a login dialog on the UI thread to get authentication data, or pull
251 // from cache and continue on the IO thread.
253 DCHECK(!login_delegate_
) <<
254 "OnAuthRequired called with login_delegate pending";
255 login_delegate_
= delegate_
->CreateLoginDelegate(this, auth_info
);
256 if (!login_delegate_
)
257 request_
->CancelAuth();
260 void ResourceLoader::OnCertificateRequested(
261 net::URLRequest
* unused
,
262 net::SSLCertRequestInfo
* cert_info
) {
263 DCHECK_EQ(request_
.get(), unused
);
265 if (!delegate_
->AcceptSSLClientCertificateRequest(this, cert_info
)) {
270 if (cert_info
->client_certs
.empty()) {
271 // No need to query the user if there are no certs to choose from.
272 request_
->ContinueWithCertificate(NULL
);
276 DCHECK(!ssl_client_auth_handler_
) <<
277 "OnCertificateRequested called with ssl_client_auth_handler pending";
278 ssl_client_auth_handler_
= new SSLClientAuthHandler(request_
.get(),
280 ssl_client_auth_handler_
->SelectCertificate();
283 void ResourceLoader::OnSSLCertificateError(net::URLRequest
* request
,
284 const net::SSLInfo
& ssl_info
,
286 ResourceRequestInfoImpl
* info
= GetRequestInfo();
288 int render_process_id
;
290 if (!info
->GetAssociatedRenderView(&render_process_id
, &render_view_id
))
293 SSLManager::OnSSLCertificateError(
294 weak_ptr_factory_
.GetWeakPtr(),
295 info
->GetGlobalRequestID(),
296 info
->GetResourceType(),
304 void ResourceLoader::OnResponseStarted(net::URLRequest
* unused
) {
305 DCHECK_EQ(request_
.get(), unused
);
307 VLOG(1) << "OnResponseStarted: " << request_
->url().spec();
309 // The CanLoadPage check should take place after any server redirects have
310 // finished, at the point in time that we know a page will commit in the
312 ResourceRequestInfoImpl
* info
= GetRequestInfo();
313 ChildProcessSecurityPolicyImpl
* policy
=
314 ChildProcessSecurityPolicyImpl::GetInstance();
315 if (!policy
->CanLoadPage(info
->GetChildID(),
317 info
->GetResourceType())) {
322 if (!request_
->status().is_success()) {
327 // We want to send a final upload progress message prior to sending the
328 // response complete message even if we're waiting for an ack to to a
329 // previous upload progress message.
330 waiting_for_upload_progress_ack_
= false;
331 ReportUploadProgress();
333 CompleteResponseStarted();
338 if (request_
->status().is_success()) {
339 StartReading(false); // Read the first chunk.
345 void ResourceLoader::OnReadCompleted(net::URLRequest
* unused
, int bytes_read
) {
346 DCHECK_EQ(request_
.get(), unused
);
347 VLOG(1) << "OnReadCompleted: \"" << request_
->url().spec() << "\""
348 << " bytes_read = " << bytes_read
;
350 // bytes_read == -1 always implies an error.
351 if (bytes_read
== -1 || !request_
->status().is_success()) {
356 CompleteRead(bytes_read
);
361 if (request_
->status().is_success() && bytes_read
> 0) {
362 StartReading(true); // Read the next chunk.
368 void ResourceLoader::CancelSSLRequest(const GlobalRequestID
& id
,
370 const net::SSLInfo
* ssl_info
) {
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
373 // The request can be NULL if it was cancelled by the renderer (as the
374 // request of the user navigating to a new page from the location bar).
375 if (!request_
->is_pending())
377 DVLOG(1) << "CancelSSLRequest() url: " << request_
->url().spec();
380 request_
->CancelWithSSLError(error
, *ssl_info
);
382 request_
->CancelWithError(error
);
386 void ResourceLoader::ContinueSSLRequest(const GlobalRequestID
& id
) {
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
389 DVLOG(1) << "ContinueSSLRequest() url: " << request_
->url().spec();
391 request_
->ContinueDespiteLastError();
394 void ResourceLoader::Resume() {
395 DCHECK(!is_transferring_
);
397 DeferredStage stage
= deferred_stage_
;
398 deferred_stage_
= DEFERRED_NONE
;
404 StartRequestInternal();
406 case DEFERRED_REDIRECT
:
407 request_
->FollowDeferredRedirect();
410 MessageLoop::current()->PostTask(
412 base::Bind(&ResourceLoader::ResumeReading
,
413 weak_ptr_factory_
.GetWeakPtr()));
415 case DEFERRED_FINISH
:
416 // Delay self-destruction since we don't know how we were reached.
417 MessageLoop::current()->PostTask(
419 base::Bind(&ResourceLoader::CallDidFinishLoading
,
420 weak_ptr_factory_
.GetWeakPtr()));
425 void ResourceLoader::Cancel() {
426 CancelRequest(false);
429 void ResourceLoader::StartRequestInternal() {
430 DCHECK(!request_
->is_pending());
433 delegate_
->DidStartRequest(this);
436 void ResourceLoader::CancelRequestInternal(int error
, bool from_renderer
) {
437 VLOG(1) << "CancelRequestInternal: " << request_
->url().spec();
439 ResourceRequestInfoImpl
* info
= GetRequestInfo();
441 // WebKit will send us a cancel for downloads since it no longer handles
442 // them. In this case, ignore the cancel since we handle downloads in the
444 if (from_renderer
&& info
->is_download())
447 // TODO(darin): Perhaps we should really be looking to see if the status is
449 bool was_pending
= request_
->is_pending();
451 if (login_delegate_
) {
452 login_delegate_
->OnRequestCancelled();
453 login_delegate_
= NULL
;
455 if (ssl_client_auth_handler_
) {
456 ssl_client_auth_handler_
->OnRequestCancelled();
457 ssl_client_auth_handler_
= NULL
;
460 request_
->CancelWithError(error
);
463 // If the request isn't in flight, then we won't get an asynchronous
464 // notification from the request, so we have to signal ourselves to finish
466 MessageLoop::current()->PostTask(
467 FROM_HERE
, base::Bind(&ResourceLoader::ResponseCompleted
,
468 weak_ptr_factory_
.GetWeakPtr()));
472 void ResourceLoader::CompleteResponseStarted() {
473 ResourceRequestInfoImpl
* info
= GetRequestInfo();
475 scoped_refptr
<ResourceResponse
> response(new ResourceResponse());
476 PopulateResourceResponse(request_
.get(), response
);
478 if (request_
->ssl_info().cert
) {
480 CertStore::GetInstance()->StoreCert(request_
->ssl_info().cert
,
482 response
->head
.security_info
= SerializeSecurityInfo(
484 request_
->ssl_info().cert_status
,
485 request_
->ssl_info().security_bits
,
486 request_
->ssl_info().connection_status
);
488 // We should not have any SSL state.
489 DCHECK(!request_
->ssl_info().cert_status
&&
490 request_
->ssl_info().security_bits
== -1 &&
491 !request_
->ssl_info().connection_status
);
494 delegate_
->DidReceiveResponse(this);
497 if (!handler_
->OnResponseStarted(info
->GetRequestID(), response
, &defer
)) {
500 deferred_stage_
= DEFERRED_READ
; // Read first chunk when resumed.
504 void ResourceLoader::StartReading(bool is_continuation
) {
506 ReadMore(&bytes_read
);
508 // If IO is pending, wait for the URLRequest to call OnReadCompleted.
509 if (request_
->status().is_io_pending())
512 if (!is_continuation
|| bytes_read
<= 0) {
513 OnReadCompleted(request_
.get(), bytes_read
);
515 // Else, trigger OnReadCompleted asynchronously to avoid starving the IO
516 // thread in case the URLRequest can provide data synchronously.
517 MessageLoop::current()->PostTask(
519 base::Bind(&ResourceLoader::OnReadCompleted
,
520 weak_ptr_factory_
.GetWeakPtr(),
521 request_
.get(), bytes_read
));
525 void ResourceLoader::ResumeReading() {
526 DCHECK(!is_deferred());
528 if (request_
->status().is_success()) {
529 StartReading(false); // Read the next chunk (OK to complete synchronously).
535 void ResourceLoader::ReadMore(int* bytes_read
) {
536 ResourceRequestInfoImpl
* info
= GetRequestInfo();
537 DCHECK(!is_deferred());
541 if (!handler_
->OnWillRead(info
->GetRequestID(), &buf
, &buf_size
, -1)) {
547 DCHECK(buf_size
> 0);
549 request_
->Read(buf
, buf_size
, bytes_read
);
551 // No need to check the return value here as we'll detect errors by
552 // inspecting the URLRequest's status.
555 void ResourceLoader::CompleteRead(int bytes_read
) {
556 DCHECK(bytes_read
>= 0);
557 DCHECK(request_
->status().is_success());
559 ResourceRequestInfoImpl
* info
= GetRequestInfo();
562 if (!handler_
->OnReadCompleted(info
->GetRequestID(), bytes_read
, &defer
)) {
565 deferred_stage_
= DEFERRED_READ
; // Read next chunk when resumed.
569 void ResourceLoader::ResponseCompleted() {
570 VLOG(1) << "ResponseCompleted: " << request_
->url().spec();
571 ResourceRequestInfoImpl
* info
= GetRequestInfo();
573 std::string security_info
;
574 const net::SSLInfo
& ssl_info
= request_
->ssl_info();
575 if (ssl_info
.cert
!= NULL
) {
576 int cert_id
= CertStore::GetInstance()->StoreCert(ssl_info
.cert
,
578 security_info
= SerializeSecurityInfo(
579 cert_id
, ssl_info
.cert_status
, ssl_info
.security_bits
,
580 ssl_info
.connection_status
);
583 if (handler_
->OnResponseCompleted(info
->GetRequestID(), request_
->status(),
585 // This will result in our destruction.
586 CallDidFinishLoading();
588 // The handler is not ready to die yet. We will call DidFinishLoading when
590 deferred_stage_
= DEFERRED_FINISH
;
594 void ResourceLoader::CallDidFinishLoading() {
595 delegate_
->DidFinishLoading(this);
598 } // namespace content