| OLD | NEW |
| 1 // Copyright (c) 2006-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 // |
| 11 // Since overlapped reads require a 'static' buffer for the duration of the | 11 // Since overlapped reads require a 'static' buffer for the duration of the |
| (...skipping 21 matching lines...) Expand all Loading... |
| 33 #include "net/base/net_util.h" | 33 #include "net/base/net_util.h" |
| 34 #include "net/http/http_util.h" | 34 #include "net/http/http_util.h" |
| 35 #include "net/url_request/url_request.h" | 35 #include "net/url_request/url_request.h" |
| 36 #include "net/url_request/url_request_error_job.h" | 36 #include "net/url_request/url_request_error_job.h" |
| 37 #include "net/url_request/url_request_file_dir_job.h" | 37 #include "net/url_request/url_request_file_dir_job.h" |
| 38 | 38 |
| 39 #if defined(OS_WIN) | 39 #if defined(OS_WIN) |
| 40 #include "base/worker_pool.h" | 40 #include "base/worker_pool.h" |
| 41 #endif | 41 #endif |
| 42 | 42 |
| 43 namespace net { |
| 44 |
| 43 #if defined(OS_WIN) | 45 #if defined(OS_WIN) |
| 44 class URLRequestFileJob::AsyncResolver | 46 class URLRequestFileJob::AsyncResolver |
| 45 : public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> { | 47 : public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> { |
| 46 public: | 48 public: |
| 47 explicit AsyncResolver(URLRequestFileJob* owner) | 49 explicit AsyncResolver(URLRequestFileJob* owner) |
| 48 : owner_(owner), owner_loop_(MessageLoop::current()) { | 50 : owner_(owner), owner_loop_(MessageLoop::current()) { |
| 49 } | 51 } |
| 50 | 52 |
| 51 void Resolve(const FilePath& file_path) { | 53 void Resolve(const FilePath& file_path) { |
| 52 base::PlatformFileInfo file_info; | 54 base::PlatformFileInfo file_info; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 76 } | 78 } |
| 77 | 79 |
| 78 URLRequestFileJob* owner_; | 80 URLRequestFileJob* owner_; |
| 79 | 81 |
| 80 Lock lock_; | 82 Lock lock_; |
| 81 MessageLoop* owner_loop_; | 83 MessageLoop* owner_loop_; |
| 82 }; | 84 }; |
| 83 #endif | 85 #endif |
| 84 | 86 |
| 85 // static | 87 // static |
| 86 net::URLRequestJob* URLRequestFileJob::Factory(net::URLRequest* request, | 88 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, |
| 87 const std::string& scheme) { | 89 const std::string& scheme) { |
| 88 | 90 |
| 89 FilePath file_path; | 91 FilePath file_path; |
| 90 const bool is_file = net::FileURLToFilePath(request->url(), &file_path); | 92 const bool is_file = FileURLToFilePath(request->url(), &file_path); |
| 91 | 93 |
| 92 #if defined(OS_CHROMEOS) | 94 #if defined(OS_CHROMEOS) |
| 93 // Check file access. | 95 // Check file access. |
| 94 if (AccessDisabled(file_path)) | 96 if (AccessDisabled(file_path)) |
| 95 return new URLRequestErrorJob(request, net::ERR_ACCESS_DENIED); | 97 return new URLRequestErrorJob(request, ERR_ACCESS_DENIED); |
| 96 #endif | 98 #endif |
| 97 | 99 |
| 98 // We need to decide whether to create URLRequestFileJob for file access or | 100 // We need to decide whether to create URLRequestFileJob for file access or |
| 99 // URLRequestFileDirJob for directory access. To avoid accessing the | 101 // URLRequestFileDirJob for directory access. To avoid accessing the |
| 100 // filesystem, we only look at the path string here. | 102 // filesystem, we only look at the path string here. |
| 101 // The code in the URLRequestFileJob::Start() method discovers that a path, | 103 // 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, | 104 // which doesn't end with a slash, should really be treated as a directory, |
| 103 // and it then redirects to the URLRequestFileDirJob. | 105 // and it then redirects to the URLRequestFileDirJob. |
| 104 if (is_file && | 106 if (is_file && |
| 105 file_util::EndsWithSeparator(file_path) && | 107 file_util::EndsWithSeparator(file_path) && |
| 106 file_path.IsAbsolute()) | 108 file_path.IsAbsolute()) |
| 107 return new URLRequestFileDirJob(request, file_path); | 109 return new URLRequestFileDirJob(request, file_path); |
| 108 | 110 |
| 109 // Use a regular file request job for all non-directories (including invalid | 111 // Use a regular file request job for all non-directories (including invalid |
| 110 // file names). | 112 // file names). |
| 111 return new URLRequestFileJob(request, file_path); | 113 return new URLRequestFileJob(request, file_path); |
| 112 } | 114 } |
| 113 | 115 |
| 114 URLRequestFileJob::URLRequestFileJob(net::URLRequest* request, | 116 URLRequestFileJob::URLRequestFileJob(URLRequest* request, |
| 115 const FilePath& file_path) | 117 const FilePath& file_path) |
| 116 : net::URLRequestJob(request), | 118 : URLRequestJob(request), |
| 117 file_path_(file_path), | 119 file_path_(file_path), |
| 118 ALLOW_THIS_IN_INITIALIZER_LIST( | 120 ALLOW_THIS_IN_INITIALIZER_LIST( |
| 119 io_callback_(this, &URLRequestFileJob::DidRead)), | 121 io_callback_(this, &URLRequestFileJob::DidRead)), |
| 120 is_directory_(false), | 122 is_directory_(false), |
| 121 remaining_bytes_(0), | 123 remaining_bytes_(0), |
| 122 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { | 124 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { |
| 123 } | 125 } |
| 124 | 126 |
| 125 URLRequestFileJob::~URLRequestFileJob() { | 127 URLRequestFileJob::~URLRequestFileJob() { |
| 126 #if defined(OS_WIN) | 128 #if defined(OS_WIN) |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 159 void URLRequestFileJob::Kill() { | 161 void URLRequestFileJob::Kill() { |
| 160 stream_.Close(); | 162 stream_.Close(); |
| 161 | 163 |
| 162 #if defined(OS_WIN) | 164 #if defined(OS_WIN) |
| 163 if (async_resolver_) { | 165 if (async_resolver_) { |
| 164 async_resolver_->Cancel(); | 166 async_resolver_->Cancel(); |
| 165 async_resolver_ = NULL; | 167 async_resolver_ = NULL; |
| 166 } | 168 } |
| 167 #endif | 169 #endif |
| 168 | 170 |
| 169 net::URLRequestJob::Kill(); | 171 URLRequestJob::Kill(); |
| 170 method_factory_.RevokeAll(); | 172 method_factory_.RevokeAll(); |
| 171 } | 173 } |
| 172 | 174 |
| 173 bool URLRequestFileJob::ReadRawData(net::IOBuffer* dest, int dest_size, | 175 bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size, |
| 174 int *bytes_read) { | 176 int *bytes_read) { |
| 175 DCHECK_NE(dest_size, 0); | 177 DCHECK_NE(dest_size, 0); |
| 176 DCHECK(bytes_read); | 178 DCHECK(bytes_read); |
| 177 DCHECK_GE(remaining_bytes_, 0); | 179 DCHECK_GE(remaining_bytes_, 0); |
| 178 | 180 |
| 179 if (remaining_bytes_ < dest_size) | 181 if (remaining_bytes_ < dest_size) |
| 180 dest_size = static_cast<int>(remaining_bytes_); | 182 dest_size = static_cast<int>(remaining_bytes_); |
| 181 | 183 |
| 182 // If we should copy zero bytes because |remaining_bytes_| is zero, short | 184 // If we should copy zero bytes because |remaining_bytes_| is zero, short |
| 183 // circuit here. | 185 // circuit here. |
| 184 if (!dest_size) { | 186 if (!dest_size) { |
| 185 *bytes_read = 0; | 187 *bytes_read = 0; |
| 186 return true; | 188 return true; |
| 187 } | 189 } |
| 188 | 190 |
| 189 int rv = stream_.Read(dest->data(), dest_size, &io_callback_); | 191 int rv = stream_.Read(dest->data(), dest_size, &io_callback_); |
| 190 if (rv >= 0) { | 192 if (rv >= 0) { |
| 191 // Data is immediately available. | 193 // Data is immediately available. |
| 192 *bytes_read = rv; | 194 *bytes_read = rv; |
| 193 remaining_bytes_ -= rv; | 195 remaining_bytes_ -= rv; |
| 194 DCHECK_GE(remaining_bytes_, 0); | 196 DCHECK_GE(remaining_bytes_, 0); |
| 195 return true; | 197 return true; |
| 196 } | 198 } |
| 197 | 199 |
| 198 // Otherwise, a read error occured. We may just need to wait... | 200 // Otherwise, a read error occured. We may just need to wait... |
| 199 if (rv == net::ERR_IO_PENDING) { | 201 if (rv == ERR_IO_PENDING) { |
| 200 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); | 202 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
| 201 } else { | 203 } else { |
| 202 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | 204 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); |
| 203 } | 205 } |
| 204 return false; | 206 return false; |
| 205 } | 207 } |
| 206 | 208 |
| 207 bool URLRequestFileJob::GetContentEncodings( | 209 bool URLRequestFileJob::GetContentEncodings( |
| 208 std::vector<Filter::FilterType>* encoding_types) { | 210 std::vector<Filter::FilterType>* encoding_types) { |
| 209 DCHECK(encoding_types->empty()); | 211 DCHECK(encoding_types->empty()); |
| 210 | 212 |
| 211 // Bug 9936 - .svgz files needs to be decompressed. | 213 // Bug 9936 - .svgz files needs to be decompressed. |
| 212 if (LowerCaseEqualsASCII(file_path_.Extension(), ".svgz")) | 214 if (LowerCaseEqualsASCII(file_path_.Extension(), ".svgz")) |
| 213 encoding_types->push_back(Filter::FILTER_TYPE_GZIP); | 215 encoding_types->push_back(Filter::FILTER_TYPE_GZIP); |
| 214 | 216 |
| 215 return !encoding_types->empty(); | 217 return !encoding_types->empty(); |
| 216 } | 218 } |
| 217 | 219 |
| 218 bool URLRequestFileJob::GetMimeType(std::string* mime_type) const { | 220 bool URLRequestFileJob::GetMimeType(std::string* mime_type) const { |
| 219 // URL requests should not block on the disk! On Windows this goes to the | 221 // URL requests should not block on the disk! On Windows this goes to the |
| 220 // registry. | 222 // registry. |
| 221 // http://code.google.com/p/chromium/issues/detail?id=59849 | 223 // http://code.google.com/p/chromium/issues/detail?id=59849 |
| 222 base::ThreadRestrictions::ScopedAllowIO allow_io; | 224 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 223 DCHECK(request_); | 225 DCHECK(request_); |
| 224 return net::GetMimeTypeFromFile(file_path_, mime_type); | 226 return GetMimeTypeFromFile(file_path_, mime_type); |
| 225 } | 227 } |
| 226 | 228 |
| 227 void URLRequestFileJob::SetExtraRequestHeaders( | 229 void URLRequestFileJob::SetExtraRequestHeaders( |
| 228 const net::HttpRequestHeaders& headers) { | 230 const HttpRequestHeaders& headers) { |
| 229 std::string range_header; | 231 std::string range_header; |
| 230 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) { | 232 if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) { |
| 231 // We only care about "Range" header here. | 233 // We only care about "Range" header here. |
| 232 std::vector<net::HttpByteRange> ranges; | 234 std::vector<HttpByteRange> ranges; |
| 233 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) { | 235 if (HttpUtil::ParseRangeHeader(range_header, &ranges)) { |
| 234 if (ranges.size() == 1) { | 236 if (ranges.size() == 1) { |
| 235 byte_range_ = ranges[0]; | 237 byte_range_ = ranges[0]; |
| 236 } else { | 238 } else { |
| 237 // We don't support multiple range requests in one single URL request, | 239 // We don't support multiple range requests in one single URL request, |
| 238 // because we need to do multipart encoding here. | 240 // because we need to do multipart encoding here. |
| 239 // TODO(hclam): decide whether we want to support multiple range | 241 // TODO(hclam): decide whether we want to support multiple range |
| 240 // requests. | 242 // requests. |
| 241 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, | 243 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 242 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); | 244 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 243 } | 245 } |
| 244 } | 246 } |
| 245 } | 247 } |
| 246 } | 248 } |
| 247 | 249 |
| 248 void URLRequestFileJob::DidResolve( | 250 void URLRequestFileJob::DidResolve( |
| 249 bool exists, const base::PlatformFileInfo& file_info) { | 251 bool exists, const base::PlatformFileInfo& file_info) { |
| 250 #if defined(OS_WIN) | 252 #if defined(OS_WIN) |
| 251 async_resolver_ = NULL; | 253 async_resolver_ = NULL; |
| 252 #endif | 254 #endif |
| 253 | 255 |
| 254 // We may have been orphaned... | 256 // We may have been orphaned... |
| 255 if (!request_) | 257 if (!request_) |
| 256 return; | 258 return; |
| 257 | 259 |
| 258 is_directory_ = file_info.is_directory; | 260 is_directory_ = file_info.is_directory; |
| 259 | 261 |
| 260 int rv = net::OK; | 262 int rv = OK; |
| 261 // We use URLRequestFileJob to handle files as well as directories without | 263 // We use URLRequestFileJob to handle files as well as directories without |
| 262 // trailing slash. | 264 // trailing slash. |
| 263 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise, | 265 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise, |
| 264 // we will append trailing slash and redirect to FileDirJob. | 266 // we will append trailing slash and redirect to FileDirJob. |
| 265 // A special case is "\" on Windows. We should resolve as invalid. | 267 // A special case is "\" on Windows. We should resolve as invalid. |
| 266 // However, Windows resolves "\" to "C:\", thus reports it as existent. | 268 // However, Windows resolves "\" to "C:\", thus reports it as existent. |
| 267 // So what happens is we append it with trailing slash and redirect it to | 269 // So what happens is we append it with trailing slash and redirect it to |
| 268 // FileDirJob where it is resolved as invalid. | 270 // FileDirJob where it is resolved as invalid. |
| 269 if (!exists) { | 271 if (!exists) { |
| 270 rv = net::ERR_FILE_NOT_FOUND; | 272 rv = ERR_FILE_NOT_FOUND; |
| 271 } else if (!is_directory_) { | 273 } else if (!is_directory_) { |
| 272 // URL requests should not block on the disk! | 274 // URL requests should not block on the disk! |
| 273 // http://code.google.com/p/chromium/issues/detail?id=59849 | 275 // http://code.google.com/p/chromium/issues/detail?id=59849 |
| 274 base::ThreadRestrictions::ScopedAllowIO allow_io; | 276 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 275 | 277 |
| 276 int flags = base::PLATFORM_FILE_OPEN | | 278 int flags = base::PLATFORM_FILE_OPEN | |
| 277 base::PLATFORM_FILE_READ | | 279 base::PLATFORM_FILE_READ | |
| 278 base::PLATFORM_FILE_ASYNC; | 280 base::PLATFORM_FILE_ASYNC; |
| 279 rv = stream_.Open(file_path_, flags); | 281 rv = stream_.Open(file_path_, flags); |
| 280 } | 282 } |
| 281 | 283 |
| 282 if (rv != net::OK) { | 284 if (rv != OK) { |
| 283 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | 285 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); |
| 284 return; | 286 return; |
| 285 } | 287 } |
| 286 | 288 |
| 287 if (!byte_range_.ComputeBounds(file_info.size)) { | 289 if (!byte_range_.ComputeBounds(file_info.size)) { |
| 288 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, | 290 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 289 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); | 291 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 290 return; | 292 return; |
| 291 } | 293 } |
| 292 | 294 |
| 293 remaining_bytes_ = byte_range_.last_byte_position() - | 295 remaining_bytes_ = byte_range_.last_byte_position() - |
| 294 byte_range_.first_byte_position() + 1; | 296 byte_range_.first_byte_position() + 1; |
| 295 DCHECK_GE(remaining_bytes_, 0); | 297 DCHECK_GE(remaining_bytes_, 0); |
| 296 | 298 |
| 297 // Do the seek at the beginning of the request. | 299 // Do the seek at the beginning of the request. |
| 298 if (remaining_bytes_ > 0 && | 300 if (remaining_bytes_ > 0 && |
| 299 byte_range_.first_byte_position() != 0 && | 301 byte_range_.first_byte_position() != 0 && |
| 300 byte_range_.first_byte_position() != | 302 byte_range_.first_byte_position() != |
| 301 stream_.Seek(net::FROM_BEGIN, byte_range_.first_byte_position())) { | 303 stream_.Seek(FROM_BEGIN, byte_range_.first_byte_position())) { |
| 302 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, | 304 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 303 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); | 305 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 304 return; | 306 return; |
| 305 } | 307 } |
| 306 | 308 |
| 307 set_expected_content_size(remaining_bytes_); | 309 set_expected_content_size(remaining_bytes_); |
| 308 NotifyHeadersComplete(); | 310 NotifyHeadersComplete(); |
| 309 } | 311 } |
| 310 | 312 |
| 311 void URLRequestFileJob::DidRead(int result) { | 313 void URLRequestFileJob::DidRead(int result) { |
| 312 if (result > 0) { | 314 if (result > 0) { |
| 313 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status | 315 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 345 return false; | 347 return false; |
| 346 | 348 |
| 347 FilePath new_path = file_path_; | 349 FilePath new_path = file_path_; |
| 348 bool resolved; | 350 bool resolved; |
| 349 resolved = file_util::ResolveShortcut(&new_path); | 351 resolved = file_util::ResolveShortcut(&new_path); |
| 350 | 352 |
| 351 // If shortcut is not resolved succesfully, do not redirect. | 353 // If shortcut is not resolved succesfully, do not redirect. |
| 352 if (!resolved) | 354 if (!resolved) |
| 353 return false; | 355 return false; |
| 354 | 356 |
| 355 *location = net::FilePathToFileURL(new_path); | 357 *location = FilePathToFileURL(new_path); |
| 356 *http_status_code = 301; | 358 *http_status_code = 301; |
| 357 return true; | 359 return true; |
| 358 #else | 360 #else |
| 359 return false; | 361 return false; |
| 360 #endif | 362 #endif |
| 361 } | 363 } |
| 362 | 364 |
| 363 #if defined(OS_CHROMEOS) | 365 #if defined(OS_CHROMEOS) |
| 364 static const char* const kLocalAccessWhiteList[] = { | 366 static const char* const kLocalAccessWhiteList[] = { |
| 365 "/home/chronos/user/Downloads", | 367 "/home/chronos/user/Downloads", |
| 366 "/mnt/partner_partition", | 368 "/mnt/partner_partition", |
| 367 "/usr/share/chromeos-assets", | 369 "/usr/share/chromeos-assets", |
| 368 "/tmp", | 370 "/tmp", |
| 369 "/var/log", | 371 "/var/log", |
| 370 }; | 372 }; |
| 371 | 373 |
| 372 // static | 374 // static |
| 373 bool URLRequestFileJob::AccessDisabled(const FilePath& file_path) { | 375 bool URLRequestFileJob::AccessDisabled(const FilePath& file_path) { |
| 374 if (net::URLRequest::IsFileAccessAllowed()) { // for tests. | 376 if (URLRequest::IsFileAccessAllowed()) { // for tests. |
| 375 return false; | 377 return false; |
| 376 } | 378 } |
| 377 | 379 |
| 378 for (size_t i = 0; i < arraysize(kLocalAccessWhiteList); ++i) { | 380 for (size_t i = 0; i < arraysize(kLocalAccessWhiteList); ++i) { |
| 379 const FilePath white_listed_path(kLocalAccessWhiteList[i]); | 381 const FilePath white_listed_path(kLocalAccessWhiteList[i]); |
| 380 // FilePath::operator== should probably handle trailing seperators. | 382 // FilePath::operator== should probably handle trailing seperators. |
| 381 if (white_listed_path == file_path.StripTrailingSeparators() || | 383 if (white_listed_path == file_path.StripTrailingSeparators() || |
| 382 white_listed_path.IsParent(file_path)) { | 384 white_listed_path.IsParent(file_path)) { |
| 383 return false; | 385 return false; |
| 384 } | 386 } |
| 385 } | 387 } |
| 386 return true; | 388 return true; |
| 387 } | 389 } |
| 388 #endif | 390 #endif |
| 391 |
| 392 } // namespace net |
| OLD | NEW |