blob: 8bfb1093bb37fbafa7cfe33f269db1971d053519 [file] [log] [blame]
Ken Rockot314714c2017-11-05 23:36:241// 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/file_url_loader_factory.h"
6
7#include <memory>
8#include <string>
9#include <utility>
Chris Mumford8f812662018-02-22 00:27:5710#include <vector>
Ken Rockot314714c2017-11-05 23:36:2411
12#include "base/bind.h"
13#include "base/files/file.h"
14#include "base/files/file_path.h"
15#include "base/files/file_util.h"
16#include "base/macros.h"
17#include "base/numerics/safe_conversions.h"
18#include "base/strings/string16.h"
19#include "base/strings/string_util.h"
20#include "base/strings/sys_string_conversions.h"
21#include "base/strings/utf_string_conversions.h"
Ken Rockot6414c4d92017-11-08 19:58:3222#include "base/task_scheduler/post_task.h"
Ken Rockot314714c2017-11-05 23:36:2423#include "base/time/time.h"
24#include "build/build_config.h"
25#include "content/public/browser/content_browser_client.h"
Ken Rockot6414c4d92017-11-08 19:58:3226#include "content/public/browser/file_url_loader.h"
Matt Menkefcbb1bd72018-01-31 21:53:1227#include "content/public/common/content_client.h"
Ken Rockot314714c2017-11-05 23:36:2428#include "mojo/public/cpp/bindings/strong_binding.h"
29#include "mojo/public/cpp/system/data_pipe.h"
30#include "mojo/public/cpp/system/file_data_pipe_producer.h"
31#include "mojo/public/cpp/system/string_data_pipe_producer.h"
32#include "net/base/directory_lister.h"
33#include "net/base/directory_listing.h"
34#include "net/base/filename_util.h"
35#include "net/base/mime_sniffer.h"
36#include "net/base/mime_util.h"
37#include "net/base/net_errors.h"
38#include "net/http/http_byte_range.h"
39#include "net/http/http_util.h"
40#include "net/url_request/redirect_info.h"
John Abd-El-Malek1df61792018-01-12 20:40:4541#include "services/network/public/cpp/resource_request.h"
Ken Rockot54311e62018-02-10 19:01:5242#include "services/network/public/mojom/url_loader.mojom.h"
Chris Mumfordb5df8ef22017-12-20 17:47:4243#include "storage/common/fileapi/file_system_util.h"
Ken Rockot314714c2017-11-05 23:36:2444#include "url/gurl.h"
45
46#if defined(OS_WIN)
47#include "base/win/shortcut.h"
48#endif
49
50namespace content {
51namespace {
52
53constexpr size_t kDefaultFileUrlPipeSize = 65536;
54
55// Because this makes things simpler.
56static_assert(kDefaultFileUrlPipeSize >= net::kMaxBytesToSniff,
57 "Default file data pipe size must be at least as large as a MIME-"
58 "type sniffing buffer.");
59
Ken Rockot6414c4d92017-11-08 19:58:3260// Policy to control how a FileURLLoader will handle directory URLs.
61enum class DirectoryLoadingPolicy {
62 // File paths which refer to directories are allowed and will load as an
63 // HTML directory listing.
64 kRespondWithListing,
65
66 // File paths which refer to directories are treated as non-existent and
67 // will result in FILE_NOT_FOUND errors.
68 kFail,
69};
70
71// Policy to control whether or not file access constraints imposed by content
72// or its embedder should be honored by a FileURLLoader.
73enum class FileAccessPolicy {
74 // Enforces file acccess policy defined by content and/or its embedder.
75 kRestricted,
76
77 // Ignores file access policy, allowing contents to be loaded from any
78 // resolvable file path given.
79 kUnrestricted,
80};
81
82// Policy to control whether or not a FileURLLoader should follow filesystem
83// links (e.g. Windows shortcuts) where applicable.
84enum class LinkFollowingPolicy {
85 kFollow,
86 kDoNotFollow,
87};
88
Ken Rockot314714c2017-11-05 23:36:2489class FileURLDirectoryLoader
John Abd-El-Malekb165dc52018-01-18 17:12:1890 : public network::mojom::URLLoader,
Ken Rockot314714c2017-11-05 23:36:2491 public net::DirectoryLister::DirectoryListerDelegate {
92 public:
Chris Mumford8f812662018-02-22 00:27:5793 static void CreateAndStart(
94 const base::FilePath& profile_path,
95 const network::ResourceRequest& request,
96 network::mojom::URLLoaderRequest loader,
97 network::mojom::URLLoaderClientPtrInfo client_info,
98 std::unique_ptr<FileURLLoaderObserver> observer,
99 scoped_refptr<net::HttpResponseHeaders> response_headers) {
Ken Rockot314714c2017-11-05 23:36:24100 // Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr
101 // bindings are alive - essentially until either the client gives up or all
102 // file data has been sent to it.
103 auto* file_url_loader = new FileURLDirectoryLoader;
104 file_url_loader->Start(profile_path, request, std::move(loader),
Chris Mumford8f812662018-02-22 00:27:57105 std::move(client_info), std::move(observer),
106 std::move(response_headers));
Ken Rockot314714c2017-11-05 23:36:24107 }
108
John Abd-El-Malekb165dc52018-01-18 17:12:18109 // network::mojom::URLLoader:
Chong Zhangf19dde92018-05-23 04:33:59110 void FollowRedirect(const base::Optional<net::HttpRequestHeaders>&
111 modified_request_headers) override {}
arthursonzogni2e1524a72017-12-18 16:53:26112 void ProceedWithResponse() override { NOTREACHED(); }
Ken Rockot314714c2017-11-05 23:36:24113 void SetPriority(net::RequestPriority priority,
114 int32_t intra_priority_value) override {}
115 void PauseReadingBodyFromNet() override {}
116 void ResumeReadingBodyFromNet() override {}
117
118 private:
119 FileURLDirectoryLoader() : binding_(this) {}
120 ~FileURLDirectoryLoader() override = default;
121
122 void Start(const base::FilePath& profile_path,
John Abd-El-Malek1df61792018-01-12 20:40:45123 const network::ResourceRequest& request,
John Abd-El-Malekb165dc52018-01-18 17:12:18124 network::mojom::URLLoaderRequest loader,
125 network::mojom::URLLoaderClientPtrInfo client_info,
Chris Mumford8f812662018-02-22 00:27:57126 std::unique_ptr<content::FileURLLoaderObserver> observer,
127 scoped_refptr<net::HttpResponseHeaders> response_headers) {
Ken Rockot314714c2017-11-05 23:36:24128 binding_.Bind(std::move(loader));
129 binding_.set_connection_error_handler(base::BindOnce(
130 &FileURLDirectoryLoader::OnConnectionError, base::Unretained(this)));
131
John Abd-El-Malekb165dc52018-01-18 17:12:18132 network::mojom::URLLoaderClientPtr client;
Ken Rockot314714c2017-11-05 23:36:24133 client.Bind(std::move(client_info));
134
135 if (!net::FileURLToFilePath(request.url, &path_)) {
Takashi Toyoshimaaa278662017-11-20 11:11:26136 client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
Ken Rockot314714c2017-11-05 23:36:24137 return;
138 }
139
140 base::File::Info info;
141 if (!base::GetFileInfo(path_, &info) || !info.is_directory) {
Takashi Toyoshimaaa278662017-11-20 11:11:26142 client->OnComplete(
143 network::URLLoaderCompletionStatus(net::ERR_FILE_NOT_FOUND));
Ken Rockot314714c2017-11-05 23:36:24144 return;
145 }
146
147 if (!GetContentClient()->browser()->IsFileAccessAllowed(
148 path_, base::MakeAbsoluteFilePath(path_), profile_path)) {
Takashi Toyoshimaaa278662017-11-20 11:11:26149 client->OnComplete(
150 network::URLLoaderCompletionStatus(net::ERR_ACCESS_DENIED));
Ken Rockot314714c2017-11-05 23:36:24151 return;
152 }
153
154 mojo::DataPipe pipe(kDefaultFileUrlPipeSize);
155 if (!pipe.consumer_handle.is_valid()) {
Takashi Toyoshimaaa278662017-11-20 11:11:26156 client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
Ken Rockot314714c2017-11-05 23:36:24157 return;
158 }
159
John Abd-El-Malek46248032018-01-17 19:11:23160 network::ResourceResponseHead head;
Ken Rockot314714c2017-11-05 23:36:24161 head.mime_type = "text/html";
162 head.charset = "utf-8";
Marijn Kruisselbrink9ebd7ba2018-06-11 23:18:04163 client->OnReceiveResponse(head);
Ken Rockot314714c2017-11-05 23:36:24164 client->OnStartLoadingResponseBody(std::move(pipe.consumer_handle));
165 client_ = std::move(client);
166
167 lister_ = std::make_unique<net::DirectoryLister>(path_, this);
168 lister_->Start();
169
170 data_producer_ = std::make_unique<mojo::StringDataPipeProducer>(
171 std::move(pipe.producer_handle));
172 }
173
174 void OnConnectionError() {
175 binding_.Close();
176 MaybeDeleteSelf();
177 }
178
179 void MaybeDeleteSelf() {
180 if (!binding_.is_bound() && !client_.is_bound() && !lister_)
181 delete this;
182 }
183
184 // net::DirectoryLister::DirectoryListerDelegate:
185 void OnListFile(
186 const net::DirectoryLister::DirectoryListerData& data) override {
187 if (!wrote_header_) {
188 wrote_header_ = true;
189
190#if defined(OS_WIN)
191 const base::string16& title = path_.value();
Fabrice de Gans-Riberi91fa35052018-05-07 19:33:48192#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
Ken Rockot314714c2017-11-05 23:36:24193 const base::string16& title =
194 base::WideToUTF16(base::SysNativeMBToWide(path_.value()));
195#endif
196 pending_data_.append(net::GetDirectoryListingHeader(title));
197
198 // If not a top-level directory, add a link to the parent directory. To
199 // figure this out, first normalize |path_| by stripping trailing
200 // separators. Then compare the result to its DirName(). For the top-level
201 // directory, e.g. "/" or "c:\\", the normalized path is equal to its own
202 // DirName().
203 base::FilePath stripped_path = path_.StripTrailingSeparators();
204 if (stripped_path != stripped_path.DirName())
205 pending_data_.append(net::GetParentDirectoryLink());
206 }
207
208 // Skip current / parent links from the listing.
209 base::FilePath filename = data.info.GetName();
210 if (filename.value() != base::FilePath::kCurrentDirectory &&
211 filename.value() != base::FilePath::kParentDirectory) {
212#if defined(OS_WIN)
213 std::string raw_bytes; // Empty on Windows means UTF-8 encoded name.
Fabrice de Gans-Riberi91fa35052018-05-07 19:33:48214#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
Ken Rockot314714c2017-11-05 23:36:24215 const std::string& raw_bytes = filename.value();
216#endif
217 pending_data_.append(net::GetDirectoryListingEntry(
218 filename.LossyDisplayName(), raw_bytes, data.info.IsDirectory(),
219 data.info.GetSize(), data.info.GetLastModifiedTime()));
220 }
221
222 MaybeTransferPendingData();
223 }
224
225 void OnListDone(int error) override {
226 listing_result_ = error;
227 lister_.reset();
228 MaybeDeleteSelf();
229 }
230
231 void MaybeTransferPendingData() {
232 if (transfer_in_progress_)
233 return;
234
235 transfer_in_progress_ = true;
236 data_producer_->Write(pending_data_,
Daniel Murphy0ecba612018-02-07 20:56:18237 mojo::StringDataPipeProducer::AsyncWritingMode::
238 STRING_MAY_BE_INVALIDATED_BEFORE_COMPLETION,
Ken Rockot314714c2017-11-05 23:36:24239 base::BindOnce(&FileURLDirectoryLoader::OnDataWritten,
240 base::Unretained(this)));
241 // The producer above will have already copied any parts of |pending_data_|
242 // that couldn't be written immediately, so we can wipe it out here to begin
243 // accumulating more data.
244 pending_data_.clear();
245 }
246
247 void OnDataWritten(MojoResult result) {
248 transfer_in_progress_ = false;
249
250 int completion_status;
251 if (result == MOJO_RESULT_OK) {
252 if (!pending_data_.empty()) {
253 // Keep flushing the data buffer as long as it's non-empty and pipe
254 // writes are succeeding.
255 MaybeTransferPendingData();
256 return;
257 }
258
259 // If there's no pending data but the lister is still active, we simply
260 // wait for more listing results.
261 if (lister_)
262 return;
263
264 // At this point we know the listing is complete and all available data
265 // has been transferred. We inherit the status of the listing operation.
266 completion_status = listing_result_;
267 } else {
268 completion_status = net::ERR_FAILED;
269 }
270
arthursonzogni3a4ca9f2017-12-07 17:58:34271 // All the data has been written now. Close the data pipe. The consumer will
272 // be notified that there will be no more data to read from now.
273 data_producer_.reset();
274
Takashi Toyoshimaaa278662017-11-20 11:11:26275 client_->OnComplete(network::URLLoaderCompletionStatus(completion_status));
Ken Rockot314714c2017-11-05 23:36:24276 client_.reset();
arthursonzogni3a4ca9f2017-12-07 17:58:34277
Ken Rockot314714c2017-11-05 23:36:24278 MaybeDeleteSelf();
279 }
280
281 base::FilePath path_;
282 std::unique_ptr<net::DirectoryLister> lister_;
283 bool wrote_header_ = false;
284 int listing_result_;
285
John Abd-El-Malekb165dc52018-01-18 17:12:18286 mojo::Binding<network::mojom::URLLoader> binding_;
287 network::mojom::URLLoaderClientPtr client_;
Ken Rockot314714c2017-11-05 23:36:24288
289 std::unique_ptr<mojo::StringDataPipeProducer> data_producer_;
290 std::string pending_data_;
291 bool transfer_in_progress_ = false;
292
293 DISALLOW_COPY_AND_ASSIGN(FileURLDirectoryLoader);
294};
295
John Abd-El-Malekb165dc52018-01-18 17:12:18296class FileURLLoader : public network::mojom::URLLoader {
Ken Rockot314714c2017-11-05 23:36:24297 public:
Ken Rockota0dfaca12018-02-15 07:26:25298 static void CreateAndStart(
299 const base::FilePath& profile_path,
300 const network::ResourceRequest& request,
301 network::mojom::URLLoaderRequest loader,
302 network::mojom::URLLoaderClientPtrInfo client_info,
303 DirectoryLoadingPolicy directory_loading_policy,
304 FileAccessPolicy file_access_policy,
305 LinkFollowingPolicy link_following_policy,
306 std::unique_ptr<FileURLLoaderObserver> observer,
307 scoped_refptr<net::HttpResponseHeaders> extra_response_headers) {
Ken Rockot314714c2017-11-05 23:36:24308 // Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr
309 // bindings are alive - essentially until either the client gives up or all
310 // file data has been sent to it.
311 auto* file_url_loader = new FileURLLoader;
Ken Rockota0dfaca12018-02-15 07:26:25312 file_url_loader->Start(
313 profile_path, request, std::move(loader), std::move(client_info),
314 directory_loading_policy, file_access_policy, link_following_policy,
315 std::move(observer), std::move(extra_response_headers));
Ken Rockot314714c2017-11-05 23:36:24316 }
317
John Abd-El-Malekb165dc52018-01-18 17:12:18318 // network::mojom::URLLoader:
Chong Zhangf19dde92018-05-23 04:33:59319 void FollowRedirect(const base::Optional<net::HttpRequestHeaders>&
320 modified_request_headers) override {}
arthursonzogni2e1524a72017-12-18 16:53:26321 void ProceedWithResponse() override {}
Ken Rockot314714c2017-11-05 23:36:24322 void SetPriority(net::RequestPriority priority,
323 int32_t intra_priority_value) override {}
324 void PauseReadingBodyFromNet() override {}
325 void ResumeReadingBodyFromNet() override {}
326
327 private:
328 FileURLLoader() : binding_(this) {}
329 ~FileURLLoader() override = default;
330
331 void Start(const base::FilePath& profile_path,
John Abd-El-Malek1df61792018-01-12 20:40:45332 const network::ResourceRequest& request,
John Abd-El-Malekb165dc52018-01-18 17:12:18333 network::mojom::URLLoaderRequest loader,
334 network::mojom::URLLoaderClientPtrInfo client_info,
Ken Rockot6414c4d92017-11-08 19:58:32335 DirectoryLoadingPolicy directory_loading_policy,
336 FileAccessPolicy file_access_policy,
Chris Mumfordb5df8ef22017-12-20 17:47:42337 LinkFollowingPolicy link_following_policy,
Ken Rockota0dfaca12018-02-15 07:26:25338 std::unique_ptr<FileURLLoaderObserver> observer,
339 scoped_refptr<net::HttpResponseHeaders> extra_response_headers) {
John Abd-El-Malek46248032018-01-17 19:11:23340 network::ResourceResponseHead head;
Ken Rockot314714c2017-11-05 23:36:24341 head.request_start = base::TimeTicks::Now();
342 head.response_start = base::TimeTicks::Now();
Ken Rockota0dfaca12018-02-15 07:26:25343 head.headers = extra_response_headers;
Ken Rockot314714c2017-11-05 23:36:24344 binding_.Bind(std::move(loader));
345 binding_.set_connection_error_handler(base::BindOnce(
346 &FileURLLoader::OnConnectionError, base::Unretained(this)));
347
John Abd-El-Malekb165dc52018-01-18 17:12:18348 network::mojom::URLLoaderClientPtr client;
Ken Rockot314714c2017-11-05 23:36:24349 client.Bind(std::move(client_info));
350
351 base::FilePath path;
352 if (!net::FileURLToFilePath(request.url, &path)) {
Takashi Toyoshimaaa278662017-11-20 11:11:26353 client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
Chris Mumfordb5df8ef22017-12-20 17:47:42354 if (observer)
355 observer->OnDoneReading();
Ken Rockot314714c2017-11-05 23:36:24356 return;
357 }
358
359 base::File::Info info;
360 if (!base::GetFileInfo(path, &info)) {
Takashi Toyoshimaaa278662017-11-20 11:11:26361 client->OnComplete(
362 network::URLLoaderCompletionStatus(net::ERR_FILE_NOT_FOUND));
Chris Mumfordb5df8ef22017-12-20 17:47:42363 if (observer)
364 observer->OnDoneReading();
Ken Rockot314714c2017-11-05 23:36:24365 return;
366 }
367
Ken Rockot6414c4d92017-11-08 19:58:32368 if (info.is_directory) {
369 if (directory_loading_policy == DirectoryLoadingPolicy::kFail) {
Takashi Toyoshimaaa278662017-11-20 11:11:26370 client->OnComplete(
371 network::URLLoaderCompletionStatus(net::ERR_FILE_NOT_FOUND));
Chris Mumfordb5df8ef22017-12-20 17:47:42372 if (observer)
373 observer->OnDoneReading();
Ken Rockot6414c4d92017-11-08 19:58:32374 return;
375 }
Ken Rockot314714c2017-11-05 23:36:24376
Ken Rockot6414c4d92017-11-08 19:58:32377 DCHECK_EQ(directory_loading_policy,
378 DirectoryLoadingPolicy::kRespondWithListing);
379
380 GURL directory_url = request.url;
381 if (!path.EndsWithSeparator()) {
382 // If the named path is a directory with no trailing slash, redirect to
383 // the same path, but with a trailing slash.
384 std::string new_path = directory_url.path() + '/';
385 GURL::Replacements replacements;
386 replacements.SetPathStr(new_path);
387 directory_url = directory_url.ReplaceComponents(replacements);
388
389 net::RedirectInfo redirect_info;
390 redirect_info.new_method = "GET";
391 redirect_info.status_code = 301;
392 redirect_info.new_url = directory_url;
393 head.encoded_data_length = 0;
394 client->OnReceiveRedirect(redirect_info, head);
395 }
Ken Rockot314714c2017-11-05 23:36:24396
397 // Restart the request with a directory loader.
John Abd-El-Malek1df61792018-01-12 20:40:45398 network::ResourceRequest new_request = request;
Ken Rockot6414c4d92017-11-08 19:58:32399 new_request.url = directory_url;
Ken Rockot314714c2017-11-05 23:36:24400 FileURLDirectoryLoader::CreateAndStart(
Chris Mumfordb5df8ef22017-12-20 17:47:42401 profile_path, new_request, binding_.Unbind(), client.PassInterface(),
Chris Mumford8f812662018-02-22 00:27:57402 std::move(observer), std::move(extra_response_headers));
Ken Rockot314714c2017-11-05 23:36:24403 MaybeDeleteSelf();
404 return;
405 }
406
407#if defined(OS_WIN)
408 base::FilePath shortcut_target;
Ken Rockot6414c4d92017-11-08 19:58:32409 if (link_following_policy == LinkFollowingPolicy::kFollow &&
410 base::LowerCaseEqualsASCII(path.Extension(), ".lnk") &&
Ken Rockot314714c2017-11-05 23:36:24411 base::win::ResolveShortcut(path, &shortcut_target, nullptr)) {
412 // Follow Windows shortcuts
413 GURL new_url = net::FilePathToFileURL(shortcut_target);
414
415 net::RedirectInfo redirect_info;
416 redirect_info.new_method = "GET";
417 redirect_info.status_code = 301;
418 redirect_info.new_url = new_url;
419 head.encoded_data_length = 0;
420 client->OnReceiveRedirect(redirect_info, head);
421
422 // Restart the request with the new URL.
John Abd-El-Malek1df61792018-01-12 20:40:45423 network::ResourceRequest new_request = request;
Ken Rockot314714c2017-11-05 23:36:24424 new_request.url = redirect_info.new_url;
425 return Start(profile_path, request, binding_.Unbind(),
Ken Rockot6414c4d92017-11-08 19:58:32426 client.PassInterface(), directory_loading_policy,
Chris Mumfordb5df8ef22017-12-20 17:47:42427 file_access_policy, link_following_policy,
Ken Rockota0dfaca12018-02-15 07:26:25428 std::move(observer), std::move(extra_response_headers));
Ken Rockot314714c2017-11-05 23:36:24429 }
430#endif // defined(OS_WIN)
431
Ken Rockot6414c4d92017-11-08 19:58:32432 if (file_access_policy == FileAccessPolicy::kRestricted &&
433 !GetContentClient()->browser()->IsFileAccessAllowed(
Ken Rockot314714c2017-11-05 23:36:24434 path, base::MakeAbsoluteFilePath(path), profile_path)) {
Takashi Toyoshimaaa278662017-11-20 11:11:26435 client->OnComplete(
436 network::URLLoaderCompletionStatus(net::ERR_ACCESS_DENIED));
Chris Mumfordb5df8ef22017-12-20 17:47:42437 if (observer)
438 observer->OnDoneReading();
Ken Rockot314714c2017-11-05 23:36:24439 return;
440 }
441
442 mojo::DataPipe pipe(kDefaultFileUrlPipeSize);
443 if (!pipe.consumer_handle.is_valid()) {
Takashi Toyoshimaaa278662017-11-20 11:11:26444 client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
Chris Mumfordb5df8ef22017-12-20 17:47:42445 if (observer)
446 observer->OnDoneReading();
Ken Rockot314714c2017-11-05 23:36:24447 return;
448 }
449
450 // Should never be possible for this to be a directory. If FileURLLoader
451 // is used to respond to a directory request, it must be because the URL
452 // path didn't have a trailing path separator. In that case we finish with
453 // a redirect above which will in turn be handled by FileURLDirectoryLoader.
454 DCHECK(!info.is_directory);
Chris Mumfordb5df8ef22017-12-20 17:47:42455 if (observer)
456 observer->OnStart();
Ken Rockot314714c2017-11-05 23:36:24457
458 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
Chris Mumford8f812662018-02-22 00:27:57459 if (!file.IsValid()) {
Mikhail Atuchin9af3ff8c2018-04-19 21:15:10460 if (observer) {
461 observer->OnBytesRead(nullptr, 0u, file.error_details());
Chris Mumford8f812662018-02-22 00:27:57462 observer->OnDoneReading();
Mikhail Atuchin9af3ff8c2018-04-19 21:15:10463 }
464 net::Error net_error = net::FileErrorToNetError(file.error_details());
Chris Mumford8f812662018-02-22 00:27:57465 client->OnComplete(network::URLLoaderCompletionStatus(net_error));
466 MaybeDeleteSelf();
467 return;
468 }
Ken Rockot314714c2017-11-05 23:36:24469 char initial_read_buffer[net::kMaxBytesToSniff];
470 int initial_read_result =
471 file.ReadAtCurrentPos(initial_read_buffer, net::kMaxBytesToSniff);
472 if (initial_read_result < 0) {
Chris Mumfordb5df8ef22017-12-20 17:47:42473 base::File::Error read_error = base::File::GetLastFileError();
474 DCHECK_NE(base::File::FILE_OK, read_error);
475 if (observer) {
Mikhail Atuchin9af3ff8c2018-04-19 21:15:10476 // This can happen when the file is unreadable (which can happen during
477 // corruption). We need to be sure to inform
478 // the observer that we've finished reading so that it can proceed.
Chris Mumfordb5df8ef22017-12-20 17:47:42479 observer->OnBytesRead(nullptr, 0u, read_error);
480 observer->OnDoneReading();
481 }
Mikhail Atuchin9af3ff8c2018-04-19 21:15:10482 net::Error net_error = net::FileErrorToNetError(read_error);
Chris Mumfordb5df8ef22017-12-20 17:47:42483 client->OnComplete(network::URLLoaderCompletionStatus(net_error));
Ken Rockot314714c2017-11-05 23:36:24484 return;
Chris Mumfordb5df8ef22017-12-20 17:47:42485 } else if (observer) {
486 observer->OnBytesRead(initial_read_buffer, initial_read_result,
487 base::File::FILE_OK);
Ken Rockot314714c2017-11-05 23:36:24488 }
489 size_t initial_read_size = static_cast<size_t>(initial_read_result);
490
491 std::string range_header;
492 net::HttpByteRange byte_range;
493 if (request.headers.GetHeader(net::HttpRequestHeaders::kRange,
494 &range_header)) {
495 // Handle a simple Range header for a single range.
496 std::vector<net::HttpByteRange> ranges;
497 bool fail = false;
498 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges) &&
499 ranges.size() == 1) {
500 byte_range = ranges[0];
501 if (!byte_range.ComputeBounds(info.size))
502 fail = true;
503 } else {
504 fail = true;
505 }
506
507 if (fail) {
Takashi Toyoshimaaa278662017-11-20 11:11:26508 client->OnComplete(network::URLLoaderCompletionStatus(
509 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
Chris Mumfordb5df8ef22017-12-20 17:47:42510 if (observer)
511 observer->OnDoneReading();
Ken Rockot314714c2017-11-05 23:36:24512 return;
513 }
514 }
515
516 size_t first_byte_to_send = 0;
517 size_t total_bytes_to_send = static_cast<size_t>(info.size);
518 if (byte_range.IsValid()) {
519 first_byte_to_send =
520 static_cast<size_t>(byte_range.first_byte_position());
521 total_bytes_to_send =
522 static_cast<size_t>(byte_range.last_byte_position()) -
523 first_byte_to_send + 1;
524 }
525
526 head.content_length = base::saturated_cast<int64_t>(total_bytes_to_send);
527
528 if (first_byte_to_send < initial_read_size) {
529 // Write any data we read for MIME sniffing, constraining by range where
530 // applicable. This will always fit in the pipe (see assertion near
531 // |kDefaultFileUrlPipeSize| definition).
532 uint32_t write_size = std::min(
533 static_cast<uint32_t>(initial_read_size - first_byte_to_send),
534 static_cast<uint32_t>(total_bytes_to_send));
535 const uint32_t expected_write_size = write_size;
536 MojoResult result = pipe.producer_handle->WriteData(
537 &initial_read_buffer[first_byte_to_send], &write_size,
538 MOJO_WRITE_DATA_FLAG_NONE);
539 if (result != MOJO_RESULT_OK || write_size != expected_write_size) {
Chris Mumfordb5df8ef22017-12-20 17:47:42540 OnFileWritten(std::move(observer), result);
Ken Rockot314714c2017-11-05 23:36:24541 return;
542 }
543
544 // Discount the bytes we just sent from the total range.
545 first_byte_to_send = initial_read_size;
546 total_bytes_to_send -= write_size;
547 }
548
549 if (!net::GetMimeTypeFromFile(path, &head.mime_type)) {
Matt Menkefcbb1bd72018-01-31 21:53:12550 net::SniffMimeType(
551 initial_read_buffer, initial_read_result, request.url, head.mime_type,
552 GetContentClient()->browser()->ForceSniffingFileUrlsForHtml()
553 ? net::ForceSniffFileUrlsForHtml::kEnabled
554 : net::ForceSniffFileUrlsForHtml::kDisabled,
555 &head.mime_type);
Ken Rockot314714c2017-11-05 23:36:24556 }
Chris Mumford8f812662018-02-22 00:27:57557 if (head.headers) {
558 head.headers->AddHeader(
559 base::StringPrintf("%s: %s", net::HttpRequestHeaders::kContentType,
560 head.mime_type.c_str()));
561 }
Marijn Kruisselbrink9ebd7ba2018-06-11 23:18:04562 client->OnReceiveResponse(head);
Ken Rockot314714c2017-11-05 23:36:24563 client->OnStartLoadingResponseBody(std::move(pipe.consumer_handle));
564 client_ = std::move(client);
565
566 if (total_bytes_to_send == 0) {
567 // There's definitely no more data, so we're already done.
Chris Mumfordb5df8ef22017-12-20 17:47:42568 OnFileWritten(std::move(observer), MOJO_RESULT_OK);
Ken Rockot314714c2017-11-05 23:36:24569 return;
570 }
571
572 // In case of a range request, seek to the appropriate position before
573 // sending the remaining bytes asynchronously. Under normal conditions
574 // (i.e., no range request) this Seek is effectively a no-op.
Chris Mumfordb5df8ef22017-12-20 17:47:42575 int new_position = file.Seek(base::File::FROM_BEGIN,
576 static_cast<int64_t>(first_byte_to_send));
577 if (observer)
578 observer->OnSeekComplete(new_position);
Ken Rockot314714c2017-11-05 23:36:24579
580 data_producer_ = std::make_unique<mojo::FileDataPipeProducer>(
Chris Mumfordb5df8ef22017-12-20 17:47:42581 std::move(pipe.producer_handle), std::move(observer));
Ken Rockot314714c2017-11-05 23:36:24582 data_producer_->WriteFromFile(
583 std::move(file), total_bytes_to_send,
Chris Mumfordb5df8ef22017-12-20 17:47:42584 base::BindOnce(&FileURLLoader::OnFileWritten, base::Unretained(this),
585 nullptr));
Ken Rockot314714c2017-11-05 23:36:24586 }
587
588 void OnConnectionError() {
589 binding_.Close();
590 MaybeDeleteSelf();
591 }
592
593 void MaybeDeleteSelf() {
594 if (!binding_.is_bound() && !client_.is_bound())
595 delete this;
596 }
597
Chris Mumfordb5df8ef22017-12-20 17:47:42598 void OnFileWritten(std::unique_ptr<FileURLLoaderObserver> observer,
599 MojoResult result) {
arthursonzogni3a4ca9f2017-12-07 17:58:34600 // All the data has been written now. Close the data pipe. The consumer will
601 // be notified that there will be no more data to read from now.
602 data_producer_.reset();
Chris Mumfordb5df8ef22017-12-20 17:47:42603 if (observer)
604 observer->OnDoneReading();
arthursonzogni3a4ca9f2017-12-07 17:58:34605
Ken Rockot314714c2017-11-05 23:36:24606 if (result == MOJO_RESULT_OK)
Takashi Toyoshimaaa278662017-11-20 11:11:26607 client_->OnComplete(network::URLLoaderCompletionStatus(net::OK));
Ken Rockot314714c2017-11-05 23:36:24608 else
Takashi Toyoshimaaa278662017-11-20 11:11:26609 client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
Ken Rockot314714c2017-11-05 23:36:24610 client_.reset();
611 MaybeDeleteSelf();
612 }
613
614 std::unique_ptr<mojo::FileDataPipeProducer> data_producer_;
John Abd-El-Malekb165dc52018-01-18 17:12:18615 mojo::Binding<network::mojom::URLLoader> binding_;
616 network::mojom::URLLoaderClientPtr client_;
Ken Rockot314714c2017-11-05 23:36:24617
618 DISALLOW_COPY_AND_ASSIGN(FileURLLoader);
619};
620
621} // namespace
622
623FileURLLoaderFactory::FileURLLoaderFactory(
624 const base::FilePath& profile_path,
625 scoped_refptr<base::SequencedTaskRunner> task_runner)
626 : profile_path_(profile_path), task_runner_(std::move(task_runner)) {}
627
628FileURLLoaderFactory::~FileURLLoaderFactory() = default;
629
Ken Rockot314714c2017-11-05 23:36:24630void FileURLLoaderFactory::CreateLoaderAndStart(
John Abd-El-Malekb165dc52018-01-18 17:12:18631 network::mojom::URLLoaderRequest loader,
Ken Rockot314714c2017-11-05 23:36:24632 int32_t routing_id,
633 int32_t request_id,
634 uint32_t options,
John Abd-El-Malek1df61792018-01-12 20:40:45635 const network::ResourceRequest& request,
John Abd-El-Malekb165dc52018-01-18 17:12:18636 network::mojom::URLLoaderClientPtr client,
Ken Rockot314714c2017-11-05 23:36:24637 const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
638 base::FilePath file_path;
639 const bool is_file = net::FileURLToFilePath(request.url, &file_path);
640 if (is_file && file_path.EndsWithSeparator() && file_path.IsAbsolute()) {
641 task_runner_->PostTask(
642 FROM_HERE,
643 base::BindOnce(&FileURLDirectoryLoader::CreateAndStart, profile_path_,
Chris Mumfordb5df8ef22017-12-20 17:47:42644 request, std::move(loader), client.PassInterface(),
Chris Mumford8f812662018-02-22 00:27:57645 std::unique_ptr<FileURLLoaderObserver>(), nullptr));
Ken Rockot314714c2017-11-05 23:36:24646 } else {
647 task_runner_->PostTask(
648 FROM_HERE,
649 base::BindOnce(&FileURLLoader::CreateAndStart, profile_path_, request,
Ken Rockot6414c4d92017-11-08 19:58:32650 std::move(loader), client.PassInterface(),
651 DirectoryLoadingPolicy::kRespondWithListing,
652 FileAccessPolicy::kRestricted,
Chris Mumfordb5df8ef22017-12-20 17:47:42653 LinkFollowingPolicy::kFollow,
Ken Rockota0dfaca12018-02-15 07:26:25654 std::unique_ptr<FileURLLoaderObserver>(),
655 nullptr /* extra_response_headers */));
Ken Rockot314714c2017-11-05 23:36:24656 }
657}
658
John Abd-El-Malekb165dc52018-01-18 17:12:18659void FileURLLoaderFactory::Clone(
660 network::mojom::URLLoaderFactoryRequest loader) {
Ken Rockot6414c4d92017-11-08 19:58:32661 bindings_.AddBinding(this, std::move(loader));
662}
663
Ken Rockota0dfaca12018-02-15 07:26:25664void CreateFileURLLoader(
665 const network::ResourceRequest& request,
666 network::mojom::URLLoaderRequest loader,
667 network::mojom::URLLoaderClientPtr client,
668 std::unique_ptr<FileURLLoaderObserver> observer,
669 scoped_refptr<net::HttpResponseHeaders> extra_response_headers) {
Ken Rockot6414c4d92017-11-08 19:58:32670 auto task_runner = base::CreateSequencedTaskRunnerWithTraits(
671 {base::MayBlock(), base::TaskPriority::BACKGROUND,
672 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
673 task_runner->PostTask(
674 FROM_HERE,
675 base::BindOnce(&FileURLLoader::CreateAndStart, base::FilePath(), request,
676 std::move(loader), client.PassInterface(),
677 DirectoryLoadingPolicy::kFail,
678 FileAccessPolicy::kUnrestricted,
Ken Rockota0dfaca12018-02-15 07:26:25679 LinkFollowingPolicy::kDoNotFollow, std::move(observer),
680 std::move(extra_response_headers)));
Ken Rockot314714c2017-11-05 23:36:24681}
682
683} // namespace content