| // 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/appcache/appcache_subresource_url_factory.h" | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/logging.h" | 
 | #include "content/browser/appcache/appcache_host.h" | 
 | #include "content/browser/appcache/appcache_request_handler.h" | 
 | #include "content/browser/appcache/appcache_url_loader_job.h" | 
 | #include "content/browser/appcache/appcache_url_loader_request.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "mojo/public/cpp/bindings/binding.h" | 
 | #include "mojo/public/cpp/bindings/binding_set.h" | 
 | #include "mojo/public/cpp/bindings/interface_ptr.h" | 
 | #include "net/traffic_annotation/network_traffic_annotation.h" | 
 | #include "net/url_request/url_request.h" | 
 | #include "services/network/public/cpp/resource_request.h" | 
 | #include "services/network/public/cpp/resource_response.h" | 
 | #include "services/network/public/cpp/shared_url_loader_factory.h" | 
 | #include "services/network/public/mojom/url_loader_factory.mojom.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | namespace { | 
 |  | 
 | // URLLoader implementation that utilizes either a network loader | 
 | // or an appcache loader depending on where the resources should | 
 | // be loaded from. This class binds to the remote client in the | 
 | // renderer and internally creates one or the other kind of loader. | 
 | // The URLLoader and URLLoaderClient interfaces are proxied between | 
 | // the remote consumer and the chosen internal loader. | 
 | // | 
 | // This class owns and scopes the lifetime of the AppCacheRequestHandler | 
 | // for the duration of a subresource load. | 
 | class SubresourceLoader : public network::mojom::URLLoader, | 
 |                           public network::mojom::URLLoaderClient { | 
 |  public: | 
 |   SubresourceLoader( | 
 |       network::mojom::URLLoaderRequest url_loader_request, | 
 |       int32_t routing_id, | 
 |       int32_t request_id, | 
 |       uint32_t options, | 
 |       const network::ResourceRequest& request, | 
 |       network::mojom::URLLoaderClientPtr client, | 
 |       const net::MutableNetworkTrafficAnnotationTag& annotation, | 
 |       base::WeakPtr<AppCacheHost> appcache_host, | 
 |       scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory) | 
 |       : remote_binding_(this, std::move(url_loader_request)), | 
 |         remote_client_(std::move(client)), | 
 |         request_(request), | 
 |         routing_id_(routing_id), | 
 |         request_id_(request_id), | 
 |         options_(options), | 
 |         traffic_annotation_(annotation), | 
 |         network_loader_factory_(std::move(network_loader_factory)), | 
 |         local_client_binding_(this), | 
 |         host_(appcache_host), | 
 |         weak_factory_(this) { | 
 |     DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
 |     remote_binding_.set_connection_error_handler(base::BindOnce( | 
 |         &SubresourceLoader::OnConnectionError, base::Unretained(this))); | 
 |     base::SequencedTaskRunnerHandle::Get()->PostTask( | 
 |         FROM_HERE, | 
 |         base::BindOnce(&SubresourceLoader::Start, weak_factory_.GetWeakPtr())); | 
 |   } | 
 |  | 
 |  private: | 
 |   ~SubresourceLoader() override {} | 
 |  | 
 |   void OnConnectionError() { delete this; } | 
 |  | 
 |   void Start() { | 
 |     if (!host_) { | 
 |       remote_client_->OnComplete( | 
 |           network::URLLoaderCompletionStatus(net::ERR_FAILED)); | 
 |       return; | 
 |     } | 
 |     handler_ = host_->CreateRequestHandler( | 
 |         AppCacheURLLoaderRequest::Create(request_), | 
 |         static_cast<ResourceType>(request_.resource_type), | 
 |         request_.should_reset_appcache); | 
 |     if (!handler_) { | 
 |       CreateAndStartNetworkLoader(); | 
 |       return; | 
 |     } | 
 |     handler_->MaybeCreateSubresourceLoader( | 
 |         request_, base::BindOnce(&SubresourceLoader::ContinueStart, | 
 |                                  weak_factory_.GetWeakPtr())); | 
 |   } | 
 |  | 
 |   void ContinueStart(SingleRequestURLLoaderFactory::RequestHandler handler) { | 
 |     if (handler) | 
 |       CreateAndStartAppCacheLoader(std::move(handler)); | 
 |     else | 
 |       CreateAndStartNetworkLoader(); | 
 |   } | 
 |  | 
 |   void CreateAndStartAppCacheLoader( | 
 |       SingleRequestURLLoaderFactory::RequestHandler handler) { | 
 |     DCHECK(!appcache_loader_) << "only expected to be called onced"; | 
 |     DCHECK(handler); | 
 |  | 
 |     // Disconnect from the network loader first. | 
 |     local_client_binding_.Close(); | 
 |     network_loader_ = nullptr; | 
 |  | 
 |     network::mojom::URLLoaderClientPtr client_ptr; | 
 |     local_client_binding_.Bind(mojo::MakeRequest(&client_ptr)); | 
 |     std::move(handler).Run(request_, mojo::MakeRequest(&appcache_loader_), | 
 |                            std::move(client_ptr)); | 
 |   } | 
 |  | 
 |   void CreateAndStartNetworkLoader() { | 
 |     DCHECK(!appcache_loader_); | 
 |     network::mojom::URLLoaderClientPtr client_ptr; | 
 |     local_client_binding_.Bind(mojo::MakeRequest(&client_ptr)); | 
 |     network_loader_factory_->CreateLoaderAndStart( | 
 |         mojo::MakeRequest(&network_loader_), routing_id_, request_id_, options_, | 
 |         request_, std::move(client_ptr), traffic_annotation_); | 
 |     if (has_set_priority_) | 
 |       network_loader_->SetPriority(priority_, intra_priority_value_); | 
 |     if (has_paused_reading_) | 
 |       network_loader_->PauseReadingBodyFromNet(); | 
 |   } | 
 |  | 
 |   // network::mojom::URLLoader implementation | 
 |   // Called by the remote client in the renderer. | 
 |   void FollowRedirect(const base::Optional<std::vector<std::string>>& | 
 |                           to_be_removed_request_headers, | 
 |                       const base::Optional<net::HttpRequestHeaders>& | 
 |                           modified_request_headers) override { | 
 |     DCHECK(!modified_request_headers.has_value()) | 
 |         << "Redirect with modified headers was not supported yet. " | 
 |            "crbug.com/845683"; | 
 |     if (!handler_) { | 
 |       network_loader_->FollowRedirect(base::nullopt, base::nullopt); | 
 |       return; | 
 |     } | 
 |     DCHECK(network_loader_); | 
 |     DCHECK(!appcache_loader_); | 
 |     handler_->MaybeFollowSubresourceRedirect( | 
 |         redirect_info_, | 
 |         base::BindOnce(&SubresourceLoader::ContinueFollowRedirect, | 
 |                        weak_factory_.GetWeakPtr())); | 
 |   } | 
 |  | 
 |   // network::mojom::URLLoader implementation | 
 |   void ProceedWithResponse() override { NOTREACHED(); } | 
 |  | 
 |   void ContinueFollowRedirect( | 
 |       SingleRequestURLLoaderFactory::RequestHandler handler) { | 
 |     if (handler) | 
 |       CreateAndStartAppCacheLoader(std::move(handler)); | 
 |     else | 
 |       network_loader_->FollowRedirect(base::nullopt, base::nullopt); | 
 |   } | 
 |  | 
 |   void SetPriority(net::RequestPriority priority, | 
 |                    int32_t intra_priority_value) override { | 
 |     has_set_priority_ = true; | 
 |     priority_ = priority; | 
 |     intra_priority_value_ = intra_priority_value; | 
 |     if (network_loader_) | 
 |       network_loader_->SetPriority(priority, intra_priority_value); | 
 |   } | 
 |  | 
 |   void PauseReadingBodyFromNet() override { | 
 |     has_paused_reading_ = true; | 
 |     if (network_loader_) | 
 |       network_loader_->PauseReadingBodyFromNet(); | 
 |   } | 
 |  | 
 |   void ResumeReadingBodyFromNet() override { | 
 |     has_paused_reading_ = false; | 
 |     if (network_loader_) | 
 |       network_loader_->ResumeReadingBodyFromNet(); | 
 |   } | 
 |  | 
 |   // network::mojom::URLLoaderClient implementation | 
 |   // Called by either the appcache or network loader, whichever is in use. | 
 |   void OnReceiveResponse( | 
 |       const network::ResourceResponseHead& response_head) override { | 
 |     // Don't MaybeFallback for appcache produced responses. | 
 |     if (appcache_loader_ || !handler_) { | 
 |       remote_client_->OnReceiveResponse(response_head); | 
 |       return; | 
 |     } | 
 |  | 
 |     did_receive_network_response_ = true; | 
 |     handler_->MaybeFallbackForSubresourceResponse( | 
 |         response_head, | 
 |         base::BindOnce(&SubresourceLoader::ContinueOnReceiveResponse, | 
 |                        weak_factory_.GetWeakPtr(), response_head)); | 
 |   } | 
 |  | 
 |   void ContinueOnReceiveResponse( | 
 |       const network::ResourceResponseHead& response_head, | 
 |       SingleRequestURLLoaderFactory::RequestHandler handler) { | 
 |     if (handler) { | 
 |       CreateAndStartAppCacheLoader(std::move(handler)); | 
 |     } else { | 
 |       remote_client_->OnReceiveResponse(response_head); | 
 |     } | 
 |   } | 
 |  | 
 |   void OnReceiveRedirect( | 
 |       const net::RedirectInfo& redirect_info, | 
 |       const network::ResourceResponseHead& response_head) override { | 
 |     DCHECK(network_loader_) << "appcache loader does not produce redirects"; | 
 |     if (!redirect_limit_--) { | 
 |       OnComplete( | 
 |           network::URLLoaderCompletionStatus(net::ERR_TOO_MANY_REDIRECTS)); | 
 |       return; | 
 |     } | 
 |     if (!handler_) { | 
 |       remote_client_->OnReceiveRedirect(redirect_info_, response_head); | 
 |       return; | 
 |     } | 
 |     redirect_info_ = redirect_info; | 
 |     handler_->MaybeFallbackForSubresourceRedirect( | 
 |         redirect_info, | 
 |         base::BindOnce(&SubresourceLoader::ContinueOnReceiveRedirect, | 
 |                        weak_factory_.GetWeakPtr(), response_head)); | 
 |   } | 
 |  | 
 |   void ContinueOnReceiveRedirect( | 
 |       const network::ResourceResponseHead& response_head, | 
 |       SingleRequestURLLoaderFactory::RequestHandler handler) { | 
 |     if (handler) | 
 |       CreateAndStartAppCacheLoader(std::move(handler)); | 
 |     else | 
 |       remote_client_->OnReceiveRedirect(redirect_info_, response_head); | 
 |   } | 
 |  | 
 |   void OnUploadProgress(int64_t current_position, | 
 |                         int64_t total_size, | 
 |                         OnUploadProgressCallback ack_callback) override { | 
 |     remote_client_->OnUploadProgress(current_position, total_size, | 
 |                                      std::move(ack_callback)); | 
 |   } | 
 |  | 
 |   void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override { | 
 |     remote_client_->OnReceiveCachedMetadata(data); | 
 |   } | 
 |  | 
 |   void OnTransferSizeUpdated(int32_t transfer_size_diff) override { | 
 |     remote_client_->OnTransferSizeUpdated(transfer_size_diff); | 
 |   } | 
 |  | 
 |   void OnStartLoadingResponseBody( | 
 |       mojo::ScopedDataPipeConsumerHandle body) override { | 
 |     remote_client_->OnStartLoadingResponseBody(std::move(body)); | 
 |   } | 
 |  | 
 |   void OnComplete(const network::URLLoaderCompletionStatus& status) override { | 
 |     if (!network_loader_ || !handler_ || did_receive_network_response_ || | 
 |         status.error_code == net::OK) { | 
 |       remote_client_->OnComplete(status); | 
 |       return; | 
 |     } | 
 |     handler_->MaybeFallbackForSubresourceResponse( | 
 |         network::ResourceResponseHead(), | 
 |         base::BindOnce(&SubresourceLoader::ContinueOnComplete, | 
 |                        weak_factory_.GetWeakPtr(), status)); | 
 |   } | 
 |  | 
 |   void ContinueOnComplete( | 
 |       const network::URLLoaderCompletionStatus& status, | 
 |       SingleRequestURLLoaderFactory::RequestHandler handler) { | 
 |     if (handler) | 
 |       CreateAndStartAppCacheLoader(std::move(handler)); | 
 |     else | 
 |       remote_client_->OnComplete(status); | 
 |   } | 
 |  | 
 |   // The binding and client pointer associated with the renderer. | 
 |   mojo::Binding<network::mojom::URLLoader> remote_binding_; | 
 |   network::mojom::URLLoaderClientPtr remote_client_; | 
 |  | 
 |   network::ResourceRequest request_; | 
 |   int32_t routing_id_; | 
 |   int32_t request_id_; | 
 |   uint32_t options_; | 
 |   net::MutableNetworkTrafficAnnotationTag traffic_annotation_; | 
 |   scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory_; | 
 |   net::RedirectInfo redirect_info_; | 
 |   int redirect_limit_ = net::URLRequest::kMaxRedirects; | 
 |   bool did_receive_network_response_ = false; | 
 |   bool has_paused_reading_ = false; | 
 |   bool has_set_priority_ = false; | 
 |   net::RequestPriority priority_; | 
 |   int32_t intra_priority_value_; | 
 |  | 
 |   // Core appcache logic that decides how to handle a request. | 
 |   std::unique_ptr<AppCacheRequestHandler> handler_; | 
 |  | 
 |   // The local binding to either our network or appcache loader, | 
 |   // we only use one of them at any given time. | 
 |   mojo::Binding<network::mojom::URLLoaderClient> local_client_binding_; | 
 |   network::mojom::URLLoaderPtr network_loader_; | 
 |   network::mojom::URLLoaderPtr appcache_loader_; | 
 |  | 
 |   base::WeakPtr<AppCacheHost> host_; | 
 |  | 
 |   base::WeakPtrFactory<SubresourceLoader> weak_factory_; | 
 |   DISALLOW_COPY_AND_ASSIGN(SubresourceLoader); | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | // Implements the URLLoaderFactory mojom for AppCache requests. | 
 | AppCacheSubresourceURLFactory::AppCacheSubresourceURLFactory( | 
 |     scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory, | 
 |     base::WeakPtr<AppCacheHost> host) | 
 |     : network_loader_factory_(std::move(network_loader_factory)), | 
 |       appcache_host_(host), | 
 |       weak_factory_(this) { | 
 |   bindings_.set_connection_error_handler( | 
 |       base::Bind(&AppCacheSubresourceURLFactory::OnConnectionError, | 
 |                  base::Unretained(this))); | 
 | } | 
 |  | 
 | AppCacheSubresourceURLFactory::~AppCacheSubresourceURLFactory() {} | 
 |  | 
 | // static | 
 | void AppCacheSubresourceURLFactory::CreateURLLoaderFactory( | 
 |     scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory, | 
 |     base::WeakPtr<AppCacheHost> host, | 
 |     network::mojom::URLLoaderFactoryPtr* loader_factory) { | 
 |   DCHECK(host.get()); | 
 |   // This instance is effectively reference counted by the number of pipes open | 
 |   // to it and will get deleted when all clients drop their connections. | 
 |   // Please see OnConnectionError() for details. | 
 |   auto* impl = new AppCacheSubresourceURLFactory( | 
 |       std::move(network_loader_factory), host); | 
 |   impl->Clone(mojo::MakeRequest(loader_factory)); | 
 |  | 
 |   // Save the factory in the host to ensure that we don't create it again when | 
 |   // the cache is selected, etc. | 
 |   host->SetAppCacheSubresourceFactory(impl); | 
 | } | 
 |  | 
 | void AppCacheSubresourceURLFactory::CreateLoaderAndStart( | 
 |     network::mojom::URLLoaderRequest url_loader_request, | 
 |     int32_t routing_id, | 
 |     int32_t request_id, | 
 |     uint32_t options, | 
 |     const network::ResourceRequest& request, | 
 |     network::mojom::URLLoaderClientPtr client, | 
 |     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { | 
 |   DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
 |   new SubresourceLoader(std::move(url_loader_request), routing_id, request_id, | 
 |                         options, request, std::move(client), traffic_annotation, | 
 |                         appcache_host_, network_loader_factory_); | 
 | } | 
 |  | 
 | void AppCacheSubresourceURLFactory::Clone( | 
 |     network::mojom::URLLoaderFactoryRequest request) { | 
 |   bindings_.AddBinding(this, std::move(request)); | 
 | } | 
 |  | 
 | base::WeakPtr<AppCacheSubresourceURLFactory> | 
 | AppCacheSubresourceURLFactory::GetWeakPtr() { | 
 |   return weak_factory_.GetWeakPtr(); | 
 | } | 
 |  | 
 | void AppCacheSubresourceURLFactory::OnConnectionError() { | 
 |   if (bindings_.empty()) | 
 |     delete this; | 
 | } | 
 |  | 
 | }  // namespace content |