1 // Copyright 2020 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_url_loader.h"
6 
7 #include "base/bind.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "content/browser/appcache/appcache_disk_cache_ops.h"
10 #include "content/browser/appcache/appcache_histograms.h"
11 #include "content/browser/appcache/appcache_request.h"
12 #include "content/browser/appcache/appcache_request_handler.h"
13 #include "content/browser/appcache/appcache_response_info.h"
14 #include "content/browser/appcache/appcache_subresource_url_factory.h"
15 #include "content/browser/url_loader_factory_getter.h"
16 #include "net/base/ip_endpoint.h"
17 #include "net/http/http_request_headers.h"
18 #include "net/http/http_response_headers.h"
19 #include "net/http/http_status_code.h"
20 #include "net/http/http_util.h"
21 #include "services/network/public/cpp/net_adapters.h"
22 #include "third_party/blink/public/common/loader/resource_type_util.h"
23 #include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
24 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
25 
26 namespace content {
27 
~AppCacheURLLoader()28 AppCacheURLLoader::~AppCacheURLLoader() {
29   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
30 
31   if (storage_.get())
32     storage_->CancelDelegateCallbacks(this);
33 }
34 
InitializeRangeRequestInfo(const net::HttpRequestHeaders & headers)35 void AppCacheURLLoader::InitializeRangeRequestInfo(
36     const net::HttpRequestHeaders& headers) {
37   std::string value;
38   std::vector<net::HttpByteRange> ranges;
39   if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &value) ||
40       !net::HttpUtil::ParseRangeHeader(value, &ranges)) {
41     return;
42   }
43 
44   // If multiple ranges are requested, we play dumb and
45   // return the entire response with 200 OK.
46   if (ranges.size() == 1U)
47     range_requested_ = ranges[0];
48 }
49 
SetupRangeResponse()50 void AppCacheURLLoader::SetupRangeResponse() {
51   DCHECK(is_range_request() && reader_.get() && IsDeliveringAppCacheResponse());
52   int resource_size = static_cast<int>(info_->response_data_size());
53   if (resource_size < 0 || !range_requested_.ComputeBounds(resource_size)) {
54     range_requested_ = net::HttpByteRange();
55     return;
56   }
57 
58   DCHECK(range_requested_.IsValid());
59   int offset = static_cast<int>(range_requested_.first_byte_position());
60   int length = static_cast<int>(range_requested_.last_byte_position() -
61                                 range_requested_.first_byte_position() + 1);
62 
63   // Tell the reader about the range to read.
64   reader_->SetReadRange(offset, length);
65 
66   // Make a copy of the full response headers and fix them up
67   // for the range we'll be returning.
68   range_response_info_ =
69       std::make_unique<net::HttpResponseInfo>(info_->http_response_info());
70   net::HttpResponseHeaders* headers = range_response_info_->headers.get();
71   headers->UpdateWithNewRange(range_requested_, resource_size,
72                               /*replace_status_line=*/true);
73 }
74 
IsStarted() const75 bool AppCacheURLLoader::IsStarted() const {
76   return delivery_type_ != DeliveryType::kAwaitingDeliverCall &&
77          delivery_type_ != DeliveryType::kNetwork;
78 }
79 
DeliverAppCachedResponse(const GURL & manifest_url,int64_t cache_id,const AppCacheEntry & entry,bool is_fallback)80 void AppCacheURLLoader::DeliverAppCachedResponse(const GURL& manifest_url,
81                                                  int64_t cache_id,
82                                                  const AppCacheEntry& entry,
83                                                  bool is_fallback) {
84   if (!storage_.get() || !appcache_request_) {
85     DeliverErrorResponse();
86     return;
87   }
88 
89   delivery_type_ = DeliveryType::kAppCached;
90 
91   // In tests we only care about the delivery_type_ state.
92   if (AppCacheRequestHandler::IsRunningInTests())
93     return;
94 
95   load_timing_info_.request_start_time = base::Time::Now();
96   load_timing_info_.request_start = base::TimeTicks::Now();
97 
98   manifest_url_ = manifest_url;
99   cache_id_ = cache_id;
100   entry_ = entry;
101   is_fallback_ = is_fallback;
102 
103   if (is_fallback_ && loader_callback_)
104     CallLoaderCallback({});
105 
106   InitializeRangeRequestInfo(appcache_request_->GetHeaders());
107   storage_->LoadResponseInfo(manifest_url_, entry_.response_id(), this);
108 }
109 
DeliverNetworkResponse()110 void AppCacheURLLoader::DeliverNetworkResponse() {
111   delivery_type_ = DeliveryType::kNetwork;
112 
113   // In tests we only care about the delivery_type_ state.
114   if (AppCacheRequestHandler::IsRunningInTests())
115     return;
116 
117   // We signal our caller with an empy callback that it needs to perform
118   // the network load.
119   DCHECK(loader_callback_);
120   DCHECK(!receiver_.is_bound());
121   std::move(loader_callback_).Run({});
122   DeleteSoon();
123 }
124 
DeliverErrorResponse()125 void AppCacheURLLoader::DeliverErrorResponse() {
126   delivery_type_ = DeliveryType::kError;
127 
128   // In tests we only care about the delivery_type_ state.
129   if (AppCacheRequestHandler::IsRunningInTests())
130     return;
131 
132   if (loader_callback_) {
133     CallLoaderCallback(base::BindOnce(&AppCacheURLLoader::NotifyCompleted,
134                                       GetWeakPtr(), net::ERR_FAILED));
135   } else {
136     NotifyCompleted(net::ERR_FAILED);
137   }
138 }
139 
GetWeakPtr()140 base::WeakPtr<AppCacheURLLoader> AppCacheURLLoader::GetWeakPtr() {
141   return weak_factory_.GetWeakPtr();
142 }
143 
FollowRedirect(const std::vector<std::string> & modified_headers,const net::HttpRequestHeaders & removed_headers,const net::HttpRequestHeaders & removed_cors_exempt_headers,const base::Optional<GURL> & new_url)144 void AppCacheURLLoader::FollowRedirect(
145     const std::vector<std::string>& modified_headers,
146     const net::HttpRequestHeaders& removed_headers,
147     const net::HttpRequestHeaders& removed_cors_exempt_headers,
148     const base::Optional<GURL>& new_url) {
149   NOTREACHED() << "appcache never produces redirects";
150 }
151 
SetPriority(net::RequestPriority priority,int32_t intra_priority_value)152 void AppCacheURLLoader::SetPriority(net::RequestPriority priority,
153                                     int32_t intra_priority_value) {}
PauseReadingBodyFromNet()154 void AppCacheURLLoader::PauseReadingBodyFromNet() {}
ResumeReadingBodyFromNet()155 void AppCacheURLLoader::ResumeReadingBodyFromNet() {}
156 
DeleteIfNeeded()157 void AppCacheURLLoader::DeleteIfNeeded() {
158   if (receiver_.is_bound() || is_deleting_soon_)
159     return;
160   delete this;
161 }
162 
Start(base::OnceClosure continuation,const network::ResourceRequest &,mojo::PendingReceiver<network::mojom::URLLoader> receiver,mojo::PendingRemote<network::mojom::URLLoaderClient> client)163 void AppCacheURLLoader::Start(
164     base::OnceClosure continuation,
165     const network::ResourceRequest& /* resource_request */,
166     mojo::PendingReceiver<network::mojom::URLLoader> receiver,
167     mojo::PendingRemote<network::mojom::URLLoaderClient> client) {
168   // TODO(crbug.com/876531): Figure out how AppCache interception should
169   // interact with URLLoaderThrottles. It might be incorrect to ignore
170   // |resource_request| here, since it's the current request after throttles.
171   DCHECK(!receiver_.is_bound());
172   receiver_.Bind(std::move(receiver));
173   client_.Bind(std::move(client));
174   receiver_.set_disconnect_handler(
175       base::BindOnce(&AppCacheURLLoader::DeleteSoon, GetWeakPtr()));
176 
177   MojoResult result =
178       mojo::CreateDataPipe(nullptr, &response_body_stream_, &consumer_handle_);
179   if (result != MOJO_RESULT_OK) {
180     NotifyCompleted(net::ERR_INSUFFICIENT_RESOURCES);
181     return;
182   }
183   DCHECK(response_body_stream_.is_valid());
184   DCHECK(consumer_handle_.is_valid());
185 
186   if (continuation)
187     std::move(continuation).Run();
188 }
189 
AppCacheURLLoader(AppCacheRequest * appcache_request,AppCacheStorage * storage,AppCacheRequestHandler::AppCacheLoaderCallback loader_callback)190 AppCacheURLLoader::AppCacheURLLoader(
191     AppCacheRequest* appcache_request,
192     AppCacheStorage* storage,
193     AppCacheRequestHandler::AppCacheLoaderCallback loader_callback)
194     : storage_(storage->GetWeakPtr()),
195       start_time_tick_(base::TimeTicks::Now()),
196       writable_handle_watcher_(FROM_HERE,
197                                mojo::SimpleWatcher::ArmingPolicy::MANUAL,
198                                base::SequencedTaskRunnerHandle::Get()),
199       loader_callback_(std::move(loader_callback)),
200       appcache_request_(appcache_request->GetWeakPtr()) {}
201 
CallLoaderCallback(base::OnceClosure continuation)202 void AppCacheURLLoader::CallLoaderCallback(base::OnceClosure continuation) {
203   DCHECK(loader_callback_);
204   DCHECK(!receiver_.is_bound());
205   std::move(loader_callback_)
206       .Run(base::BindOnce(&AppCacheURLLoader::Start, GetWeakPtr(),
207                           std::move(continuation)));
208 }
209 
OnResponseInfoLoaded(AppCacheResponseInfo * response_info,int64_t response_id)210 void AppCacheURLLoader::OnResponseInfoLoaded(
211     AppCacheResponseInfo* response_info,
212     int64_t response_id) {
213   DCHECK(IsDeliveringAppCacheResponse());
214 
215   if (!storage_.get()) {
216     DeliverErrorResponse();
217     return;
218   }
219 
220   if (response_info) {
221     if (loader_callback_) {
222       CallLoaderCallback(
223           base::BindOnce(&AppCacheURLLoader::ContinueOnResponseInfoLoaded,
224                          GetWeakPtr(), base::WrapRefCounted(response_info)));
225     } else {
226       ContinueOnResponseInfoLoaded(response_info);
227     }
228     return;
229   }
230 
231   // We failed to load the response headers from the disk cache.
232   if (storage_->service()->storage() == storage_.get()) {
233     // A resource that is expected to be in the appcache is missing.
234     // If the 'check' fails, the corrupt appcache will be deleted.
235     // See http://code.google.com/p/chromium/issues/detail?id=50657
236     storage_->service()->CheckAppCacheResponse(manifest_url_, cache_id_,
237                                                entry_.response_id());
238   }
239   cache_entry_not_found_ = true;
240 
241   // We fallback to the network unless this loader was falling back to the
242   // appcache from the network which had already failed in some way.
243   if (!is_fallback_)
244     DeliverNetworkResponse();
245   else
246     DeliverErrorResponse();
247 }
248 
ContinueOnResponseInfoLoaded(scoped_refptr<AppCacheResponseInfo> response_info)249 void AppCacheURLLoader::ContinueOnResponseInfoLoaded(
250     scoped_refptr<AppCacheResponseInfo> response_info) {
251   info_ = response_info;
252   reader_ = storage_->CreateResponseReader(manifest_url_, entry_.response_id());
253 
254   if (is_range_request())
255     SetupRangeResponse();
256 
257   // TODO(ananta)
258   // Move the asynchronous reading and mojo pipe handling code to a helper
259   // class. That would also need a change to BlobURLLoader.
260 
261   // Wait for the data pipe to be ready to accept data.
262   writable_handle_watcher_.Watch(
263       response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
264       base::BindRepeating(&AppCacheURLLoader::OnResponseBodyStreamReady,
265                           GetWeakPtr()));
266 
267   SendResponseInfo();
268   ReadMore();
269 }
270 
OnReadComplete(int result)271 void AppCacheURLLoader::OnReadComplete(int result) {
272   if (result > 0) {
273     uint32_t bytes_written = static_cast<uint32_t>(result);
274     response_body_stream_ = pending_write_->Complete(bytes_written);
275     pending_write_ = nullptr;
276     ReadMore();
277     return;
278   }
279 
280   writable_handle_watcher_.Cancel();
281   pending_write_->Complete(0);
282   pending_write_ = nullptr;
283   NotifyCompleted(result);
284 }
285 
OnResponseBodyStreamReady(MojoResult result)286 void AppCacheURLLoader::OnResponseBodyStreamReady(MojoResult result) {
287   // TODO(ananta)
288   // Add proper error handling here.
289   if (result != MOJO_RESULT_OK) {
290     NotifyCompleted(net::ERR_FAILED);
291     return;
292   }
293   ReadMore();
294 }
295 
DeleteSoon()296 void AppCacheURLLoader::DeleteSoon() {
297   if (storage_.get())
298     storage_->CancelDelegateCallbacks(this);
299   weak_factory_.InvalidateWeakPtrs();
300   is_deleting_soon_ = true;
301   base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
302 }
303 
SendResponseInfo()304 void AppCacheURLLoader::SendResponseInfo() {
305   // If this is null it means the response information was sent to the client.
306   if (!consumer_handle_.is_valid())
307     return;
308 
309   const net::HttpResponseInfo& http_info =
310       is_range_request() ? *range_response_info_ : info_->http_response_info();
311   auto response_head = network::mojom::URLResponseHead::New();
312   response_head->headers = http_info.headers;
313   response_head->appcache_id = cache_id_;
314   response_head->appcache_manifest_url = manifest_url_;
315 
316   http_info.headers->GetMimeType(&response_head->mime_type);
317   http_info.headers->GetCharset(&response_head->charset);
318 
319   // TODO(ananta)
320   // Verify if the times sent here are correct.
321   response_head->request_time = http_info.request_time;
322   response_head->response_time = http_info.response_time;
323   response_head->content_length =
324       is_range_request() ? range_response_info_->headers->GetContentLength()
325                          : info_->response_data_size();
326   response_head->connection_info = http_info.connection_info;
327   response_head->remote_endpoint = http_info.remote_endpoint;
328   response_head->was_fetched_via_spdy = http_info.was_fetched_via_spdy;
329   response_head->was_alpn_negotiated = http_info.was_alpn_negotiated;
330   response_head->alpn_negotiated_protocol = http_info.alpn_negotiated_protocol;
331   if (http_info.ssl_info.cert)
332     response_head->ssl_info = http_info.ssl_info;
333   response_head->load_timing = load_timing_info_;
334 
335   client_->OnReceiveResponse(std::move(response_head));
336   client_->OnStartLoadingResponseBody(std::move(consumer_handle_));
337 }
338 
ReadMore()339 void AppCacheURLLoader::ReadMore() {
340   DCHECK(!pending_write_.get());
341 
342   uint32_t num_bytes;
343   // TODO: we should use the abstractions in MojoAsyncResourceHandler.
344   MojoResult result = network::NetToMojoPendingBuffer::BeginWrite(
345       &response_body_stream_, &pending_write_, &num_bytes);
346   if (result == MOJO_RESULT_SHOULD_WAIT) {
347     // The pipe is full. We need to wait for it to have more space.
348     writable_handle_watcher_.ArmOrNotify();
349     return;
350   }
351   if (result != MOJO_RESULT_OK) {
352     NotifyCompleted(net::ERR_FAILED);
353     writable_handle_watcher_.Cancel();
354     response_body_stream_.reset();
355     return;
356   }
357 
358   CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
359   auto buffer =
360       base::MakeRefCounted<network::NetToMojoIOBuffer>(pending_write_.get());
361 
362   uint32_t bytes_to_read =
363       std::min<uint32_t>(num_bytes, info_->response_data_size());
364 
365   reader_->ReadData(
366       buffer.get(), bytes_to_read,
367       base::BindOnce(&AppCacheURLLoader::OnReadComplete, GetWeakPtr()));
368 }
369 
NotifyCompleted(int error_code)370 void AppCacheURLLoader::NotifyCompleted(int error_code) {
371   if (storage_.get())
372     storage_->CancelDelegateCallbacks(this);
373 
374   if (AppCacheRequestHandler::IsRunningInTests())
375     return;
376 
377   network::URLLoaderCompletionStatus status(error_code);
378   if (!error_code) {
379     const net::HttpResponseInfo* http_info = is_range_request()
380                                                  ? range_response_info_.get()
381                                                  : &info_->http_response_info();
382     status.exists_in_cache = http_info->was_cached;
383     status.completion_time = base::TimeTicks::Now();
384     status.encoded_body_length =
385         is_range_request() ? range_response_info_->headers->GetContentLength()
386                            : info_->response_data_size();
387     status.decoded_body_length = status.encoded_body_length;
388   }
389   client_->OnComplete(status);
390 }
391 
392 }  // namespace content
393