| // Copyright 2017 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/navigation_url_loader_network_service.h" |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/lazy_instance.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/trace_event/trace_event.h" |
| #include "content/browser/blob_storage/chrome_blob_storage_context.h" |
| #include "content/browser/frame_host/navigation_request_info.h" |
| #include "content/browser/loader/navigation_resource_handler.h" |
| #include "content/browser/loader/navigation_resource_throttle.h" |
| #include "content/browser/loader/navigation_url_loader_delegate.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/global_request_id.h" |
| #include "content/public/browser/navigation_data.h" |
| #include "content/public/browser/navigation_ui_data.h" |
| #include "content/public/browser/ssl_status.h" |
| #include "content/public/browser/stream_handle.h" |
| #include "content/public/common/referrer.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "content/public/common/service_names.mojom.h" |
| #include "net/base/load_flags.h" |
| #include "net/url_request/url_request_context.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| |
| namespace content { |
| |
| namespace { |
| static base::LazyInstance<mojom::URLLoaderFactoryPtr>::Leaky |
| g_url_loader_factory = LAZY_INSTANCE_INITIALIZER; |
| } |
| |
| // This function is called on the IO thread for POST/PUT requests for |
| // attaching blob information to the request body. |
| void HandleRequestsWithBody( |
| std::unique_ptr<ResourceRequest> request, |
| ResourceContext* resource_context, |
| base::WeakPtr<NavigationURLLoaderNetworkService> url_loader) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(request->request_body.get()); |
| |
| AttachRequestBodyBlobDataHandles(request->request_body.get(), |
| resource_context); |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| base::Bind(&NavigationURLLoaderNetworkService::StartURLRequest, |
| url_loader, base::Passed(&request))); |
| } |
| |
| NavigationURLLoaderNetworkService::NavigationURLLoaderNetworkService( |
| ResourceContext* resource_context, |
| StoragePartition* storage_partition, |
| std::unique_ptr<NavigationRequestInfo> request_info, |
| std::unique_ptr<NavigationUIData> navigation_ui_data, |
| ServiceWorkerNavigationHandle* service_worker_handle, |
| AppCacheNavigationHandle* appcache_handle, |
| NavigationURLLoaderDelegate* delegate) |
| : delegate_(delegate), binding_(this), weak_factory_(this) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1( |
| "navigation", "Navigation timeToResponseStarted", this, |
| request_info->common_params.navigation_start, "FrameTreeNode id", |
| request_info->frame_tree_node_id); |
| |
| // TODO(scottmg): Maybe some of this setup should be done only once, instead |
| // of every time. |
| if (g_url_loader_factory.Get().get()) { |
| url_loader_factory_ = std::move(g_url_loader_factory.Get()); |
| } else { |
| ServiceManagerConnection::GetForProcess()->GetConnector()->BindInterface( |
| mojom::kNetworkServiceName, &url_loader_factory_); |
| } |
| |
| // TODO(scottmg): Port over stuff from RDHI::BeginNavigationRequest() here. |
| auto new_request = base::MakeUnique<ResourceRequest>(); |
| |
| new_request->method = request_info->common_params.method; |
| new_request->url = request_info->common_params.url; |
| new_request->first_party_for_cookies = request_info->first_party_for_cookies; |
| new_request->priority = net::HIGHEST; |
| |
| // The code below to set fields like request_initiator, referrer, etc has |
| // been copied from ResourceDispatcherHostImpl. We did not refactor the |
| // common code into a function, because RDHI uses accessor functions on the |
| // URLRequest class to set these fields. whereas we use ResourceRequest here. |
| new_request->request_initiator = request_info->begin_params.initiator_origin; |
| new_request->referrer = request_info->common_params.referrer.url; |
| new_request->referrer_policy = request_info->common_params.referrer.policy; |
| new_request->headers = request_info->begin_params.headers; |
| |
| int load_flags = request_info->begin_params.load_flags; |
| load_flags |= net::LOAD_VERIFY_EV_CERT; |
| if (request_info->is_main_frame) |
| load_flags |= net::LOAD_MAIN_FRAME_DEPRECATED; |
| |
| // Sync loads should have maximum priority and should be the only |
| // requests that have the ignore limits flag set. |
| DCHECK(!(load_flags & net::LOAD_IGNORE_LIMITS)); |
| |
| new_request->load_flags = load_flags; |
| |
| new_request->request_body = request_info->common_params.post_data.get(); |
| if (new_request->request_body.get()) { |
| // The request body may need blob handles to be added to it. This |
| // functionality has to be invoked on the IO thread. |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&HandleRequestsWithBody, |
| base::Passed(&new_request), |
| resource_context, |
| weak_factory_.GetWeakPtr())); |
| return; |
| } |
| |
| StartURLRequest(std::move(new_request)); |
| } |
| |
| NavigationURLLoaderNetworkService::~NavigationURLLoaderNetworkService() {} |
| |
| void NavigationURLLoaderNetworkService::OverrideURLLoaderFactoryForTesting( |
| mojom::URLLoaderFactoryPtr url_loader_factory) { |
| g_url_loader_factory.Get() = std::move(url_loader_factory); |
| } |
| |
| void NavigationURLLoaderNetworkService::FollowRedirect() { |
| url_loader_associated_ptr_->FollowRedirect(); |
| } |
| |
| void NavigationURLLoaderNetworkService::ProceedWithResponse() {} |
| |
| void NavigationURLLoaderNetworkService::OnReceiveResponse( |
| const ResourceResponseHead& head, |
| const base::Optional<net::SSLInfo>& ssl_info, |
| mojom::DownloadedTempFilePtr downloaded_file) { |
| // TODO(scottmg): This needs to do more of what |
| // NavigationResourceHandler::OnReponseStarted() does. Or maybe in |
| // OnStartLoadingResponseBody(). |
| if (ssl_info && ssl_info->cert) |
| NavigationResourceHandler::GetSSLStatusForRequest(*ssl_info, &ssl_status_); |
| response_ = base::MakeShared<ResourceResponse>(); |
| response_->head = head; |
| } |
| |
| void NavigationURLLoaderNetworkService::OnReceiveRedirect( |
| const net::RedirectInfo& redirect_info, |
| const ResourceResponseHead& head) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| scoped_refptr<ResourceResponse> response(new ResourceResponse()); |
| response->head = head; |
| delegate_->OnRequestRedirected(redirect_info, response); |
| } |
| |
| void NavigationURLLoaderNetworkService::OnDataDownloaded( |
| int64_t data_length, |
| int64_t encoded_length) {} |
| |
| void NavigationURLLoaderNetworkService::OnUploadProgress( |
| int64_t current_position, |
| int64_t total_size, |
| OnUploadProgressCallback callback) {} |
| |
| void NavigationURLLoaderNetworkService::OnReceiveCachedMetadata( |
| const std::vector<uint8_t>& data) {} |
| |
| void NavigationURLLoaderNetworkService::OnTransferSizeUpdated( |
| int32_t transfer_size_diff) {} |
| |
| void NavigationURLLoaderNetworkService::OnStartLoadingResponseBody( |
| mojo::ScopedDataPipeConsumerHandle body) { |
| DCHECK(response_); |
| |
| TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this, |
| "&NavigationURLLoaderNetworkService", this, "success", |
| true); |
| |
| // Temporarily, we pass both a stream (null) and the data pipe to the |
| // delegate until PlzNavigate has shipped and we can be comfortable fully |
| // switching to the data pipe. |
| delegate_->OnResponseStarted(response_, nullptr, std::move(body), ssl_status_, |
| std::unique_ptr<NavigationData>(), |
| GlobalRequestID() /* request_id? */, |
| false /* is_download? */, false /* is_stream */); |
| } |
| |
| void NavigationURLLoaderNetworkService::OnComplete( |
| const ResourceRequestCompletionStatus& completion_status) { |
| if (completion_status.error_code != net::OK) { |
| TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", |
| this, "&NavigationURLLoaderNetworkService", this, |
| "success", false); |
| |
| delegate_->OnRequestFailed(completion_status.exists_in_cache, |
| completion_status.error_code); |
| } |
| } |
| |
| void NavigationURLLoaderNetworkService::StartURLRequest( |
| std::unique_ptr<ResourceRequest> request) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| mojom::URLLoaderClientPtr url_loader_client_ptr_to_pass; |
| binding_.Bind(&url_loader_client_ptr_to_pass); |
| |
| url_loader_factory_->CreateLoaderAndStart( |
| mojo::MakeRequest(&url_loader_associated_ptr_), 0 /* routing_id? */, |
| 0 /* request_id? */, mojom::kURLLoadOptionSendSSLInfo, *request, |
| std::move(url_loader_client_ptr_to_pass)); |
| } |
| |
| } // namespace content |