// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/loader/resource_loader.h" #include "base/command_line.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" #include "base/profiler/scoped_tracker.h" #include "base/time/time.h" #include "content/browser/appcache/appcache_interceptor.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/loader/cross_site_resource_handler.h" #include "content/browser/loader/detachable_resource_handler.h" #include "content/browser/loader/resource_loader_delegate.h" #include "content/browser/loader/resource_request_info_impl.h" #include "content/browser/service_worker/service_worker_request_handler.h" #include "content/browser/ssl/ssl_client_auth_handler.h" #include "content/browser/ssl/ssl_manager.h" #include "content/common/ssl_status_serialization.h" #include "content/public/browser/cert_store.h" #include "content/public/browser/resource_context.h" #include "content/public/browser/resource_dispatcher_host_login_delegate.h" #include "content/public/browser/signed_certificate_timestamp_store.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" #include "content/public/common/process_type.h" #include "content/public/common/resource_response.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/http/http_response_headers.h" #include "net/ssl/client_cert_store.h" #include "net/url_request/redirect_info.h" #include "net/url_request/url_request_status.h" using base::TimeDelta; using base::TimeTicks; namespace content { namespace { void PopulateResourceResponse(ResourceRequestInfoImpl* info, net::URLRequest* request, ResourceResponse* response) { response->head.request_time = request->request_time(); response->head.response_time = request->response_time(); response->head.headers = request->response_headers(); request->GetCharset(&response->head.charset); response->head.content_length = request->GetExpectedContentSize(); request->GetMimeType(&response->head.mime_type); net::HttpResponseInfo response_info = request->response_info(); response->head.was_fetched_via_spdy = response_info.was_fetched_via_spdy; response->head.was_npn_negotiated = response_info.was_npn_negotiated; response->head.npn_negotiated_protocol = response_info.npn_negotiated_protocol; response->head.connection_info = response_info.connection_info; response->head.was_fetched_via_proxy = request->was_fetched_via_proxy(); response->head.proxy_server = response_info.proxy_server; response->head.socket_address = request->GetSocketAddress(); if (ServiceWorkerRequestHandler* handler = ServiceWorkerRequestHandler::GetHandler(request)) { handler->GetExtraResponseInfo( &response->head.was_fetched_via_service_worker, &response->head.was_fallback_required_by_service_worker, &response->head.original_url_via_service_worker, &response->head.response_type_via_service_worker, &response->head.service_worker_fetch_start, &response->head.service_worker_fetch_ready, &response->head.service_worker_fetch_end); } AppCacheInterceptor::GetExtraResponseInfo( request, &response->head.appcache_id, &response->head.appcache_manifest_url); if (info->is_load_timing_enabled()) request->GetLoadTimingInfo(&response->head.load_timing); } } // namespace ResourceLoader::ResourceLoader(scoped_ptr request, scoped_ptr handler, ResourceLoaderDelegate* delegate) : deferred_stage_(DEFERRED_NONE), request_(request.Pass()), handler_(handler.Pass()), delegate_(delegate), last_upload_position_(0), waiting_for_upload_progress_ack_(false), is_transferring_(false), weak_ptr_factory_(this) { request_->set_delegate(this); handler_->SetController(this); } ResourceLoader::~ResourceLoader() { if (login_delegate_.get()) login_delegate_->OnRequestCancelled(); ssl_client_auth_handler_.reset(); // Run ResourceHandler destructor before we tear-down the rest of our state // as the ResourceHandler may want to inspect the URLRequest and other state. handler_.reset(); } void ResourceLoader::StartRequest() { if (delegate_->HandleExternalProtocol(this, request_->url())) { CancelAndIgnore(); return; } // Give the handler a chance to delay the URLRequest from being started. bool defer_start = false; { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::StartRequest")); if (!handler_->OnWillStart(request_->url(), &defer_start)) { Cancel(); return; } } if (defer_start) { deferred_stage_ = DEFERRED_START; } else { StartRequestInternal(); } } void ResourceLoader::CancelRequest(bool from_renderer) { CancelRequestInternal(net::ERR_ABORTED, from_renderer); } void ResourceLoader::CancelAndIgnore() { ResourceRequestInfoImpl* info = GetRequestInfo(); info->set_was_ignored_by_handler(true); CancelRequest(false); } void ResourceLoader::CancelWithError(int error_code) { CancelRequestInternal(error_code, false); } void ResourceLoader::ReportUploadProgress() { if (waiting_for_upload_progress_ack_) return; // Send one progress event at a time. net::UploadProgress progress = request_->GetUploadProgress(); if (!progress.size()) return; // Nothing to upload. if (progress.position() == last_upload_position_) return; // No progress made since last time. const uint64 kHalfPercentIncrements = 200; const TimeDelta kOneSecond = TimeDelta::FromMilliseconds(1000); uint64 amt_since_last = progress.position() - last_upload_position_; TimeDelta time_since_last = TimeTicks::Now() - last_upload_ticks_; bool is_finished = (progress.size() == progress.position()); bool enough_new_progress = (amt_since_last > (progress.size() / kHalfPercentIncrements)); bool too_much_time_passed = time_since_last > kOneSecond; if (is_finished || enough_new_progress || too_much_time_passed) { ResourceRequestInfoImpl* info = GetRequestInfo(); if (info->is_upload_progress_enabled()) { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is // fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::ReportUploadProgress")); handler_->OnUploadProgress(progress.position(), progress.size()); waiting_for_upload_progress_ack_ = true; } last_upload_ticks_ = TimeTicks::Now(); last_upload_position_ = progress.position(); } } void ResourceLoader::MarkAsTransferring() { CHECK(IsResourceTypeFrame(GetRequestInfo()->GetResourceType())) << "Can only transfer for navigations"; is_transferring_ = true; int child_id = GetRequestInfo()->GetChildID(); AppCacheInterceptor::PrepareForCrossSiteTransfer(request(), child_id); ServiceWorkerRequestHandler* handler = ServiceWorkerRequestHandler::GetHandler(request()); if (handler) handler->PrepareForCrossSiteTransfer(child_id); } void ResourceLoader::CompleteTransfer() { // Although CrossSiteResourceHandler defers at OnResponseStarted // (DEFERRED_READ), it may be seeing a replay of events via // BufferedResourceHandler, and so the request itself is actually deferred at // a later read stage. DCHECK(DEFERRED_READ == deferred_stage_ || DEFERRED_RESPONSE_COMPLETE == deferred_stage_); DCHECK(is_transferring_); // In some cases, a process transfer doesn't really happen and the // request is resumed in the original process. Real transfers to a new process // are completed via ResourceDispatcherHostImpl::UpdateRequestForTransfer. int child_id = GetRequestInfo()->GetChildID(); AppCacheInterceptor::MaybeCompleteCrossSiteTransferInOldProcess( request(), child_id); ServiceWorkerRequestHandler* handler = ServiceWorkerRequestHandler::GetHandler(request()); if (handler) handler->MaybeCompleteCrossSiteTransferInOldProcess(child_id); is_transferring_ = false; GetRequestInfo()->cross_site_handler()->ResumeResponse(); } ResourceRequestInfoImpl* ResourceLoader::GetRequestInfo() { return ResourceRequestInfoImpl::ForRequest(request_.get()); } void ResourceLoader::ClearLoginDelegate() { login_delegate_ = NULL; } void ResourceLoader::OnUploadProgressACK() { waiting_for_upload_progress_ack_ = false; } void ResourceLoader::OnReceivedRedirect(net::URLRequest* unused, const net::RedirectInfo& redirect_info, bool* defer) { DCHECK_EQ(request_.get(), unused); VLOG(1) << "OnReceivedRedirect: " << request_->url().spec(); DCHECK(request_->status().is_success()); ResourceRequestInfoImpl* info = GetRequestInfo(); if (info->GetProcessType() != PROCESS_TYPE_PLUGIN && !ChildProcessSecurityPolicyImpl::GetInstance()-> CanRequestURL(info->GetChildID(), redirect_info.new_url)) { VLOG(1) << "Denied unauthorized request for " << redirect_info.new_url.possibly_invalid_spec(); // Tell the renderer that this request was disallowed. Cancel(); return; } delegate_->DidReceiveRedirect(this, redirect_info.new_url); if (delegate_->HandleExternalProtocol(this, redirect_info.new_url)) { // The request is complete so we can remove it. CancelAndIgnore(); return; } scoped_refptr response(new ResourceResponse()); PopulateResourceResponse(info, request_.get(), response.get()); // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnReceivedRedirect")); if (!handler_->OnRequestRedirected(redirect_info, response.get(), defer)) { Cancel(); } else if (*defer) { deferred_stage_ = DEFERRED_REDIRECT; // Follow redirect when resumed. } } void ResourceLoader::OnAuthRequired(net::URLRequest* unused, net::AuthChallengeInfo* auth_info) { DCHECK_EQ(request_.get(), unused); if (request_->load_flags() & net::LOAD_DO_NOT_PROMPT_FOR_LOGIN) { request_->CancelAuth(); return; } // Create a login dialog on the UI thread to get authentication data, or pull // from cache and continue on the IO thread. DCHECK(!login_delegate_.get()) << "OnAuthRequired called with login_delegate pending"; login_delegate_ = delegate_->CreateLoginDelegate(this, auth_info); if (!login_delegate_.get()) request_->CancelAuth(); } void ResourceLoader::OnCertificateRequested( net::URLRequest* unused, net::SSLCertRequestInfo* cert_info) { DCHECK_EQ(request_.get(), unused); if (request_->load_flags() & net::LOAD_PREFETCH) { request_->Cancel(); return; } DCHECK(!ssl_client_auth_handler_) << "OnCertificateRequested called with ssl_client_auth_handler pending"; ssl_client_auth_handler_.reset(new SSLClientAuthHandler( GetRequestInfo()->GetContext()->CreateClientCertStore(), request_.get(), cert_info, base::Bind(&ResourceLoader::ContinueWithCertificate, weak_ptr_factory_.GetWeakPtr()))); ssl_client_auth_handler_->SelectCertificate(); } void ResourceLoader::OnSSLCertificateError(net::URLRequest* request, const net::SSLInfo& ssl_info, bool fatal) { ResourceRequestInfoImpl* info = GetRequestInfo(); int render_process_id; int render_frame_id; if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_id)) NOTREACHED(); SSLManager::OnSSLCertificateError( weak_ptr_factory_.GetWeakPtr(), info->GetResourceType(), request_->url(), render_process_id, render_frame_id, ssl_info, fatal); } void ResourceLoader::OnBeforeNetworkStart(net::URLRequest* unused, bool* defer) { DCHECK_EQ(request_.get(), unused); // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnBeforeNetworkStart")); // Give the handler a chance to delay the URLRequest from using the network. if (!handler_->OnBeforeNetworkStart(request_->url(), defer)) { Cancel(); return; } else if (*defer) { deferred_stage_ = DEFERRED_NETWORK_START; } } void ResourceLoader::OnResponseStarted(net::URLRequest* unused) { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnResponseStarted")); DCHECK_EQ(request_.get(), unused); VLOG(1) << "OnResponseStarted: " << request_->url().spec(); // The CanLoadPage check should take place after any server redirects have // finished, at the point in time that we know a page will commit in the // renderer process. ResourceRequestInfoImpl* info = GetRequestInfo(); ChildProcessSecurityPolicyImpl* policy = ChildProcessSecurityPolicyImpl::GetInstance(); if (!policy->CanLoadPage(info->GetChildID(), request_->url(), info->GetResourceType())) { Cancel(); return; } if (!request_->status().is_success()) { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile1( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnResponseStarted1")); ResponseCompleted(); return; } // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile2( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnResponseStarted2")); // We want to send a final upload progress message prior to sending the // response complete message even if we're waiting for an ack to to a // previous upload progress message. waiting_for_upload_progress_ack_ = false; ReportUploadProgress(); // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile3( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnResponseStarted3")); CompleteResponseStarted(); if (is_deferred()) return; if (request_->status().is_success()) { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile4( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnResponseStarted4")); StartReading(false); // Read the first chunk. } else { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile5( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnResponseStarted5")); ResponseCompleted(); } } void ResourceLoader::OnReadCompleted(net::URLRequest* unused, int bytes_read) { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnReadCompleted")); DCHECK_EQ(request_.get(), unused); VLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\"" << " bytes_read = " << bytes_read; // bytes_read == -1 always implies an error. if (bytes_read == -1 || !request_->status().is_success()) { tracked_objects::ScopedTracker tracking_profile1( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnReadCompleted1")); ResponseCompleted(); return; } tracked_objects::ScopedTracker tracking_profile2( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnReadCompleted2")); CompleteRead(bytes_read); // If the handler cancelled or deferred the request, do not continue // processing the read. If cancelled, the URLRequest has already been // cancelled and will schedule an erroring OnReadCompleted later. If deferred, // do nothing until resumed. // // Note: if bytes_read is 0 (EOF) and the handler defers, resumption will call // ResponseCompleted(). if (is_deferred() || !request_->status().is_success()) return; if (bytes_read > 0) { tracked_objects::ScopedTracker tracking_profile3( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnReadCompleted3")); StartReading(true); // Read the next chunk. } else { tracked_objects::ScopedTracker tracking_profile4( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::OnReadCompleted4")); // URLRequest reported an EOF. Call ResponseCompleted. DCHECK_EQ(0, bytes_read); ResponseCompleted(); } } void ResourceLoader::CancelSSLRequest(int error, const net::SSLInfo* ssl_info) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // The request can be NULL if it was cancelled by the renderer (as the // request of the user navigating to a new page from the location bar). if (!request_->is_pending()) return; DVLOG(1) << "CancelSSLRequest() url: " << request_->url().spec(); if (ssl_info) { request_->CancelWithSSLError(error, *ssl_info); } else { request_->CancelWithError(error); } } void ResourceLoader::ContinueSSLRequest() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DVLOG(1) << "ContinueSSLRequest() url: " << request_->url().spec(); request_->ContinueDespiteLastError(); } void ResourceLoader::Resume() { DCHECK(!is_transferring_); DeferredStage stage = deferred_stage_; deferred_stage_ = DEFERRED_NONE; switch (stage) { case DEFERRED_NONE: NOTREACHED(); break; case DEFERRED_START: StartRequestInternal(); break; case DEFERRED_NETWORK_START: request_->ResumeNetworkStart(); break; case DEFERRED_REDIRECT: request_->FollowDeferredRedirect(); break; case DEFERRED_READ: base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&ResourceLoader::ResumeReading, weak_ptr_factory_.GetWeakPtr())); break; case DEFERRED_RESPONSE_COMPLETE: base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&ResourceLoader::ResponseCompleted, weak_ptr_factory_.GetWeakPtr())); break; case DEFERRED_FINISH: // Delay self-destruction since we don't know how we were reached. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&ResourceLoader::CallDidFinishLoading, weak_ptr_factory_.GetWeakPtr())); break; } } void ResourceLoader::Cancel() { CancelRequest(false); } void ResourceLoader::StartRequestInternal() { DCHECK(!request_->is_pending()); if (!request_->status().is_success()) { return; } request_->Start(); delegate_->DidStartRequest(this); } void ResourceLoader::CancelRequestInternal(int error, bool from_renderer) { VLOG(1) << "CancelRequestInternal: " << request_->url().spec(); ResourceRequestInfoImpl* info = GetRequestInfo(); // WebKit will send us a cancel for downloads since it no longer handles // them. In this case, ignore the cancel since we handle downloads in the // browser. if (from_renderer && (info->IsDownload() || info->is_stream())) return; if (from_renderer && info->detachable_handler()) { // TODO(davidben): Fix Blink handling of prefetches so they are not // cancelled on navigate away and end up in the local cache. info->detachable_handler()->Detach(); return; } // TODO(darin): Perhaps we should really be looking to see if the status is // IO_PENDING? bool was_pending = request_->is_pending(); if (login_delegate_.get()) { login_delegate_->OnRequestCancelled(); login_delegate_ = NULL; } ssl_client_auth_handler_.reset(); request_->CancelWithError(error); if (!was_pending) { // If the request isn't in flight, then we won't get an asynchronous // notification from the request, so we have to signal ourselves to finish // this request. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&ResourceLoader::ResponseCompleted, weak_ptr_factory_.GetWeakPtr())); } } void ResourceLoader::StoreSignedCertificateTimestamps( const net::SignedCertificateTimestampAndStatusList& sct_list, int process_id, SignedCertificateTimestampIDStatusList* sct_ids) { SignedCertificateTimestampStore* sct_store( SignedCertificateTimestampStore::GetInstance()); for (net::SignedCertificateTimestampAndStatusList::const_iterator iter = sct_list.begin(); iter != sct_list.end(); ++iter) { const int sct_id(sct_store->Store(iter->sct.get(), process_id)); sct_ids->push_back( SignedCertificateTimestampIDAndStatus(sct_id, iter->status)); } } void ResourceLoader::CompleteResponseStarted() { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile1( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::CompleteResponseStarted1")); ResourceRequestInfoImpl* info = GetRequestInfo(); // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile2( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::CompleteResponseStarted2")); scoped_refptr response(new ResourceResponse()); PopulateResourceResponse(info, request_.get(), response.get()); if (request_->ssl_info().cert.get()) { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile3( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::CompleteResponseStarted3")); int cert_id = CertStore::GetInstance()->StoreCert( request_->ssl_info().cert.get(), info->GetChildID()); SignedCertificateTimestampIDStatusList signed_certificate_timestamp_ids; StoreSignedCertificateTimestamps( request_->ssl_info().signed_certificate_timestamps, info->GetChildID(), &signed_certificate_timestamp_ids); response->head.security_info = SerializeSecurityInfo( cert_id, request_->ssl_info().cert_status, request_->ssl_info().security_bits, request_->ssl_info().connection_status, signed_certificate_timestamp_ids); } else { // We should not have any SSL state. DCHECK(!request_->ssl_info().cert_status && request_->ssl_info().security_bits == -1 && !request_->ssl_info().connection_status); } // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile5( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::CompleteResponseStarted5")); delegate_->DidReceiveResponse(this); // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::CompleteResponseStarted")); bool defer = false; if (!handler_->OnResponseStarted(response.get(), &defer)) { Cancel(); } else if (defer) { read_deferral_start_time_ = base::TimeTicks::Now(); deferred_stage_ = DEFERRED_READ; // Read first chunk when resumed. } } void ResourceLoader::StartReading(bool is_continuation) { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION("423948 ResourceLoader::StartReading")); int bytes_read = 0; ReadMore(&bytes_read); // If IO is pending, wait for the URLRequest to call OnReadCompleted. if (request_->status().is_io_pending()) return; if (!is_continuation || bytes_read <= 0) { OnReadCompleted(request_.get(), bytes_read); } else { // Else, trigger OnReadCompleted asynchronously to avoid starving the IO // thread in case the URLRequest can provide data synchronously. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&ResourceLoader::OnReadCompleted, weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read)); } } void ResourceLoader::ResumeReading() { DCHECK(!is_deferred()); if (!read_deferral_start_time_.is_null()) { UMA_HISTOGRAM_TIMES("Net.ResourceLoader.ReadDeferral", base::TimeTicks::Now() - read_deferral_start_time_); read_deferral_start_time_ = base::TimeTicks(); } if (request_->status().is_success()) { StartReading(false); // Read the next chunk (OK to complete synchronously). } else { ResponseCompleted(); } } void ResourceLoader::ReadMore(int* bytes_read) { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile1( FROM_HERE_WITH_EXPLICIT_FUNCTION("423948 ResourceLoader::ReadMore1")); DCHECK(!is_deferred()); // Make sure we track the buffer in at least one place. This ensures it gets // deleted even in the case the request has already finished its job and // doesn't use the buffer. scoped_refptr buf; int buf_size; { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile2( FROM_HERE_WITH_EXPLICIT_FUNCTION("423948 ResourceLoader::ReadMore2")); if (!handler_->OnWillRead(&buf, &buf_size, -1)) { Cancel(); return; } } DCHECK(buf.get()); DCHECK(buf_size > 0); request_->Read(buf.get(), buf_size, bytes_read); // No need to check the return value here as we'll detect errors by // inspecting the URLRequest's status. } void ResourceLoader::CompleteRead(int bytes_read) { DCHECK(bytes_read >= 0); DCHECK(request_->status().is_success()); // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION("423948 ResourceLoader::CompleteRead")); bool defer = false; if (!handler_->OnReadCompleted(bytes_read, &defer)) { Cancel(); } else if (defer) { deferred_stage_ = bytes_read > 0 ? DEFERRED_READ : DEFERRED_RESPONSE_COMPLETE; } // Note: the request may still have been cancelled while OnReadCompleted // returns true if OnReadCompleted caused request to get cancelled // out-of-band. (In AwResourceDispatcherHostDelegate::DownloadStarting, for // instance.) } void ResourceLoader::ResponseCompleted() { VLOG(1) << "ResponseCompleted: " << request_->url().spec(); RecordHistograms(); ResourceRequestInfoImpl* info = GetRequestInfo(); std::string security_info; const net::SSLInfo& ssl_info = request_->ssl_info(); if (ssl_info.cert.get() != NULL) { int cert_id = CertStore::GetInstance()->StoreCert(ssl_info.cert.get(), info->GetChildID()); SignedCertificateTimestampIDStatusList signed_certificate_timestamp_ids; StoreSignedCertificateTimestamps(ssl_info.signed_certificate_timestamps, info->GetChildID(), &signed_certificate_timestamp_ids); security_info = SerializeSecurityInfo( cert_id, ssl_info.cert_status, ssl_info.security_bits, ssl_info.connection_status, signed_certificate_timestamp_ids); } bool defer = false; { // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "423948 ResourceLoader::ResponseCompleted")); handler_->OnResponseCompleted(request_->status(), security_info, &defer); } if (defer) { // The handler is not ready to die yet. We will call DidFinishLoading when // we resume. deferred_stage_ = DEFERRED_FINISH; } else { // This will result in our destruction. CallDidFinishLoading(); } } void ResourceLoader::CallDidFinishLoading() { delegate_->DidFinishLoading(this); } void ResourceLoader::RecordHistograms() { ResourceRequestInfoImpl* info = GetRequestInfo(); if (info->GetResourceType() == RESOURCE_TYPE_PREFETCH) { PrefetchStatus status = STATUS_UNDEFINED; TimeDelta total_time = base::TimeTicks::Now() - request_->creation_time(); switch (request_->status().status()) { case net::URLRequestStatus::SUCCESS: if (request_->was_cached()) { status = STATUS_SUCCESS_FROM_CACHE; UMA_HISTOGRAM_TIMES("Net.Prefetch.TimeSpentPrefetchingFromCache", total_time); } else { status = STATUS_SUCCESS_FROM_NETWORK; UMA_HISTOGRAM_TIMES("Net.Prefetch.TimeSpentPrefetchingFromNetwork", total_time); } break; case net::URLRequestStatus::CANCELED: status = STATUS_CANCELED; UMA_HISTOGRAM_TIMES("Net.Prefetch.TimeBeforeCancel", total_time); break; case net::URLRequestStatus::IO_PENDING: case net::URLRequestStatus::FAILED: status = STATUS_UNDEFINED; break; } UMA_HISTOGRAM_ENUMERATION("Net.Prefetch.Pattern", status, STATUS_MAX); } } void ResourceLoader::ContinueWithCertificate(net::X509Certificate* cert) { ssl_client_auth_handler_.reset(); request_->ContinueWithCertificate(cert); } } // namespace content