1 // Copyright 2017 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/appcache/appcache_subresource_url_factory.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/check_op.h"
12 #include "base/debug/crash_logging.h"
13 #include "content/browser/appcache/appcache_host.h"
14 #include "content/browser/appcache/appcache_request.h"
15 #include "content/browser/appcache/appcache_request_handler.h"
16 #include "content/browser/loader/navigation_url_loader_impl.h"
17 #include "content/browser/storage_partition_impl.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/content_browser_client.h"
20 #include "content/public/common/content_client.h"
21 #include "mojo/public/cpp/bindings/message.h"
22 #include "mojo/public/cpp/bindings/receiver.h"
23 #include "mojo/public/cpp/bindings/remote.h"
24 #include "net/traffic_annotation/network_traffic_annotation.h"
25 #include "services/network/public/cpp/request_mode.h"
26 #include "services/network/public/cpp/resource_request.h"
27 #include "services/network/public/cpp/shared_url_loader_factory.h"
28 #include "services/network/public/mojom/url_loader_factory.mojom.h"
29 #include "services/network/public/mojom/url_response_head.mojom.h"
30 
31 namespace content {
32 
33 namespace {
34 
35 // URLLoader implementation that utilizes either a network loader
36 // or an appcache loader depending on where the resources should
37 // be loaded from. This class binds to the remote client in the
38 // renderer and internally creates one or the other kind of loader.
39 // The URLLoader and URLLoaderClient interfaces are proxied between
40 // the remote consumer and the chosen internal loader.
41 //
42 // This class owns and scopes the lifetime of the AppCacheRequestHandler
43 // for the duration of a subresource load.
44 class SubresourceLoader : public network::mojom::URLLoader,
45                           public network::mojom::URLLoaderClient {
46  public:
SubresourceLoader(mojo::PendingReceiver<network::mojom::URLLoader> url_loader_receiver,int32_t routing_id,int32_t request_id,uint32_t options,const network::ResourceRequest & request,mojo::PendingRemote<network::mojom::URLLoaderClient> client,const net::MutableNetworkTrafficAnnotationTag & annotation,base::WeakPtr<AppCacheHost> appcache_host,scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory)47   SubresourceLoader(
48       mojo::PendingReceiver<network::mojom::URLLoader> url_loader_receiver,
49       int32_t routing_id,
50       int32_t request_id,
51       uint32_t options,
52       const network::ResourceRequest& request,
53       mojo::PendingRemote<network::mojom::URLLoaderClient> client,
54       const net::MutableNetworkTrafficAnnotationTag& annotation,
55       base::WeakPtr<AppCacheHost> appcache_host,
56       scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory)
57       : remote_receiver_(this, std::move(url_loader_receiver)),
58         remote_client_(std::move(client)),
59         request_(request),
60         routing_id_(routing_id),
61         request_id_(request_id),
62         options_(options),
63         traffic_annotation_(annotation),
64         network_loader_factory_(std::move(network_loader_factory)),
65         host_(appcache_host) {
66     DCHECK_CURRENTLY_ON(BrowserThread::UI);
67     remote_receiver_.set_disconnect_handler(base::BindOnce(
68         &SubresourceLoader::OnMojoDisconnect, base::Unretained(this)));
69     base::SequencedTaskRunnerHandle::Get()->PostTask(
70         FROM_HERE,
71         base::BindOnce(&SubresourceLoader::Start, weak_factory_.GetWeakPtr()));
72   }
73 
74  private:
75   ~SubresourceLoader() override = default;
76 
OnMojoDisconnect()77   void OnMojoDisconnect() { delete this; }
78 
Start()79   void Start() {
80     if (!host_) {
81       remote_client_->OnComplete(
82           network::URLLoaderCompletionStatus(net::ERR_FAILED));
83       return;
84     }
85     handler_ = host_->CreateRequestHandler(
86         std::make_unique<AppCacheRequest>(request_), request_.destination,
87         request_.should_reset_appcache);
88     if (!handler_) {
89       CreateAndStartNetworkLoader();
90       return;
91     }
92     handler_->MaybeCreateSubresourceLoader(
93         request_, base::BindOnce(&SubresourceLoader::ContinueStart,
94                                  weak_factory_.GetWeakPtr()));
95   }
96 
ContinueStart(SingleRequestURLLoaderFactory::RequestHandler handler)97   void ContinueStart(SingleRequestURLLoaderFactory::RequestHandler handler) {
98     if (handler)
99       CreateAndStartAppCacheLoader(std::move(handler));
100     else
101       CreateAndStartNetworkLoader();
102   }
103 
CreateAndStartAppCacheLoader(SingleRequestURLLoaderFactory::RequestHandler handler)104   void CreateAndStartAppCacheLoader(
105       SingleRequestURLLoaderFactory::RequestHandler handler) {
106     DCHECK(!appcache_loader_) << "only expected to be called onced";
107     DCHECK(handler);
108 
109     // Disconnect from the network loader first.
110     local_client_receiver_.reset();
111     network_loader_.reset();
112 
113     std::move(handler).Run(request_,
114                            appcache_loader_.BindNewPipeAndPassReceiver(),
115                            local_client_receiver_.BindNewPipeAndPassRemote());
116   }
117 
CreateAndStartNetworkLoader()118   void CreateAndStartNetworkLoader() {
119     DCHECK(!appcache_loader_);
120     network_loader_factory_->CreateLoaderAndStart(
121         network_loader_.BindNewPipeAndPassReceiver(), routing_id_, request_id_,
122         options_, request_, local_client_receiver_.BindNewPipeAndPassRemote(),
123         traffic_annotation_);
124     if (has_set_priority_)
125       network_loader_->SetPriority(priority_, intra_priority_value_);
126     if (has_paused_reading_)
127       network_loader_->PauseReadingBodyFromNet();
128   }
129 
130   // network::mojom::URLLoader implementation
131   // Called by the remote client in the renderer.
FollowRedirect(const std::vector<std::string> & removed_headers,const net::HttpRequestHeaders & modified_headers,const net::HttpRequestHeaders & modified_cors_exempt_headers,const base::Optional<GURL> & new_url)132   void FollowRedirect(
133       const std::vector<std::string>& removed_headers,
134       const net::HttpRequestHeaders& modified_headers,
135       const net::HttpRequestHeaders& modified_cors_exempt_headers,
136       const base::Optional<GURL>& new_url) override {
137     DCHECK(modified_headers.IsEmpty() && modified_cors_exempt_headers.IsEmpty())
138         << "Redirect with modified headers was not supported yet. "
139            "crbug.com/845683";
140     if (!handler_) {
141       network_loader_->FollowRedirect(
142           removed_headers, {} /* modified_headers */,
143           {} /* modified_cors_exempt_headers */, base::nullopt /* new_url */);
144       return;
145     }
146     DCHECK(network_loader_);
147     DCHECK(!appcache_loader_);
148     handler_->MaybeFollowSubresourceRedirect(
149         redirect_info_,
150         base::BindOnce(&SubresourceLoader::ContinueFollowRedirect,
151                        weak_factory_.GetWeakPtr()));
152   }
153 
154   // network::mojom::URLLoader implementation
ContinueFollowRedirect(SingleRequestURLLoaderFactory::RequestHandler handler)155   void ContinueFollowRedirect(
156       SingleRequestURLLoaderFactory::RequestHandler handler) {
157     if (handler) {
158       CreateAndStartAppCacheLoader(std::move(handler));
159     } else {
160       network_loader_->FollowRedirect(
161           {} /* removed_headers */, {} /* modified_headers */,
162           {} /* modified_cors_exempt_headers */, base::nullopt /* new_url */);
163     }
164   }
165 
SetPriority(net::RequestPriority priority,int32_t intra_priority_value)166   void SetPriority(net::RequestPriority priority,
167                    int32_t intra_priority_value) override {
168     has_set_priority_ = true;
169     priority_ = priority;
170     intra_priority_value_ = intra_priority_value;
171     if (network_loader_)
172       network_loader_->SetPriority(priority, intra_priority_value);
173   }
174 
PauseReadingBodyFromNet()175   void PauseReadingBodyFromNet() override {
176     has_paused_reading_ = true;
177     if (network_loader_)
178       network_loader_->PauseReadingBodyFromNet();
179   }
180 
ResumeReadingBodyFromNet()181   void ResumeReadingBodyFromNet() override {
182     has_paused_reading_ = false;
183     if (network_loader_)
184       network_loader_->ResumeReadingBodyFromNet();
185   }
186 
187   // network::mojom::URLLoaderClient implementation
188   // Called by either the appcache or network loader, whichever is in use.
OnReceiveResponse(network::mojom::URLResponseHeadPtr response_head)189   void OnReceiveResponse(
190       network::mojom::URLResponseHeadPtr response_head) override {
191     // Don't MaybeFallback for appcache produced responses.
192     if (appcache_loader_ || !handler_) {
193       remote_client_->OnReceiveResponse(std::move(response_head));
194       return;
195     }
196 
197     did_receive_network_response_ = true;
198     auto response_head_clone = response_head.Clone();
199     handler_->MaybeFallbackForSubresourceResponse(
200         std::move(response_head),
201         base::BindOnce(&SubresourceLoader::ContinueOnReceiveResponse,
202                        weak_factory_.GetWeakPtr(),
203                        std::move(response_head_clone)));
204   }
205 
ContinueOnReceiveResponse(network::mojom::URLResponseHeadPtr response_head,SingleRequestURLLoaderFactory::RequestHandler handler)206   void ContinueOnReceiveResponse(
207       network::mojom::URLResponseHeadPtr response_head,
208       SingleRequestURLLoaderFactory::RequestHandler handler) {
209     if (handler) {
210       CreateAndStartAppCacheLoader(std::move(handler));
211     } else {
212       remote_client_->OnReceiveResponse(std::move(response_head));
213     }
214   }
215 
OnReceiveRedirect(const net::RedirectInfo & redirect_info,network::mojom::URLResponseHeadPtr response_head)216   void OnReceiveRedirect(
217       const net::RedirectInfo& redirect_info,
218       network::mojom::URLResponseHeadPtr response_head) override {
219     DCHECK(network_loader_) << "appcache loader does not produce redirects";
220     if (!redirect_limit_--) {
221       OnComplete(
222           network::URLLoaderCompletionStatus(net::ERR_TOO_MANY_REDIRECTS));
223       return;
224     }
225     if (!handler_) {
226       remote_client_->OnReceiveRedirect(redirect_info_,
227                                         std::move(response_head));
228       return;
229     }
230     redirect_info_ = redirect_info;
231     handler_->MaybeFallbackForSubresourceRedirect(
232         redirect_info,
233         base::BindOnce(&SubresourceLoader::ContinueOnReceiveRedirect,
234                        weak_factory_.GetWeakPtr(), std::move(response_head)));
235   }
236 
ContinueOnReceiveRedirect(network::mojom::URLResponseHeadPtr response_head,SingleRequestURLLoaderFactory::RequestHandler handler)237   void ContinueOnReceiveRedirect(
238       network::mojom::URLResponseHeadPtr response_head,
239       SingleRequestURLLoaderFactory::RequestHandler handler) {
240     if (handler) {
241       CreateAndStartAppCacheLoader(std::move(handler));
242     } else {
243       remote_client_->OnReceiveRedirect(redirect_info_,
244                                         std::move(response_head));
245     }
246   }
247 
OnUploadProgress(int64_t current_position,int64_t total_size,OnUploadProgressCallback ack_callback)248   void OnUploadProgress(int64_t current_position,
249                         int64_t total_size,
250                         OnUploadProgressCallback ack_callback) override {
251     remote_client_->OnUploadProgress(current_position, total_size,
252                                      std::move(ack_callback));
253   }
254 
OnReceiveCachedMetadata(mojo_base::BigBuffer data)255   void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override {
256     remote_client_->OnReceiveCachedMetadata(std::move(data));
257   }
258 
OnTransferSizeUpdated(int32_t transfer_size_diff)259   void OnTransferSizeUpdated(int32_t transfer_size_diff) override {
260     remote_client_->OnTransferSizeUpdated(transfer_size_diff);
261   }
262 
OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body)263   void OnStartLoadingResponseBody(
264       mojo::ScopedDataPipeConsumerHandle body) override {
265     remote_client_->OnStartLoadingResponseBody(std::move(body));
266   }
267 
OnComplete(const network::URLLoaderCompletionStatus & status)268   void OnComplete(const network::URLLoaderCompletionStatus& status) override {
269     if (!network_loader_ || !handler_ || did_receive_network_response_ ||
270         status.error_code == net::OK) {
271       remote_client_->OnComplete(status);
272       return;
273     }
274     handler_->MaybeFallbackForSubresourceResponse(
275         network::mojom::URLResponseHead::New(),
276         base::BindOnce(&SubresourceLoader::ContinueOnComplete,
277                        weak_factory_.GetWeakPtr(), status));
278   }
279 
ContinueOnComplete(const network::URLLoaderCompletionStatus & status,SingleRequestURLLoaderFactory::RequestHandler handler)280   void ContinueOnComplete(
281       const network::URLLoaderCompletionStatus& status,
282       SingleRequestURLLoaderFactory::RequestHandler handler) {
283     if (handler)
284       CreateAndStartAppCacheLoader(std::move(handler));
285     else
286       remote_client_->OnComplete(status);
287   }
288 
289   // The receiver and remote client associated with the renderer.
290   mojo::Receiver<network::mojom::URLLoader> remote_receiver_;
291   mojo::Remote<network::mojom::URLLoaderClient> remote_client_;
292 
293   network::ResourceRequest request_;
294   int32_t routing_id_;
295   int32_t request_id_;
296   uint32_t options_;
297   net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
298   scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory_;
299   net::RedirectInfo redirect_info_;
300   int redirect_limit_ = net::URLRequest::kMaxRedirects;
301   bool did_receive_network_response_ = false;
302   bool has_paused_reading_ = false;
303   bool has_set_priority_ = false;
304   net::RequestPriority priority_;
305   int32_t intra_priority_value_;
306 
307   // Core appcache logic that decides how to handle a request.
308   std::unique_ptr<AppCacheRequestHandler> handler_;
309 
310   // The local receiver to either our network or appcache loader,
311   // we only use one of them at any given time.
312   mojo::Receiver<network::mojom::URLLoaderClient> local_client_receiver_{this};
313   mojo::Remote<network::mojom::URLLoader> network_loader_;
314   mojo::Remote<network::mojom::URLLoader> appcache_loader_;
315 
316   base::WeakPtr<AppCacheHost> host_;
317 
318   base::WeakPtrFactory<SubresourceLoader> weak_factory_{this};
319   DISALLOW_COPY_AND_ASSIGN(SubresourceLoader);
320 };
321 
322 }  // namespace
323 
324 // Implements the URLLoaderFactory mojom for AppCache requests.
AppCacheSubresourceURLFactory(scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory,base::WeakPtr<AppCacheHost> host)325 AppCacheSubresourceURLFactory::AppCacheSubresourceURLFactory(
326     scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory,
327     base::WeakPtr<AppCacheHost> host)
328     : network_loader_factory_(std::move(network_loader_factory)),
329       appcache_host_(host) {
330   receivers_.set_disconnect_handler(
331       base::BindRepeating(&AppCacheSubresourceURLFactory::OnMojoDisconnect,
332                           base::Unretained(this)));
333 }
334 
335 AppCacheSubresourceURLFactory::~AppCacheSubresourceURLFactory() = default;
336 
337 // static
CreateURLLoaderFactory(base::WeakPtr<AppCacheHost> host,mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_factory_receiver)338 bool AppCacheSubresourceURLFactory::CreateURLLoaderFactory(
339     base::WeakPtr<AppCacheHost> host,
340     mojo::PendingReceiver<network::mojom::URLLoaderFactory>
341         loader_factory_receiver) {
342   DCHECK(host.get());
343   scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory;
344   // The partition has shutdown, return without binding |loader_factory|.
345   if (!host->service()->partition())
346     return false;
347   network_loader_factory =
348       host->service()
349           ->partition()
350           ->GetURLLoaderFactoryForBrowserProcessWithCORBEnabled();
351   // This instance is effectively reference counted by the number of pipes open
352   // to it and will get deleted when all clients drop their connections.
353   // Please see OnMojoDisconnect() for details.
354   auto* impl = new AppCacheSubresourceURLFactory(
355       std::move(network_loader_factory), host);
356   impl->Clone(std::move(loader_factory_receiver));
357 
358   // Save the factory in the host to ensure that we don't create it again when
359   // the cache is selected, etc.
360   host->SetAppCacheSubresourceFactory(impl);
361   return true;
362 }
363 
CreateLoaderAndStart(mojo::PendingReceiver<network::mojom::URLLoader> url_loader_receiver,int32_t routing_id,int32_t request_id,uint32_t options,const network::ResourceRequest & request,mojo::PendingRemote<network::mojom::URLLoaderClient> client,const net::MutableNetworkTrafficAnnotationTag & traffic_annotation)364 void AppCacheSubresourceURLFactory::CreateLoaderAndStart(
365     mojo::PendingReceiver<network::mojom::URLLoader> url_loader_receiver,
366     int32_t routing_id,
367     int32_t request_id,
368     uint32_t options,
369     const network::ResourceRequest& request,
370     mojo::PendingRemote<network::mojom::URLLoaderClient> client,
371     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
372   DCHECK_CURRENTLY_ON(BrowserThread::UI);
373 
374   if (request.request_initiator.has_value() && appcache_host_ &&
375       !appcache_host_->security_policy_handle()->CanAccessDataForOrigin(
376           request.request_initiator.value())) {
377     static auto* initiator_origin_key = base::debug::AllocateCrashKeyString(
378         "initiator_origin", base::debug::CrashKeySize::Size64);
379     base::debug::SetCrashKeyString(
380         initiator_origin_key, request.request_initiator.value().Serialize());
381 
382     mojo::ReportBadMessage(
383         "APPCACHE_SUBRESOURCE_URL_FACTORY_INVALID_INITIATOR");
384     return;
385   }
386 
387   // Subresource requests from renderer processes should not be allowed to use
388   // network::mojom::FetchRequestMode::kNavigate.
389   if (request.mode == network::mojom::RequestMode::kNavigate) {
390     mojo::ReportBadMessage("APPCACHE_SUBRESOURCE_URL_FACTORY_NAVIGATE");
391     return;
392   }
393 
394   new SubresourceLoader(std::move(url_loader_receiver), routing_id, request_id,
395                         options, request, std::move(client), traffic_annotation,
396                         appcache_host_, network_loader_factory_);
397 }
398 
Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)399 void AppCacheSubresourceURLFactory::Clone(
400     mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
401   receivers_.Add(this, std::move(receiver));
402 }
403 
404 base::WeakPtr<AppCacheSubresourceURLFactory>
GetWeakPtr()405 AppCacheSubresourceURLFactory::GetWeakPtr() {
406   return weak_factory_.GetWeakPtr();
407 }
408 
OnMojoDisconnect()409 void AppCacheSubresourceURLFactory::OnMojoDisconnect() {
410   if (receivers_.empty())
411     delete this;
412 }
413 
414 }  // namespace content
415