| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2009 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 10 matching lines...) Expand all Loading... |
| 22 #include "base/compiler_specific.h" | 22 #include "base/compiler_specific.h" |
| 23 #include "base/message_loop.h" | 23 #include "base/message_loop.h" |
| 24 #include "base/platform_file.h" | 24 #include "base/platform_file.h" |
| 25 #include "base/string_util.h" | 25 #include "base/string_util.h" |
| 26 #include "base/worker_pool.h" | 26 #include "base/worker_pool.h" |
| 27 #include "googleurl/src/gurl.h" | 27 #include "googleurl/src/gurl.h" |
| 28 #include "net/base/load_flags.h" | 28 #include "net/base/load_flags.h" |
| 29 #include "net/base/mime_util.h" | 29 #include "net/base/mime_util.h" |
| 30 #include "net/base/net_errors.h" | 30 #include "net/base/net_errors.h" |
| 31 #include "net/base/net_util.h" | 31 #include "net/base/net_util.h" |
| 32 #include "net/http/http_util.h" |
| 32 #include "net/url_request/url_request.h" | 33 #include "net/url_request/url_request.h" |
| 33 #include "net/url_request/url_request_file_dir_job.h" | 34 #include "net/url_request/url_request_file_dir_job.h" |
| 34 | 35 |
| 35 #if defined(OS_WIN) | 36 #if defined(OS_WIN) |
| 36 class URLRequestFileJob::AsyncResolver : | 37 class URLRequestFileJob::AsyncResolver : |
| 37 public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> { | 38 public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> { |
| 38 public: | 39 public: |
| 39 explicit AsyncResolver(URLRequestFileJob* owner) | 40 explicit AsyncResolver(URLRequestFileJob* owner) |
| 40 : owner_(owner), owner_loop_(MessageLoop::current()) { | 41 : owner_(owner), owner_loop_(MessageLoop::current()) { |
| 41 } | 42 } |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 83 // file names). | 84 // file names). |
| 84 return new URLRequestFileJob(request, file_path); | 85 return new URLRequestFileJob(request, file_path); |
| 85 } | 86 } |
| 86 | 87 |
| 87 URLRequestFileJob::URLRequestFileJob(URLRequest* request, | 88 URLRequestFileJob::URLRequestFileJob(URLRequest* request, |
| 88 const FilePath& file_path) | 89 const FilePath& file_path) |
| 89 : URLRequestJob(request), | 90 : URLRequestJob(request), |
| 90 file_path_(file_path), | 91 file_path_(file_path), |
| 91 ALLOW_THIS_IN_INITIALIZER_LIST( | 92 ALLOW_THIS_IN_INITIALIZER_LIST( |
| 92 io_callback_(this, &URLRequestFileJob::DidRead)), | 93 io_callback_(this, &URLRequestFileJob::DidRead)), |
| 93 is_directory_(false) { | 94 is_directory_(false), |
| 95 remaining_bytes_(0) { |
| 94 } | 96 } |
| 95 | 97 |
| 96 URLRequestFileJob::~URLRequestFileJob() { | 98 URLRequestFileJob::~URLRequestFileJob() { |
| 97 #if defined(OS_WIN) | 99 #if defined(OS_WIN) |
| 98 DCHECK(!async_resolver_); | 100 DCHECK(!async_resolver_); |
| 99 #endif | 101 #endif |
| 100 } | 102 } |
| 101 | 103 |
| 102 void URLRequestFileJob::Start() { | 104 void URLRequestFileJob::Start() { |
| 103 #if defined(OS_WIN) | 105 #if defined(OS_WIN) |
| (...skipping 24 matching lines...) Expand all Loading... |
| 128 } | 130 } |
| 129 #endif | 131 #endif |
| 130 | 132 |
| 131 URLRequestJob::Kill(); | 133 URLRequestJob::Kill(); |
| 132 } | 134 } |
| 133 | 135 |
| 134 bool URLRequestFileJob::ReadRawData(net::IOBuffer* dest, int dest_size, | 136 bool URLRequestFileJob::ReadRawData(net::IOBuffer* dest, int dest_size, |
| 135 int *bytes_read) { | 137 int *bytes_read) { |
| 136 DCHECK_NE(dest_size, 0); | 138 DCHECK_NE(dest_size, 0); |
| 137 DCHECK(bytes_read); | 139 DCHECK(bytes_read); |
| 140 DCHECK_GE(remaining_bytes_, 0); |
| 141 |
| 142 if (remaining_bytes_ < dest_size) |
| 143 dest_size = static_cast<int>(remaining_bytes_); |
| 138 | 144 |
| 139 int rv = stream_.Read(dest->data(), dest_size, &io_callback_); | 145 int rv = stream_.Read(dest->data(), dest_size, &io_callback_); |
| 140 if (rv >= 0) { | 146 if (rv >= 0) { |
| 141 // Data is immediately available. | 147 // Data is immediately available. |
| 142 *bytes_read = rv; | 148 *bytes_read = rv; |
| 149 remaining_bytes_ -= rv; |
| 150 DCHECK_GE(remaining_bytes_, 0); |
| 143 return true; | 151 return true; |
| 144 } | 152 } |
| 145 | 153 |
| 146 // Otherwise, a read error occured. We may just need to wait... | 154 // Otherwise, a read error occured. We may just need to wait... |
| 147 if (rv == net::ERR_IO_PENDING) { | 155 if (rv == net::ERR_IO_PENDING) { |
| 148 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); | 156 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
| 149 } else { | 157 } else { |
| 150 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | 158 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); |
| 151 } | 159 } |
| 152 return false; | 160 return false; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 168 stream_.IsOpen()) { | 176 stream_.IsOpen()) { |
| 169 info->response_data_file = | 177 info->response_data_file = |
| 170 base::CreatePlatformFile(file_path_.ToWStringHack(), | 178 base::CreatePlatformFile(file_path_.ToWStringHack(), |
| 171 base::PLATFORM_FILE_OPEN | | 179 base::PLATFORM_FILE_OPEN | |
| 172 base::PLATFORM_FILE_READ | | 180 base::PLATFORM_FILE_READ | |
| 173 base::PLATFORM_FILE_ASYNC, | 181 base::PLATFORM_FILE_ASYNC, |
| 174 &created); | 182 &created); |
| 175 } | 183 } |
| 176 } | 184 } |
| 177 | 185 |
| 186 void URLRequestFileJob::SetExtraRequestHeaders(const std::string& headers) { |
| 187 // We only care about "Range" header here. |
| 188 std::vector<net::HttpByteRange> ranges; |
| 189 if (net::HttpUtil::ParseRanges(headers, &ranges)) { |
| 190 if (ranges.size() == 1) { |
| 191 byte_range_ = ranges[0]; |
| 192 } else { |
| 193 // We don't support multiple range requests in one single URL request, |
| 194 // because we need to do multipart encoding here. |
| 195 // TODO(hclam): decide whether we want to support multiple range requests. |
| 196 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 197 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 198 } |
| 199 } |
| 200 } |
| 201 |
| 178 void URLRequestFileJob::DidResolve( | 202 void URLRequestFileJob::DidResolve( |
| 179 bool exists, const file_util::FileInfo& file_info) { | 203 bool exists, const file_util::FileInfo& file_info) { |
| 180 #if defined(OS_WIN) | 204 #if defined(OS_WIN) |
| 181 async_resolver_ = NULL; | 205 async_resolver_ = NULL; |
| 182 #endif | 206 #endif |
| 183 | 207 |
| 184 // We may have been orphaned... | 208 // We may have been orphaned... |
| 185 if (!request_) | 209 if (!request_) |
| 186 return; | 210 return; |
| 187 | 211 |
| 188 int rv = net::OK; | 212 int rv = net::OK; |
| 189 // We use URLRequestFileJob to handle valid and invalid files as well as | 213 // We use URLRequestFileJob to handle valid and invalid files as well as |
| 190 // invalid directories. For a directory to be invalid, it must either not | 214 // invalid directories. For a directory to be invalid, it must either not |
| 191 // exist, or be "\" on Windows. (Windows resolves "\" to "C:\", thus | 215 // exist, or be "\" on Windows. (Windows resolves "\" to "C:\", thus |
| 192 // reporting it as existent.) On POSIX, we don't count any existent | 216 // reporting it as existent.) On POSIX, we don't count any existent |
| 193 // directory as invalid. | 217 // directory as invalid. |
| 194 if (!exists || file_info.is_directory) { | 218 if (!exists || file_info.is_directory) { |
| 195 rv = net::ERR_FILE_NOT_FOUND; | 219 rv = net::ERR_FILE_NOT_FOUND; |
| 196 } else { | 220 } else { |
| 197 int flags = base::PLATFORM_FILE_OPEN | | 221 int flags = base::PLATFORM_FILE_OPEN | |
| 198 base::PLATFORM_FILE_READ | | 222 base::PLATFORM_FILE_READ | |
| 199 base::PLATFORM_FILE_ASYNC; | 223 base::PLATFORM_FILE_ASYNC; |
| 200 rv = stream_.Open(file_path_, flags); | 224 rv = stream_.Open(file_path_, flags); |
| 201 } | 225 } |
| 202 | 226 |
| 203 if (rv == net::OK) { | 227 if (rv != net::OK) { |
| 204 set_expected_content_size(file_info.size); | |
| 205 NotifyHeadersComplete(); | |
| 206 } else { | |
| 207 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | 228 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); |
| 229 return; |
| 208 } | 230 } |
| 231 |
| 232 if (!byte_range_.ComputeBounds(file_info.size)) { |
| 233 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 234 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 235 return; |
| 236 } |
| 237 |
| 238 remaining_bytes_ = byte_range_.last_byte_position() - |
| 239 byte_range_.first_byte_position() + 1; |
| 240 DCHECK_GE(remaining_bytes_, 0); |
| 241 |
| 242 // Do the seek at the beginning of the request. |
| 243 if (remaining_bytes_ > 0 && |
| 244 byte_range_.first_byte_position() != 0 && |
| 245 byte_range_.first_byte_position() != |
| 246 stream_.Seek(net::FROM_BEGIN, byte_range_.first_byte_position())) { |
| 247 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 248 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 249 return; |
| 250 } |
| 251 |
| 252 set_expected_content_size(remaining_bytes_); |
| 253 NotifyHeadersComplete(); |
| 209 } | 254 } |
| 210 | 255 |
| 211 void URLRequestFileJob::DidRead(int result) { | 256 void URLRequestFileJob::DidRead(int result) { |
| 212 if (result > 0) { | 257 if (result > 0) { |
| 213 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status | 258 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status |
| 214 } else if (result == 0) { | 259 } else if (result == 0) { |
| 215 NotifyDone(URLRequestStatus()); | 260 NotifyDone(URLRequestStatus()); |
| 216 } else { | 261 } else { |
| 217 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); | 262 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); |
| 218 } | 263 } |
| 264 |
| 265 remaining_bytes_ -= result; |
| 266 DCHECK_GE(remaining_bytes_, 0); |
| 267 |
| 219 NotifyReadComplete(result); | 268 NotifyReadComplete(result); |
| 220 } | 269 } |
| 221 | 270 |
| 222 bool URLRequestFileJob::IsRedirectResponse( | 271 bool URLRequestFileJob::IsRedirectResponse( |
| 223 GURL* location, int* http_status_code) { | 272 GURL* location, int* http_status_code) { |
| 224 #if defined(OS_WIN) | 273 #if defined(OS_WIN) |
| 225 std::wstring extension = | 274 std::wstring extension = |
| 226 file_util::GetFileExtensionFromPath(file_path_.value()); | 275 file_util::GetFileExtensionFromPath(file_path_.value()); |
| 227 | 276 |
| 228 // Follow a Windows shortcut. | 277 // Follow a Windows shortcut. |
| 229 // We just resolve .lnk file, ignore others. | 278 // We just resolve .lnk file, ignore others. |
| 230 if (!LowerCaseEqualsASCII(extension, "lnk")) | 279 if (!LowerCaseEqualsASCII(extension, "lnk")) |
| 231 return false; | 280 return false; |
| 232 | 281 |
| 233 std::wstring new_path = file_path_.value(); | 282 std::wstring new_path = file_path_.value(); |
| 234 bool resolved; | 283 bool resolved; |
| 235 resolved = file_util::ResolveShortcut(&new_path); | 284 resolved = file_util::ResolveShortcut(&new_path); |
| 236 | 285 |
| 237 // If shortcut is not resolved succesfully, do not redirect. | 286 // If shortcut is not resolved succesfully, do not redirect. |
| 238 if (!resolved) | 287 if (!resolved) |
| 239 return false; | 288 return false; |
| 240 | 289 |
| 241 *location = net::FilePathToFileURL(FilePath(new_path)); | 290 *location = net::FilePathToFileURL(FilePath(new_path)); |
| 242 *http_status_code = 301; | 291 *http_status_code = 301; |
| 243 return true; | 292 return true; |
| 244 #else | 293 #else |
| 245 return false; | 294 return false; |
| 246 #endif | 295 #endif |
| 247 } | 296 } |
| OLD | NEW |