| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // For loading files, we make use of overlapped i/o to ensure that reading from | 5 // For loading files, we make use of overlapped i/o to ensure that reading from |
| 6 // the filesystem (e.g., a network filesystem) does not block the calling | 6 // the filesystem (e.g., a network filesystem) does not block the calling |
| 7 // thread. An alternative approach would be to use a background thread or pool | 7 // thread. An alternative approach would be to use a background thread or pool |
| 8 // of threads, but it seems better to leverage the operating system's ability | 8 // of threads, but it seems better to leverage the operating system's ability |
| 9 // to do background file reads for us. | 9 // to do background file reads for us. |
| 10 // | 10 // |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 owner_->DidResolve(exists, file_info); | 75 owner_->DidResolve(exists, file_info); |
| 76 } | 76 } |
| 77 | 77 |
| 78 URLRequestFileJob* owner_; | 78 URLRequestFileJob* owner_; |
| 79 | 79 |
| 80 base::Lock lock_; | 80 base::Lock lock_; |
| 81 MessageLoop* owner_loop_; | 81 MessageLoop* owner_loop_; |
| 82 }; | 82 }; |
| 83 #endif | 83 #endif |
| 84 | 84 |
| 85 URLRequestFileJob::URLRequestFileJob(URLRequest* request, |
| 86 const FilePath& file_path) |
| 87 : URLRequestJob(request), |
| 88 file_path_(file_path), |
| 89 ALLOW_THIS_IN_INITIALIZER_LIST( |
| 90 io_callback_(this, &URLRequestFileJob::DidRead)), |
| 91 is_directory_(false), |
| 92 remaining_bytes_(0), |
| 93 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { |
| 94 } |
| 95 |
| 85 // static | 96 // static |
| 86 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, | 97 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, |
| 87 const std::string& scheme) { | 98 const std::string& scheme) { |
| 88 | 99 |
| 89 FilePath file_path; | 100 FilePath file_path; |
| 90 const bool is_file = FileURLToFilePath(request->url(), &file_path); | 101 const bool is_file = FileURLToFilePath(request->url(), &file_path); |
| 91 | 102 |
| 92 #if defined(OS_CHROMEOS) | 103 #if defined(OS_CHROMEOS) |
| 93 // Check file access. | 104 // Check file access. |
| 94 if (AccessDisabled(file_path)) | 105 if (AccessDisabled(file_path)) |
| 95 return new URLRequestErrorJob(request, ERR_ACCESS_DENIED); | 106 return new URLRequestErrorJob(request, ERR_ACCESS_DENIED); |
| 96 #endif | 107 #endif |
| 97 | 108 |
| 98 // We need to decide whether to create URLRequestFileJob for file access or | 109 // We need to decide whether to create URLRequestFileJob for file access or |
| 99 // URLRequestFileDirJob for directory access. To avoid accessing the | 110 // URLRequestFileDirJob for directory access. To avoid accessing the |
| 100 // filesystem, we only look at the path string here. | 111 // filesystem, we only look at the path string here. |
| 101 // The code in the URLRequestFileJob::Start() method discovers that a path, | 112 // The code in the URLRequestFileJob::Start() method discovers that a path, |
| 102 // which doesn't end with a slash, should really be treated as a directory, | 113 // which doesn't end with a slash, should really be treated as a directory, |
| 103 // and it then redirects to the URLRequestFileDirJob. | 114 // and it then redirects to the URLRequestFileDirJob. |
| 104 if (is_file && | 115 if (is_file && |
| 105 file_util::EndsWithSeparator(file_path) && | 116 file_util::EndsWithSeparator(file_path) && |
| 106 file_path.IsAbsolute()) | 117 file_path.IsAbsolute()) |
| 107 return new URLRequestFileDirJob(request, file_path); | 118 return new URLRequestFileDirJob(request, file_path); |
| 108 | 119 |
| 109 // Use a regular file request job for all non-directories (including invalid | 120 // Use a regular file request job for all non-directories (including invalid |
| 110 // file names). | 121 // file names). |
| 111 return new URLRequestFileJob(request, file_path); | 122 return new URLRequestFileJob(request, file_path); |
| 112 } | 123 } |
| 113 | 124 |
| 114 URLRequestFileJob::URLRequestFileJob(URLRequest* request, | 125 #if defined(OS_CHROMEOS) |
| 115 const FilePath& file_path) | 126 static const char* const kLocalAccessWhiteList[] = { |
| 116 : URLRequestJob(request), | 127 "/home/chronos/user/Downloads", |
| 117 file_path_(file_path), | 128 "/media", |
| 118 ALLOW_THIS_IN_INITIALIZER_LIST( | 129 "/mnt/partner_partition", |
| 119 io_callback_(this, &URLRequestFileJob::DidRead)), | 130 "/usr/share/chromeos-assets", |
| 120 is_directory_(false), | 131 "/tmp", |
| 121 remaining_bytes_(0), | 132 "/var/log", |
| 122 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { | 133 }; |
| 134 |
| 135 // static |
| 136 bool URLRequestFileJob::AccessDisabled(const FilePath& file_path) { |
| 137 if (URLRequest::IsFileAccessAllowed()) { // for tests. |
| 138 return false; |
| 139 } |
| 140 |
| 141 for (size_t i = 0; i < arraysize(kLocalAccessWhiteList); ++i) { |
| 142 const FilePath white_listed_path(kLocalAccessWhiteList[i]); |
| 143 // FilePath::operator== should probably handle trailing seperators. |
| 144 if (white_listed_path == file_path.StripTrailingSeparators() || |
| 145 white_listed_path.IsParent(file_path)) { |
| 146 return false; |
| 147 } |
| 148 } |
| 149 return true; |
| 123 } | 150 } |
| 124 | |
| 125 URLRequestFileJob::~URLRequestFileJob() { | |
| 126 #if defined(OS_WIN) | |
| 127 DCHECK(!async_resolver_); | |
| 128 #endif | 151 #endif |
| 129 } | |
| 130 | 152 |
| 131 void URLRequestFileJob::Start() { | 153 void URLRequestFileJob::Start() { |
| 132 #if defined(OS_WIN) | 154 #if defined(OS_WIN) |
| 133 // Resolve UNC paths on a background thread. | 155 // Resolve UNC paths on a background thread. |
| 134 if (!file_path_.value().compare(0, 2, L"\\\\")) { | 156 if (!file_path_.value().compare(0, 2, L"\\\\")) { |
| 135 DCHECK(!async_resolver_); | 157 DCHECK(!async_resolver_); |
| 136 async_resolver_ = new AsyncResolver(this); | 158 async_resolver_ = new AsyncResolver(this); |
| 137 base::WorkerPool::PostTask(FROM_HERE, NewRunnableMethod( | 159 base::WorkerPool::PostTask(FROM_HERE, NewRunnableMethod( |
| 138 async_resolver_.get(), &AsyncResolver::Resolve, file_path_), true); | 160 async_resolver_.get(), &AsyncResolver::Resolve, file_path_), true); |
| 139 return; | 161 return; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 197 | 219 |
| 198 // Otherwise, a read error occured. We may just need to wait... | 220 // Otherwise, a read error occured. We may just need to wait... |
| 199 if (rv == ERR_IO_PENDING) { | 221 if (rv == ERR_IO_PENDING) { |
| 200 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); | 222 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
| 201 } else { | 223 } else { |
| 202 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | 224 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); |
| 203 } | 225 } |
| 204 return false; | 226 return false; |
| 205 } | 227 } |
| 206 | 228 |
| 229 bool URLRequestFileJob::IsRedirectResponse(GURL* location, |
| 230 int* http_status_code) { |
| 231 if (is_directory_) { |
| 232 // This happens when we discovered the file is a directory, so needs a |
| 233 // slash at the end of the path. |
| 234 std::string new_path = request_->url().path(); |
| 235 new_path.push_back('/'); |
| 236 GURL::Replacements replacements; |
| 237 replacements.SetPathStr(new_path); |
| 238 |
| 239 *location = request_->url().ReplaceComponents(replacements); |
| 240 *http_status_code = 301; // simulate a permanent redirect |
| 241 return true; |
| 242 } |
| 243 |
| 244 #if defined(OS_WIN) |
| 245 // Follow a Windows shortcut. |
| 246 // We just resolve .lnk file, ignore others. |
| 247 if (!LowerCaseEqualsASCII(file_path_.Extension(), ".lnk")) |
| 248 return false; |
| 249 |
| 250 FilePath new_path = file_path_; |
| 251 bool resolved; |
| 252 resolved = file_util::ResolveShortcut(&new_path); |
| 253 |
| 254 // If shortcut is not resolved succesfully, do not redirect. |
| 255 if (!resolved) |
| 256 return false; |
| 257 |
| 258 *location = FilePathToFileURL(new_path); |
| 259 *http_status_code = 301; |
| 260 return true; |
| 261 #else |
| 262 return false; |
| 263 #endif |
| 264 } |
| 265 |
| 207 bool URLRequestFileJob::GetContentEncodings( | 266 bool URLRequestFileJob::GetContentEncodings( |
| 208 std::vector<Filter::FilterType>* encoding_types) { | 267 std::vector<Filter::FilterType>* encoding_types) { |
| 209 DCHECK(encoding_types->empty()); | 268 DCHECK(encoding_types->empty()); |
| 210 | 269 |
| 211 // Bug 9936 - .svgz files needs to be decompressed. | 270 // Bug 9936 - .svgz files needs to be decompressed. |
| 212 if (LowerCaseEqualsASCII(file_path_.Extension(), ".svgz")) | 271 if (LowerCaseEqualsASCII(file_path_.Extension(), ".svgz")) |
| 213 encoding_types->push_back(Filter::FILTER_TYPE_GZIP); | 272 encoding_types->push_back(Filter::FILTER_TYPE_GZIP); |
| 214 | 273 |
| 215 return !encoding_types->empty(); | 274 return !encoding_types->empty(); |
| 216 } | 275 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 238 // because we need to do multipart encoding here. | 297 // because we need to do multipart encoding here. |
| 239 // TODO(hclam): decide whether we want to support multiple range | 298 // TODO(hclam): decide whether we want to support multiple range |
| 240 // requests. | 299 // requests. |
| 241 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, | 300 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 242 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); | 301 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 243 } | 302 } |
| 244 } | 303 } |
| 245 } | 304 } |
| 246 } | 305 } |
| 247 | 306 |
| 307 URLRequestFileJob::~URLRequestFileJob() { |
| 308 #if defined(OS_WIN) |
| 309 DCHECK(!async_resolver_); |
| 310 #endif |
| 311 } |
| 312 |
| 248 void URLRequestFileJob::DidResolve( | 313 void URLRequestFileJob::DidResolve( |
| 249 bool exists, const base::PlatformFileInfo& file_info) { | 314 bool exists, const base::PlatformFileInfo& file_info) { |
| 250 #if defined(OS_WIN) | 315 #if defined(OS_WIN) |
| 251 async_resolver_ = NULL; | 316 async_resolver_ = NULL; |
| 252 #endif | 317 #endif |
| 253 | 318 |
| 254 // We may have been orphaned... | 319 // We may have been orphaned... |
| 255 if (!request_) | 320 if (!request_) |
| 256 return; | 321 return; |
| 257 | 322 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 316 } else { | 381 } else { |
| 317 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); | 382 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); |
| 318 } | 383 } |
| 319 | 384 |
| 320 remaining_bytes_ -= result; | 385 remaining_bytes_ -= result; |
| 321 DCHECK_GE(remaining_bytes_, 0); | 386 DCHECK_GE(remaining_bytes_, 0); |
| 322 | 387 |
| 323 NotifyReadComplete(result); | 388 NotifyReadComplete(result); |
| 324 } | 389 } |
| 325 | 390 |
| 326 bool URLRequestFileJob::IsRedirectResponse(GURL* location, | |
| 327 int* http_status_code) { | |
| 328 if (is_directory_) { | |
| 329 // This happens when we discovered the file is a directory, so needs a | |
| 330 // slash at the end of the path. | |
| 331 std::string new_path = request_->url().path(); | |
| 332 new_path.push_back('/'); | |
| 333 GURL::Replacements replacements; | |
| 334 replacements.SetPathStr(new_path); | |
| 335 | |
| 336 *location = request_->url().ReplaceComponents(replacements); | |
| 337 *http_status_code = 301; // simulate a permanent redirect | |
| 338 return true; | |
| 339 } | |
| 340 | |
| 341 #if defined(OS_WIN) | |
| 342 // Follow a Windows shortcut. | |
| 343 // We just resolve .lnk file, ignore others. | |
| 344 if (!LowerCaseEqualsASCII(file_path_.Extension(), ".lnk")) | |
| 345 return false; | |
| 346 | |
| 347 FilePath new_path = file_path_; | |
| 348 bool resolved; | |
| 349 resolved = file_util::ResolveShortcut(&new_path); | |
| 350 | |
| 351 // If shortcut is not resolved succesfully, do not redirect. | |
| 352 if (!resolved) | |
| 353 return false; | |
| 354 | |
| 355 *location = FilePathToFileURL(new_path); | |
| 356 *http_status_code = 301; | |
| 357 return true; | |
| 358 #else | |
| 359 return false; | |
| 360 #endif | |
| 361 } | |
| 362 | |
| 363 #if defined(OS_CHROMEOS) | |
| 364 static const char* const kLocalAccessWhiteList[] = { | |
| 365 "/home/chronos/user/Downloads", | |
| 366 "/media", | |
| 367 "/mnt/partner_partition", | |
| 368 "/usr/share/chromeos-assets", | |
| 369 "/tmp", | |
| 370 "/var/log", | |
| 371 }; | |
| 372 | |
| 373 // static | |
| 374 bool URLRequestFileJob::AccessDisabled(const FilePath& file_path) { | |
| 375 if (URLRequest::IsFileAccessAllowed()) { // for tests. | |
| 376 return false; | |
| 377 } | |
| 378 | |
| 379 for (size_t i = 0; i < arraysize(kLocalAccessWhiteList); ++i) { | |
| 380 const FilePath white_listed_path(kLocalAccessWhiteList[i]); | |
| 381 // FilePath::operator== should probably handle trailing seperators. | |
| 382 if (white_listed_path == file_path.StripTrailingSeparators() || | |
| 383 white_listed_path.IsParent(file_path)) { | |
| 384 return false; | |
| 385 } | |
| 386 } | |
| 387 return true; | |
| 388 } | |
| 389 #endif | |
| 390 | |
| 391 } // namespace net | 391 } // namespace net |
| OLD | NEW |