Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 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/file_url_loader_factory.h" |
| 6 | |
| 7 | #include <memory> |
| 8 | #include <string> |
| 9 | #include <utility> |
Chris Mumford | 8f81266 | 2018-02-22 00:27:57 | [diff] [blame] | 10 | #include <vector> |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 11 | |
| 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" |
Gabriel Charette | 44db142 | 2018-08-06 11:19:33 | [diff] [blame] | 22 | #include "base/task/post_task.h" |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 23 | #include "base/time/time.h" |
| 24 | #include "build/build_config.h" |
| 25 | #include "content/public/browser/content_browser_client.h" |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 26 | #include "content/public/browser/file_url_loader.h" |
Matt Menke | fcbb1bd7 | 2018-01-31 21:53:12 | [diff] [blame] | 27 | #include "content/public/common/content_client.h" |
Yutaka Hirano | 6eddf738 | 2018-10-10 06:44:04 | [diff] [blame] | 28 | #include "content/public/common/content_switches.h" |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 29 | #include "mojo/public/cpp/bindings/strong_binding.h" |
| 30 | #include "mojo/public/cpp/system/data_pipe.h" |
| 31 | #include "mojo/public/cpp/system/file_data_pipe_producer.h" |
| 32 | #include "mojo/public/cpp/system/string_data_pipe_producer.h" |
| 33 | #include "net/base/directory_lister.h" |
| 34 | #include "net/base/directory_listing.h" |
| 35 | #include "net/base/filename_util.h" |
| 36 | #include "net/base/mime_sniffer.h" |
| 37 | #include "net/base/mime_util.h" |
| 38 | #include "net/base/net_errors.h" |
| 39 | #include "net/http/http_byte_range.h" |
| 40 | #include "net/http/http_util.h" |
| 41 | #include "net/url_request/redirect_info.h" |
Yutaka Hirano | 6eddf738 | 2018-10-10 06:44:04 | [diff] [blame] | 42 | #include "services/network/public/cpp/cors/cors_error_status.h" |
John Abd-El-Malek | 1df6179 | 2018-01-12 20:40:45 | [diff] [blame] | 43 | #include "services/network/public/cpp/resource_request.h" |
Yutaka Hirano | 6eddf738 | 2018-10-10 06:44:04 | [diff] [blame] | 44 | #include "services/network/public/mojom/cors.mojom-shared.h" |
Ken Rockot | 54311e6 | 2018-02-10 19:01:52 | [diff] [blame] | 45 | #include "services/network/public/mojom/url_loader.mojom.h" |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 46 | #include "storage/common/fileapi/file_system_util.h" |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 47 | #include "url/gurl.h" |
| 48 | |
| 49 | #if defined(OS_WIN) |
| 50 | #include "base/win/shortcut.h" |
| 51 | #endif |
| 52 | |
| 53 | namespace content { |
| 54 | namespace { |
| 55 | |
| 56 | constexpr size_t kDefaultFileUrlPipeSize = 65536; |
| 57 | |
| 58 | // Because this makes things simpler. |
| 59 | static_assert(kDefaultFileUrlPipeSize >= net::kMaxBytesToSniff, |
| 60 | "Default file data pipe size must be at least as large as a MIME-" |
| 61 | "type sniffing buffer."); |
| 62 | |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 63 | // Policy to control how a FileURLLoader will handle directory URLs. |
| 64 | enum class DirectoryLoadingPolicy { |
| 65 | // File paths which refer to directories are allowed and will load as an |
| 66 | // HTML directory listing. |
| 67 | kRespondWithListing, |
| 68 | |
| 69 | // File paths which refer to directories are treated as non-existent and |
| 70 | // will result in FILE_NOT_FOUND errors. |
| 71 | kFail, |
| 72 | }; |
| 73 | |
| 74 | // Policy to control whether or not file access constraints imposed by content |
| 75 | // or its embedder should be honored by a FileURLLoader. |
| 76 | enum class FileAccessPolicy { |
| 77 | // Enforces file acccess policy defined by content and/or its embedder. |
| 78 | kRestricted, |
| 79 | |
| 80 | // Ignores file access policy, allowing contents to be loaded from any |
| 81 | // resolvable file path given. |
| 82 | kUnrestricted, |
| 83 | }; |
| 84 | |
| 85 | // Policy to control whether or not a FileURLLoader should follow filesystem |
| 86 | // links (e.g. Windows shortcuts) where applicable. |
| 87 | enum class LinkFollowingPolicy { |
| 88 | kFollow, |
| 89 | kDoNotFollow, |
| 90 | }; |
| 91 | |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 92 | GURL AppendUrlSeparator(const GURL& url) { |
| 93 | std::string new_path = url.path() + '/'; |
| 94 | GURL::Replacements replacements; |
| 95 | replacements.SetPathStr(new_path); |
| 96 | return url.ReplaceComponents(replacements); |
| 97 | } |
| 98 | |
Yutaka Hirano | 6eddf738 | 2018-10-10 06:44:04 | [diff] [blame] | 99 | bool ShouldFailRequestDueToCORS(const network::ResourceRequest& request) { |
| 100 | if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 101 | switches::kDisableWebSecurity)) { |
| 102 | return false; |
| 103 | } |
| 104 | |
| 105 | const auto mode = request.fetch_request_mode; |
| 106 | if (mode == network::mojom::FetchRequestMode::kNavigate || |
| 107 | mode == network::mojom::FetchRequestMode::kNoCORS) { |
| 108 | return false; |
| 109 | } |
| 110 | |
| 111 | if (!request.request_initiator) |
| 112 | return true; |
| 113 | |
| 114 | return !request.request_initiator->IsSameOriginWith( |
| 115 | url::Origin::Create(request.url)); |
| 116 | } |
| 117 | |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 118 | class FileURLDirectoryLoader |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 119 | : public network::mojom::URLLoader, |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 120 | public net::DirectoryLister::DirectoryListerDelegate { |
| 121 | public: |
Chris Mumford | 8f81266 | 2018-02-22 00:27:57 | [diff] [blame] | 122 | static void CreateAndStart( |
| 123 | const base::FilePath& profile_path, |
| 124 | const network::ResourceRequest& request, |
| 125 | network::mojom::URLLoaderRequest loader, |
| 126 | network::mojom::URLLoaderClientPtrInfo client_info, |
| 127 | std::unique_ptr<FileURLLoaderObserver> observer, |
| 128 | scoped_refptr<net::HttpResponseHeaders> response_headers) { |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 129 | // Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr |
| 130 | // bindings are alive - essentially until either the client gives up or all |
| 131 | // file data has been sent to it. |
| 132 | auto* file_url_loader = new FileURLDirectoryLoader; |
| 133 | file_url_loader->Start(profile_path, request, std::move(loader), |
Chris Mumford | 8f81266 | 2018-02-22 00:27:57 | [diff] [blame] | 134 | std::move(client_info), std::move(observer), |
| 135 | std::move(response_headers)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 136 | } |
| 137 | |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 138 | // network::mojom::URLLoader: |
Jun Cai | 605ff0e7 | 2018-06-12 22:29:20 | [diff] [blame] | 139 | void FollowRedirect(const base::Optional<std::vector<std::string>>& |
| 140 | to_be_removed_request_headers, |
| 141 | const base::Optional<net::HttpRequestHeaders>& |
Chong Zhang | f19dde9 | 2018-05-23 04:33:59 | [diff] [blame] | 142 | modified_request_headers) override {} |
arthursonzogni | 2e1524a7 | 2017-12-18 16:53:26 | [diff] [blame] | 143 | void ProceedWithResponse() override { NOTREACHED(); } |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 144 | void SetPriority(net::RequestPriority priority, |
| 145 | int32_t intra_priority_value) override {} |
| 146 | void PauseReadingBodyFromNet() override {} |
| 147 | void ResumeReadingBodyFromNet() override {} |
| 148 | |
| 149 | private: |
| 150 | FileURLDirectoryLoader() : binding_(this) {} |
| 151 | ~FileURLDirectoryLoader() override = default; |
| 152 | |
| 153 | void Start(const base::FilePath& profile_path, |
John Abd-El-Malek | 1df6179 | 2018-01-12 20:40:45 | [diff] [blame] | 154 | const network::ResourceRequest& request, |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 155 | network::mojom::URLLoaderRequest loader, |
| 156 | network::mojom::URLLoaderClientPtrInfo client_info, |
Chris Mumford | 8f81266 | 2018-02-22 00:27:57 | [diff] [blame] | 157 | std::unique_ptr<content::FileURLLoaderObserver> observer, |
| 158 | scoped_refptr<net::HttpResponseHeaders> response_headers) { |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 159 | binding_.Bind(std::move(loader)); |
| 160 | binding_.set_connection_error_handler(base::BindOnce( |
| 161 | &FileURLDirectoryLoader::OnConnectionError, base::Unretained(this))); |
| 162 | |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 163 | network::mojom::URLLoaderClientPtr client; |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 164 | client.Bind(std::move(client_info)); |
| 165 | |
| 166 | if (!net::FileURLToFilePath(request.url, &path_)) { |
Takashi Toyoshima | aa27866 | 2017-11-20 11:11:26 | [diff] [blame] | 167 | client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 168 | return; |
| 169 | } |
| 170 | |
| 171 | base::File::Info info; |
| 172 | if (!base::GetFileInfo(path_, &info) || !info.is_directory) { |
Takashi Toyoshima | aa27866 | 2017-11-20 11:11:26 | [diff] [blame] | 173 | client->OnComplete( |
| 174 | network::URLLoaderCompletionStatus(net::ERR_FILE_NOT_FOUND)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 175 | return; |
| 176 | } |
| 177 | |
| 178 | if (!GetContentClient()->browser()->IsFileAccessAllowed( |
| 179 | path_, base::MakeAbsoluteFilePath(path_), profile_path)) { |
Takashi Toyoshima | aa27866 | 2017-11-20 11:11:26 | [diff] [blame] | 180 | client->OnComplete( |
| 181 | network::URLLoaderCompletionStatus(net::ERR_ACCESS_DENIED)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 182 | return; |
| 183 | } |
| 184 | |
| 185 | mojo::DataPipe pipe(kDefaultFileUrlPipeSize); |
| 186 | if (!pipe.consumer_handle.is_valid()) { |
Takashi Toyoshima | aa27866 | 2017-11-20 11:11:26 | [diff] [blame] | 187 | client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 188 | return; |
| 189 | } |
| 190 | |
John Abd-El-Malek | 4624803 | 2018-01-17 19:11:23 | [diff] [blame] | 191 | network::ResourceResponseHead head; |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 192 | head.mime_type = "text/html"; |
| 193 | head.charset = "utf-8"; |
Marijn Kruisselbrink | 9ebd7ba | 2018-06-11 23:18:04 | [diff] [blame] | 194 | client->OnReceiveResponse(head); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 195 | client->OnStartLoadingResponseBody(std::move(pipe.consumer_handle)); |
| 196 | client_ = std::move(client); |
| 197 | |
| 198 | lister_ = std::make_unique<net::DirectoryLister>(path_, this); |
| 199 | lister_->Start(); |
| 200 | |
| 201 | data_producer_ = std::make_unique<mojo::StringDataPipeProducer>( |
| 202 | std::move(pipe.producer_handle)); |
| 203 | } |
| 204 | |
| 205 | void OnConnectionError() { |
| 206 | binding_.Close(); |
| 207 | MaybeDeleteSelf(); |
| 208 | } |
| 209 | |
| 210 | void MaybeDeleteSelf() { |
| 211 | if (!binding_.is_bound() && !client_.is_bound() && !lister_) |
| 212 | delete this; |
| 213 | } |
| 214 | |
| 215 | // net::DirectoryLister::DirectoryListerDelegate: |
| 216 | void OnListFile( |
| 217 | const net::DirectoryLister::DirectoryListerData& data) override { |
| 218 | if (!wrote_header_) { |
| 219 | wrote_header_ = true; |
| 220 | |
| 221 | #if defined(OS_WIN) |
| 222 | const base::string16& title = path_.value(); |
Fabrice de Gans-Riberi | 91fa3505 | 2018-05-07 19:33:48 | [diff] [blame] | 223 | #elif defined(OS_POSIX) || defined(OS_FUCHSIA) |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 224 | const base::string16& title = |
| 225 | base::WideToUTF16(base::SysNativeMBToWide(path_.value())); |
| 226 | #endif |
| 227 | pending_data_.append(net::GetDirectoryListingHeader(title)); |
| 228 | |
| 229 | // If not a top-level directory, add a link to the parent directory. To |
| 230 | // figure this out, first normalize |path_| by stripping trailing |
| 231 | // separators. Then compare the result to its DirName(). For the top-level |
| 232 | // directory, e.g. "/" or "c:\\", the normalized path is equal to its own |
| 233 | // DirName(). |
| 234 | base::FilePath stripped_path = path_.StripTrailingSeparators(); |
| 235 | if (stripped_path != stripped_path.DirName()) |
| 236 | pending_data_.append(net::GetParentDirectoryLink()); |
| 237 | } |
| 238 | |
| 239 | // Skip current / parent links from the listing. |
| 240 | base::FilePath filename = data.info.GetName(); |
| 241 | if (filename.value() != base::FilePath::kCurrentDirectory && |
| 242 | filename.value() != base::FilePath::kParentDirectory) { |
| 243 | #if defined(OS_WIN) |
| 244 | std::string raw_bytes; // Empty on Windows means UTF-8 encoded name. |
Fabrice de Gans-Riberi | 91fa3505 | 2018-05-07 19:33:48 | [diff] [blame] | 245 | #elif defined(OS_POSIX) || defined(OS_FUCHSIA) |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 246 | const std::string& raw_bytes = filename.value(); |
| 247 | #endif |
| 248 | pending_data_.append(net::GetDirectoryListingEntry( |
| 249 | filename.LossyDisplayName(), raw_bytes, data.info.IsDirectory(), |
| 250 | data.info.GetSize(), data.info.GetLastModifiedTime())); |
| 251 | } |
| 252 | |
| 253 | MaybeTransferPendingData(); |
| 254 | } |
| 255 | |
| 256 | void OnListDone(int error) override { |
| 257 | listing_result_ = error; |
| 258 | lister_.reset(); |
| 259 | MaybeDeleteSelf(); |
| 260 | } |
| 261 | |
| 262 | void MaybeTransferPendingData() { |
| 263 | if (transfer_in_progress_) |
| 264 | return; |
| 265 | |
| 266 | transfer_in_progress_ = true; |
| 267 | data_producer_->Write(pending_data_, |
Daniel Murphy | 0ecba61 | 2018-02-07 20:56:18 | [diff] [blame] | 268 | mojo::StringDataPipeProducer::AsyncWritingMode:: |
| 269 | STRING_MAY_BE_INVALIDATED_BEFORE_COMPLETION, |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 270 | base::BindOnce(&FileURLDirectoryLoader::OnDataWritten, |
| 271 | base::Unretained(this))); |
| 272 | // The producer above will have already copied any parts of |pending_data_| |
| 273 | // that couldn't be written immediately, so we can wipe it out here to begin |
| 274 | // accumulating more data. |
| 275 | pending_data_.clear(); |
| 276 | } |
| 277 | |
| 278 | void OnDataWritten(MojoResult result) { |
| 279 | transfer_in_progress_ = false; |
| 280 | |
| 281 | int completion_status; |
| 282 | if (result == MOJO_RESULT_OK) { |
| 283 | if (!pending_data_.empty()) { |
| 284 | // Keep flushing the data buffer as long as it's non-empty and pipe |
| 285 | // writes are succeeding. |
| 286 | MaybeTransferPendingData(); |
| 287 | return; |
| 288 | } |
| 289 | |
| 290 | // If there's no pending data but the lister is still active, we simply |
| 291 | // wait for more listing results. |
| 292 | if (lister_) |
| 293 | return; |
| 294 | |
| 295 | // At this point we know the listing is complete and all available data |
| 296 | // has been transferred. We inherit the status of the listing operation. |
| 297 | completion_status = listing_result_; |
| 298 | } else { |
| 299 | completion_status = net::ERR_FAILED; |
| 300 | } |
| 301 | |
arthursonzogni | 3a4ca9f | 2017-12-07 17:58:34 | [diff] [blame] | 302 | // All the data has been written now. Close the data pipe. The consumer will |
| 303 | // be notified that there will be no more data to read from now. |
| 304 | data_producer_.reset(); |
| 305 | |
Takashi Toyoshima | aa27866 | 2017-11-20 11:11:26 | [diff] [blame] | 306 | client_->OnComplete(network::URLLoaderCompletionStatus(completion_status)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 307 | client_.reset(); |
arthursonzogni | 3a4ca9f | 2017-12-07 17:58:34 | [diff] [blame] | 308 | |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 309 | MaybeDeleteSelf(); |
| 310 | } |
| 311 | |
| 312 | base::FilePath path_; |
| 313 | std::unique_ptr<net::DirectoryLister> lister_; |
| 314 | bool wrote_header_ = false; |
| 315 | int listing_result_; |
| 316 | |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 317 | mojo::Binding<network::mojom::URLLoader> binding_; |
| 318 | network::mojom::URLLoaderClientPtr client_; |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 319 | |
| 320 | std::unique_ptr<mojo::StringDataPipeProducer> data_producer_; |
| 321 | std::string pending_data_; |
| 322 | bool transfer_in_progress_ = false; |
| 323 | |
| 324 | DISALLOW_COPY_AND_ASSIGN(FileURLDirectoryLoader); |
| 325 | }; |
| 326 | |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 327 | class FileURLLoader : public network::mojom::URLLoader { |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 328 | public: |
Ken Rockot | a0dfaca1 | 2018-02-15 07:26:25 | [diff] [blame] | 329 | static void CreateAndStart( |
| 330 | const base::FilePath& profile_path, |
| 331 | const network::ResourceRequest& request, |
| 332 | network::mojom::URLLoaderRequest loader, |
| 333 | network::mojom::URLLoaderClientPtrInfo client_info, |
| 334 | DirectoryLoadingPolicy directory_loading_policy, |
| 335 | FileAccessPolicy file_access_policy, |
| 336 | LinkFollowingPolicy link_following_policy, |
| 337 | std::unique_ptr<FileURLLoaderObserver> observer, |
| 338 | scoped_refptr<net::HttpResponseHeaders> extra_response_headers) { |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 339 | // Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr |
| 340 | // bindings are alive - essentially until either the client gives up or all |
| 341 | // file data has been sent to it. |
| 342 | auto* file_url_loader = new FileURLLoader; |
Ken Rockot | a0dfaca1 | 2018-02-15 07:26:25 | [diff] [blame] | 343 | file_url_loader->Start( |
| 344 | profile_path, request, std::move(loader), std::move(client_info), |
| 345 | directory_loading_policy, file_access_policy, link_following_policy, |
| 346 | std::move(observer), std::move(extra_response_headers)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 347 | } |
| 348 | |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 349 | // network::mojom::URLLoader: |
Jun Cai | 605ff0e7 | 2018-06-12 22:29:20 | [diff] [blame] | 350 | void FollowRedirect(const base::Optional<std::vector<std::string>>& |
| 351 | to_be_removed_request_headers, |
| 352 | const base::Optional<net::HttpRequestHeaders>& |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 353 | modified_request_headers) override { |
| 354 | std::unique_ptr<RedirectData> redirect_data = std::move(redirect_data_); |
| 355 | if (redirect_data->is_directory) { |
| 356 | FileURLDirectoryLoader::CreateAndStart( |
| 357 | redirect_data->profile_path, redirect_data->request, |
| 358 | binding_.Unbind(), client_.PassInterface(), |
| 359 | std::move(redirect_data->observer), |
| 360 | std::move(redirect_data->extra_response_headers)); |
| 361 | } else { |
| 362 | FileURLLoader::CreateAndStart( |
| 363 | redirect_data->profile_path, redirect_data->request, |
| 364 | binding_.Unbind(), client_.PassInterface(), |
| 365 | redirect_data->directory_loading_policy, |
| 366 | redirect_data->file_access_policy, |
| 367 | redirect_data->link_following_policy, |
| 368 | std::move(redirect_data->observer), |
| 369 | std::move(redirect_data->extra_response_headers)); |
| 370 | } |
| 371 | MaybeDeleteSelf(); |
| 372 | } |
arthursonzogni | 2e1524a7 | 2017-12-18 16:53:26 | [diff] [blame] | 373 | void ProceedWithResponse() override {} |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 374 | void SetPriority(net::RequestPriority priority, |
| 375 | int32_t intra_priority_value) override {} |
| 376 | void PauseReadingBodyFromNet() override {} |
| 377 | void ResumeReadingBodyFromNet() override {} |
| 378 | |
| 379 | private: |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 380 | // Used to save outstanding redirect data while waiting for FollowRedirect |
| 381 | // to be called. Values default to their most restrictive in case they are |
| 382 | // not set. |
| 383 | struct RedirectData { |
| 384 | bool is_directory = false; |
| 385 | base::FilePath profile_path; |
| 386 | network::ResourceRequest request; |
| 387 | network::mojom::URLLoaderRequest loader; |
| 388 | DirectoryLoadingPolicy directory_loading_policy = |
| 389 | DirectoryLoadingPolicy::kFail; |
| 390 | FileAccessPolicy file_access_policy = FileAccessPolicy::kRestricted; |
| 391 | LinkFollowingPolicy link_following_policy = |
| 392 | LinkFollowingPolicy::kDoNotFollow; |
| 393 | std::unique_ptr<FileURLLoaderObserver> observer; |
| 394 | scoped_refptr<net::HttpResponseHeaders> extra_response_headers; |
| 395 | }; |
| 396 | |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 397 | FileURLLoader() : binding_(this) {} |
| 398 | ~FileURLLoader() override = default; |
| 399 | |
| 400 | void Start(const base::FilePath& profile_path, |
John Abd-El-Malek | 1df6179 | 2018-01-12 20:40:45 | [diff] [blame] | 401 | const network::ResourceRequest& request, |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 402 | network::mojom::URLLoaderRequest loader, |
| 403 | network::mojom::URLLoaderClientPtrInfo client_info, |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 404 | DirectoryLoadingPolicy directory_loading_policy, |
| 405 | FileAccessPolicy file_access_policy, |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 406 | LinkFollowingPolicy link_following_policy, |
Ken Rockot | a0dfaca1 | 2018-02-15 07:26:25 | [diff] [blame] | 407 | std::unique_ptr<FileURLLoaderObserver> observer, |
| 408 | scoped_refptr<net::HttpResponseHeaders> extra_response_headers) { |
John Abd-El-Malek | 4624803 | 2018-01-17 19:11:23 | [diff] [blame] | 409 | network::ResourceResponseHead head; |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 410 | head.request_start = base::TimeTicks::Now(); |
| 411 | head.response_start = base::TimeTicks::Now(); |
Ken Rockot | a0dfaca1 | 2018-02-15 07:26:25 | [diff] [blame] | 412 | head.headers = extra_response_headers; |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 413 | binding_.Bind(std::move(loader)); |
| 414 | binding_.set_connection_error_handler(base::BindOnce( |
| 415 | &FileURLLoader::OnConnectionError, base::Unretained(this))); |
| 416 | |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 417 | client_.Bind(std::move(client_info)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 418 | |
| 419 | base::FilePath path; |
| 420 | if (!net::FileURLToFilePath(request.url, &path)) { |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 421 | OnClientComplete(net::ERR_FAILED, std::move(observer)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 422 | return; |
| 423 | } |
| 424 | |
| 425 | base::File::Info info; |
| 426 | if (!base::GetFileInfo(path, &info)) { |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 427 | OnClientComplete(net::ERR_FILE_NOT_FOUND, std::move(observer)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 428 | return; |
| 429 | } |
| 430 | |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 431 | if (info.is_directory) { |
| 432 | if (directory_loading_policy == DirectoryLoadingPolicy::kFail) { |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 433 | OnClientComplete(net::ERR_FILE_NOT_FOUND, std::move(observer)); |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 434 | return; |
| 435 | } |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 436 | |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 437 | DCHECK_EQ(directory_loading_policy, |
| 438 | DirectoryLoadingPolicy::kRespondWithListing); |
| 439 | |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 440 | net::RedirectInfo redirect_info; |
| 441 | redirect_info.new_method = "GET"; |
| 442 | redirect_info.status_code = 301; |
| 443 | redirect_info.new_url = path.EndsWithSeparator() |
| 444 | ? request.url |
| 445 | : AppendUrlSeparator(request.url); |
| 446 | head.encoded_data_length = 0; |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 447 | |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 448 | redirect_data_ = std::make_unique<RedirectData>(); |
| 449 | redirect_data_->is_directory = true; |
| 450 | redirect_data_->profile_path = std::move(profile_path); |
| 451 | redirect_data_->request = request; |
| 452 | redirect_data_->directory_loading_policy = directory_loading_policy; |
| 453 | redirect_data_->file_access_policy = file_access_policy; |
| 454 | redirect_data_->link_following_policy = link_following_policy; |
| 455 | redirect_data_->request.url = redirect_info.new_url; |
| 456 | redirect_data_->observer = std::move(observer); |
| 457 | redirect_data_->extra_response_headers = |
| 458 | std::move(extra_response_headers); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 459 | |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 460 | client_->OnReceiveRedirect(redirect_info, head); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 461 | return; |
| 462 | } |
| 463 | |
| 464 | #if defined(OS_WIN) |
| 465 | base::FilePath shortcut_target; |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 466 | if (link_following_policy == LinkFollowingPolicy::kFollow && |
| 467 | base::LowerCaseEqualsASCII(path.Extension(), ".lnk") && |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 468 | base::win::ResolveShortcut(path, &shortcut_target, nullptr)) { |
| 469 | // Follow Windows shortcuts |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 470 | redirect_data_ = std::make_unique<RedirectData>(); |
| 471 | if (!base::GetFileInfo(shortcut_target, &info)) { |
| 472 | OnClientComplete(net::ERR_FILE_NOT_FOUND, std::move(observer)); |
| 473 | return; |
| 474 | } |
| 475 | |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 476 | GURL new_url = net::FilePathToFileURL(shortcut_target); |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 477 | if (info.is_directory && !path.EndsWithSeparator()) |
| 478 | new_url = AppendUrlSeparator(new_url); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 479 | |
| 480 | net::RedirectInfo redirect_info; |
| 481 | redirect_info.new_method = "GET"; |
| 482 | redirect_info.status_code = 301; |
| 483 | redirect_info.new_url = new_url; |
| 484 | head.encoded_data_length = 0; |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 485 | |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 486 | redirect_data_->is_directory = info.is_directory; |
| 487 | redirect_data_->profile_path = std::move(profile_path); |
| 488 | redirect_data_->request = request; |
| 489 | redirect_data_->directory_loading_policy = directory_loading_policy; |
| 490 | redirect_data_->file_access_policy = file_access_policy; |
| 491 | redirect_data_->link_following_policy = link_following_policy; |
| 492 | redirect_data_->request.url = redirect_info.new_url; |
| 493 | redirect_data_->observer = std::move(observer); |
| 494 | redirect_data_->extra_response_headers = |
| 495 | std::move(extra_response_headers); |
| 496 | |
| 497 | client_->OnReceiveRedirect(redirect_info, head); |
| 498 | return; |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 499 | } |
| 500 | #endif // defined(OS_WIN) |
| 501 | |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 502 | if (file_access_policy == FileAccessPolicy::kRestricted && |
| 503 | !GetContentClient()->browser()->IsFileAccessAllowed( |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 504 | path, base::MakeAbsoluteFilePath(path), profile_path)) { |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 505 | OnClientComplete(net::ERR_ACCESS_DENIED, std::move(observer)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 506 | return; |
| 507 | } |
| 508 | |
| 509 | mojo::DataPipe pipe(kDefaultFileUrlPipeSize); |
| 510 | if (!pipe.consumer_handle.is_valid()) { |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 511 | OnClientComplete(net::ERR_FAILED, std::move(observer)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 512 | return; |
| 513 | } |
| 514 | |
| 515 | // Should never be possible for this to be a directory. If FileURLLoader |
| 516 | // is used to respond to a directory request, it must be because the URL |
| 517 | // path didn't have a trailing path separator. In that case we finish with |
| 518 | // a redirect above which will in turn be handled by FileURLDirectoryLoader. |
| 519 | DCHECK(!info.is_directory); |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 520 | if (observer) |
| 521 | observer->OnStart(); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 522 | |
| 523 | base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ); |
Chris Mumford | 8f81266 | 2018-02-22 00:27:57 | [diff] [blame] | 524 | if (!file.IsValid()) { |
Mikhail Atuchin | 9af3ff8c | 2018-04-19 21:15:10 | [diff] [blame] | 525 | if (observer) { |
| 526 | observer->OnBytesRead(nullptr, 0u, file.error_details()); |
Chris Mumford | 8f81266 | 2018-02-22 00:27:57 | [diff] [blame] | 527 | observer->OnDoneReading(); |
Mikhail Atuchin | 9af3ff8c | 2018-04-19 21:15:10 | [diff] [blame] | 528 | } |
| 529 | net::Error net_error = net::FileErrorToNetError(file.error_details()); |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 530 | client_->OnComplete(network::URLLoaderCompletionStatus(net_error)); |
| 531 | client_.reset(); |
Chris Mumford | 8f81266 | 2018-02-22 00:27:57 | [diff] [blame] | 532 | MaybeDeleteSelf(); |
| 533 | return; |
| 534 | } |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 535 | char initial_read_buffer[net::kMaxBytesToSniff]; |
| 536 | int initial_read_result = |
| 537 | file.ReadAtCurrentPos(initial_read_buffer, net::kMaxBytesToSniff); |
| 538 | if (initial_read_result < 0) { |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 539 | base::File::Error read_error = base::File::GetLastFileError(); |
| 540 | DCHECK_NE(base::File::FILE_OK, read_error); |
| 541 | if (observer) { |
Mikhail Atuchin | 9af3ff8c | 2018-04-19 21:15:10 | [diff] [blame] | 542 | // This can happen when the file is unreadable (which can happen during |
| 543 | // corruption). We need to be sure to inform |
| 544 | // the observer that we've finished reading so that it can proceed. |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 545 | observer->OnBytesRead(nullptr, 0u, read_error); |
| 546 | observer->OnDoneReading(); |
| 547 | } |
Mikhail Atuchin | 9af3ff8c | 2018-04-19 21:15:10 | [diff] [blame] | 548 | net::Error net_error = net::FileErrorToNetError(read_error); |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 549 | client_->OnComplete(network::URLLoaderCompletionStatus(net_error)); |
| 550 | client_.reset(); |
| 551 | MaybeDeleteSelf(); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 552 | return; |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 553 | } else if (observer) { |
| 554 | observer->OnBytesRead(initial_read_buffer, initial_read_result, |
| 555 | base::File::FILE_OK); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 556 | } |
| 557 | size_t initial_read_size = static_cast<size_t>(initial_read_result); |
| 558 | |
| 559 | std::string range_header; |
| 560 | net::HttpByteRange byte_range; |
| 561 | if (request.headers.GetHeader(net::HttpRequestHeaders::kRange, |
| 562 | &range_header)) { |
| 563 | // Handle a simple Range header for a single range. |
| 564 | std::vector<net::HttpByteRange> ranges; |
| 565 | bool fail = false; |
| 566 | if (net::HttpUtil::ParseRangeHeader(range_header, &ranges) && |
| 567 | ranges.size() == 1) { |
| 568 | byte_range = ranges[0]; |
| 569 | if (!byte_range.ComputeBounds(info.size)) |
| 570 | fail = true; |
| 571 | } else { |
| 572 | fail = true; |
| 573 | } |
| 574 | |
| 575 | if (fail) { |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 576 | OnClientComplete(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, |
| 577 | std::move(observer)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 578 | return; |
| 579 | } |
| 580 | } |
| 581 | |
| 582 | size_t first_byte_to_send = 0; |
| 583 | size_t total_bytes_to_send = static_cast<size_t>(info.size); |
Antonio Gomes | 402dbf5 | 2018-08-02 11:06:41 | [diff] [blame] | 584 | |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 585 | if (byte_range.IsValid()) { |
| 586 | first_byte_to_send = |
| 587 | static_cast<size_t>(byte_range.first_byte_position()); |
| 588 | total_bytes_to_send = |
| 589 | static_cast<size_t>(byte_range.last_byte_position()) - |
| 590 | first_byte_to_send + 1; |
| 591 | } |
| 592 | |
Eric Roman | 7f6604c | 2018-10-02 22:50:02 | [diff] [blame] | 593 | total_bytes_written_ = static_cast<size_t>(total_bytes_to_send); |
| 594 | |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 595 | head.content_length = base::saturated_cast<int64_t>(total_bytes_to_send); |
| 596 | |
| 597 | if (first_byte_to_send < initial_read_size) { |
| 598 | // Write any data we read for MIME sniffing, constraining by range where |
| 599 | // applicable. This will always fit in the pipe (see assertion near |
| 600 | // |kDefaultFileUrlPipeSize| definition). |
| 601 | uint32_t write_size = std::min( |
| 602 | static_cast<uint32_t>(initial_read_size - first_byte_to_send), |
| 603 | static_cast<uint32_t>(total_bytes_to_send)); |
| 604 | const uint32_t expected_write_size = write_size; |
| 605 | MojoResult result = pipe.producer_handle->WriteData( |
| 606 | &initial_read_buffer[first_byte_to_send], &write_size, |
| 607 | MOJO_WRITE_DATA_FLAG_NONE); |
| 608 | if (result != MOJO_RESULT_OK || write_size != expected_write_size) { |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 609 | OnFileWritten(std::move(observer), result); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 610 | return; |
| 611 | } |
| 612 | |
| 613 | // Discount the bytes we just sent from the total range. |
| 614 | first_byte_to_send = initial_read_size; |
| 615 | total_bytes_to_send -= write_size; |
| 616 | } |
| 617 | |
| 618 | if (!net::GetMimeTypeFromFile(path, &head.mime_type)) { |
Matt Menke | fcbb1bd7 | 2018-01-31 21:53:12 | [diff] [blame] | 619 | net::SniffMimeType( |
| 620 | initial_read_buffer, initial_read_result, request.url, head.mime_type, |
| 621 | GetContentClient()->browser()->ForceSniffingFileUrlsForHtml() |
| 622 | ? net::ForceSniffFileUrlsForHtml::kEnabled |
| 623 | : net::ForceSniffFileUrlsForHtml::kDisabled, |
| 624 | &head.mime_type); |
Makoto Shimazu | 21950e8 | 2018-08-02 12:37:14 | [diff] [blame] | 625 | head.did_mime_sniff = true; |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 626 | } |
Chris Mumford | 8f81266 | 2018-02-22 00:27:57 | [diff] [blame] | 627 | if (head.headers) { |
| 628 | head.headers->AddHeader( |
| 629 | base::StringPrintf("%s: %s", net::HttpRequestHeaders::kContentType, |
| 630 | head.mime_type.c_str())); |
| 631 | } |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 632 | client_->OnReceiveResponse(head); |
| 633 | client_->OnStartLoadingResponseBody(std::move(pipe.consumer_handle)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 634 | |
| 635 | if (total_bytes_to_send == 0) { |
| 636 | // There's definitely no more data, so we're already done. |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 637 | OnFileWritten(std::move(observer), MOJO_RESULT_OK); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 638 | return; |
| 639 | } |
| 640 | |
| 641 | // In case of a range request, seek to the appropriate position before |
| 642 | // sending the remaining bytes asynchronously. Under normal conditions |
| 643 | // (i.e., no range request) this Seek is effectively a no-op. |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 644 | int new_position = file.Seek(base::File::FROM_BEGIN, |
| 645 | static_cast<int64_t>(first_byte_to_send)); |
| 646 | if (observer) |
| 647 | observer->OnSeekComplete(new_position); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 648 | |
| 649 | data_producer_ = std::make_unique<mojo::FileDataPipeProducer>( |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 650 | std::move(pipe.producer_handle), std::move(observer)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 651 | data_producer_->WriteFromFile( |
| 652 | std::move(file), total_bytes_to_send, |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 653 | base::BindOnce(&FileURLLoader::OnFileWritten, base::Unretained(this), |
| 654 | nullptr)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 655 | } |
| 656 | |
| 657 | void OnConnectionError() { |
| 658 | binding_.Close(); |
| 659 | MaybeDeleteSelf(); |
| 660 | } |
| 661 | |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 662 | void OnClientComplete(net::Error net_error, |
| 663 | std::unique_ptr<FileURLLoaderObserver> observer) { |
| 664 | client_->OnComplete(network::URLLoaderCompletionStatus(net_error)); |
| 665 | client_.reset(); |
| 666 | if (observer) |
| 667 | observer->OnDoneReading(); |
| 668 | MaybeDeleteSelf(); |
| 669 | } |
| 670 | |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 671 | void MaybeDeleteSelf() { |
| 672 | if (!binding_.is_bound() && !client_.is_bound()) |
| 673 | delete this; |
| 674 | } |
| 675 | |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 676 | void OnFileWritten(std::unique_ptr<FileURLLoaderObserver> observer, |
| 677 | MojoResult result) { |
arthursonzogni | 3a4ca9f | 2017-12-07 17:58:34 | [diff] [blame] | 678 | // All the data has been written now. Close the data pipe. The consumer will |
| 679 | // be notified that there will be no more data to read from now. |
| 680 | data_producer_.reset(); |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 681 | if (observer) |
| 682 | observer->OnDoneReading(); |
arthursonzogni | 3a4ca9f | 2017-12-07 17:58:34 | [diff] [blame] | 683 | |
Antonio Gomes | 402dbf5 | 2018-08-02 11:06:41 | [diff] [blame] | 684 | if (result == MOJO_RESULT_OK) { |
| 685 | network::URLLoaderCompletionStatus status(net::OK); |
| 686 | status.encoded_data_length = total_bytes_written_; |
| 687 | status.encoded_body_length = total_bytes_written_; |
| 688 | status.decoded_body_length = total_bytes_written_; |
| 689 | client_->OnComplete(status); |
| 690 | } else { |
Takashi Toyoshima | aa27866 | 2017-11-20 11:11:26 | [diff] [blame] | 691 | client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); |
Antonio Gomes | 402dbf5 | 2018-08-02 11:06:41 | [diff] [blame] | 692 | } |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 693 | client_.reset(); |
| 694 | MaybeDeleteSelf(); |
| 695 | } |
| 696 | |
| 697 | std::unique_ptr<mojo::FileDataPipeProducer> data_producer_; |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 698 | mojo::Binding<network::mojom::URLLoader> binding_; |
| 699 | network::mojom::URLLoaderClientPtr client_; |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 700 | std::unique_ptr<RedirectData> redirect_data_; |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 701 | |
Eric Roman | 7f6604c | 2018-10-02 22:50:02 | [diff] [blame] | 702 | // In case of successful loads, this holds the total number of bytes written |
| 703 | // to the response (this may be smaller than the total size of the file when |
| 704 | // a byte range was requested). |
Antonio Gomes | 402dbf5 | 2018-08-02 11:06:41 | [diff] [blame] | 705 | // It is used to set some of the URLLoaderCompletionStatus data passed back |
| 706 | // to the URLLoaderClients (eg SimpleURLLoader). |
| 707 | size_t total_bytes_written_ = 0; |
| 708 | |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 709 | DISALLOW_COPY_AND_ASSIGN(FileURLLoader); |
| 710 | }; |
| 711 | |
| 712 | } // namespace |
| 713 | |
| 714 | FileURLLoaderFactory::FileURLLoaderFactory( |
| 715 | const base::FilePath& profile_path, |
| 716 | scoped_refptr<base::SequencedTaskRunner> task_runner) |
| 717 | : profile_path_(profile_path), task_runner_(std::move(task_runner)) {} |
| 718 | |
| 719 | FileURLLoaderFactory::~FileURLLoaderFactory() = default; |
| 720 | |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 721 | void FileURLLoaderFactory::CreateLoaderAndStart( |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 722 | network::mojom::URLLoaderRequest loader, |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 723 | int32_t routing_id, |
| 724 | int32_t request_id, |
| 725 | uint32_t options, |
John Abd-El-Malek | 1df6179 | 2018-01-12 20:40:45 | [diff] [blame] | 726 | const network::ResourceRequest& request, |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 727 | network::mojom::URLLoaderClientPtr client, |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 728 | const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { |
| 729 | base::FilePath file_path; |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 730 | if (!net::FileURLToFilePath(request.url, &file_path)) { |
| 731 | client->OnComplete( |
| 732 | network::URLLoaderCompletionStatus(net::ERR_INVALID_URL)); |
| 733 | return; |
| 734 | } |
Yutaka Hirano | 6eddf738 | 2018-10-10 06:44:04 | [diff] [blame] | 735 | |
| 736 | // FileURLLoader doesn't support CORS and it's not covered by CORSURLLoader, |
| 737 | // so we need to reject requests that need CORS manually. |
| 738 | if (ShouldFailRequestDueToCORS(request)) { |
| 739 | client->OnComplete( |
| 740 | network::URLLoaderCompletionStatus(network::CORSErrorStatus( |
| 741 | network::mojom::CORSError::kCORSDisabledScheme))); |
| 742 | return; |
| 743 | } |
| 744 | |
Chris Mumford | 90b1a0d | 2018-09-26 20:10:43 | [diff] [blame] | 745 | if (file_path.EndsWithSeparator() && file_path.IsAbsolute()) { |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 746 | task_runner_->PostTask( |
| 747 | FROM_HERE, |
| 748 | base::BindOnce(&FileURLDirectoryLoader::CreateAndStart, profile_path_, |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 749 | request, std::move(loader), client.PassInterface(), |
Chris Mumford | 8f81266 | 2018-02-22 00:27:57 | [diff] [blame] | 750 | std::unique_ptr<FileURLLoaderObserver>(), nullptr)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 751 | } else { |
| 752 | task_runner_->PostTask( |
| 753 | FROM_HERE, |
| 754 | base::BindOnce(&FileURLLoader::CreateAndStart, profile_path_, request, |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 755 | std::move(loader), client.PassInterface(), |
| 756 | DirectoryLoadingPolicy::kRespondWithListing, |
| 757 | FileAccessPolicy::kRestricted, |
Chris Mumford | b5df8ef2 | 2017-12-20 17:47:42 | [diff] [blame] | 758 | LinkFollowingPolicy::kFollow, |
Ken Rockot | a0dfaca1 | 2018-02-15 07:26:25 | [diff] [blame] | 759 | std::unique_ptr<FileURLLoaderObserver>(), |
| 760 | nullptr /* extra_response_headers */)); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 761 | } |
| 762 | } |
| 763 | |
John Abd-El-Malek | b165dc5 | 2018-01-18 17:12:18 | [diff] [blame] | 764 | void FileURLLoaderFactory::Clone( |
| 765 | network::mojom::URLLoaderFactoryRequest loader) { |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 766 | bindings_.AddBinding(this, std::move(loader)); |
| 767 | } |
| 768 | |
Ken Rockot | a0dfaca1 | 2018-02-15 07:26:25 | [diff] [blame] | 769 | void CreateFileURLLoader( |
| 770 | const network::ResourceRequest& request, |
| 771 | network::mojom::URLLoaderRequest loader, |
| 772 | network::mojom::URLLoaderClientPtr client, |
| 773 | std::unique_ptr<FileURLLoaderObserver> observer, |
| 774 | scoped_refptr<net::HttpResponseHeaders> extra_response_headers) { |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 775 | auto task_runner = base::CreateSequencedTaskRunnerWithTraits( |
Gabriel Charette | b10aeeb | 2018-07-26 20:15:00 | [diff] [blame] | 776 | {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
Ken Rockot | 6414c4d9 | 2017-11-08 19:58:32 | [diff] [blame] | 777 | base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); |
| 778 | task_runner->PostTask( |
| 779 | FROM_HERE, |
| 780 | base::BindOnce(&FileURLLoader::CreateAndStart, base::FilePath(), request, |
| 781 | std::move(loader), client.PassInterface(), |
| 782 | DirectoryLoadingPolicy::kFail, |
| 783 | FileAccessPolicy::kUnrestricted, |
Ken Rockot | a0dfaca1 | 2018-02-15 07:26:25 | [diff] [blame] | 784 | LinkFollowingPolicy::kDoNotFollow, std::move(observer), |
| 785 | std::move(extra_response_headers))); |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 786 | } |
| 787 | |
Antonio Gomes | e1a0219 | 2018-08-01 15:39:44 | [diff] [blame] | 788 | std::unique_ptr<network::mojom::URLLoaderFactory> CreateFileURLLoaderFactory( |
| 789 | const base::FilePath& profile_path) { |
| 790 | return std::make_unique<content::FileURLLoaderFactory>( |
| 791 | profile_path, base::CreateSequencedTaskRunnerWithTraits( |
| 792 | {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| 793 | base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); |
| 794 | } |
| 795 | |
Ken Rockot | 314714c | 2017-11-05 23:36:24 | [diff] [blame] | 796 | } // namespace content |