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