| 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_); |
| 144 |
| 145 // If we should copy zero bytes because |remaining_bytes_| is zero, short |
| 146 // circuit here. |
| 147 if (!dest_size) { |
| 148 *bytes_read = 0; |
| 149 return true; |
| 150 } |
| 138 | 151 |
| 139 int rv = stream_.Read(dest->data(), dest_size, &io_callback_); | 152 int rv = stream_.Read(dest->data(), dest_size, &io_callback_); |
| 140 if (rv >= 0) { | 153 if (rv >= 0) { |
| 141 // Data is immediately available. | 154 // Data is immediately available. |
| 142 *bytes_read = rv; | 155 *bytes_read = rv; |
| 156 remaining_bytes_ -= rv; |
| 157 DCHECK_GE(remaining_bytes_, 0); |
| 143 return true; | 158 return true; |
| 144 } | 159 } |
| 145 | 160 |
| 146 // Otherwise, a read error occured. We may just need to wait... | 161 // Otherwise, a read error occured. We may just need to wait... |
| 147 if (rv == net::ERR_IO_PENDING) { | 162 if (rv == net::ERR_IO_PENDING) { |
| 148 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); | 163 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
| 149 } else { | 164 } else { |
| 150 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | 165 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); |
| 151 } | 166 } |
| 152 return false; | 167 return false; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 168 stream_.IsOpen()) { | 183 stream_.IsOpen()) { |
| 169 info->response_data_file = | 184 info->response_data_file = |
| 170 base::CreatePlatformFile(file_path_.ToWStringHack(), | 185 base::CreatePlatformFile(file_path_.ToWStringHack(), |
| 171 base::PLATFORM_FILE_OPEN | | 186 base::PLATFORM_FILE_OPEN | |
| 172 base::PLATFORM_FILE_READ | | 187 base::PLATFORM_FILE_READ | |
| 173 base::PLATFORM_FILE_ASYNC, | 188 base::PLATFORM_FILE_ASYNC, |
| 174 &created); | 189 &created); |
| 175 } | 190 } |
| 176 } | 191 } |
| 177 | 192 |
| 193 void URLRequestFileJob::SetExtraRequestHeaders(const std::string& headers) { |
| 194 // We only care about "Range" header here. |
| 195 std::vector<net::HttpByteRange> ranges; |
| 196 if (net::HttpUtil::ParseRanges(headers, &ranges)) { |
| 197 if (ranges.size() == 1) { |
| 198 byte_range_ = ranges[0]; |
| 199 } else { |
| 200 // We don't support multiple range requests in one single URL request, |
| 201 // because we need to do multipart encoding here. |
| 202 // TODO(hclam): decide whether we want to support multiple range requests. |
| 203 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 204 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 205 } |
| 206 } |
| 207 } |
| 208 |
| 178 void URLRequestFileJob::DidResolve( | 209 void URLRequestFileJob::DidResolve( |
| 179 bool exists, const file_util::FileInfo& file_info) { | 210 bool exists, const file_util::FileInfo& file_info) { |
| 180 #if defined(OS_WIN) | 211 #if defined(OS_WIN) |
| 181 async_resolver_ = NULL; | 212 async_resolver_ = NULL; |
| 182 #endif | 213 #endif |
| 183 | 214 |
| 184 // We may have been orphaned... | 215 // We may have been orphaned... |
| 185 if (!request_) | 216 if (!request_) |
| 186 return; | 217 return; |
| 187 | 218 |
| 188 int rv = net::OK; | 219 int rv = net::OK; |
| 189 // We use URLRequestFileJob to handle valid and invalid files as well as | 220 // 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 | 221 // invalid directories. For a directory to be invalid, it must either not |
| 191 // exist, or be "\" on Windows. (Windows resolves "\" to "C:\", thus | 222 // exist, or be "\" on Windows. (Windows resolves "\" to "C:\", thus |
| 192 // reporting it as existent.) On POSIX, we don't count any existent | 223 // reporting it as existent.) On POSIX, we don't count any existent |
| 193 // directory as invalid. | 224 // directory as invalid. |
| 194 if (!exists || file_info.is_directory) { | 225 if (!exists || file_info.is_directory) { |
| 195 rv = net::ERR_FILE_NOT_FOUND; | 226 rv = net::ERR_FILE_NOT_FOUND; |
| 196 } else { | 227 } else { |
| 197 int flags = base::PLATFORM_FILE_OPEN | | 228 int flags = base::PLATFORM_FILE_OPEN | |
| 198 base::PLATFORM_FILE_READ | | 229 base::PLATFORM_FILE_READ | |
| 199 base::PLATFORM_FILE_ASYNC; | 230 base::PLATFORM_FILE_ASYNC; |
| 200 rv = stream_.Open(file_path_, flags); | 231 rv = stream_.Open(file_path_, flags); |
| 201 } | 232 } |
| 202 | 233 |
| 203 if (rv == net::OK) { | 234 if (rv != net::OK) { |
| 204 set_expected_content_size(file_info.size); | |
| 205 NotifyHeadersComplete(); | |
| 206 } else { | |
| 207 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | 235 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); |
| 236 return; |
| 208 } | 237 } |
| 238 |
| 239 if (!byte_range_.ComputeBounds(file_info.size)) { |
| 240 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 241 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 242 return; |
| 243 } |
| 244 |
| 245 remaining_bytes_ = byte_range_.last_byte_position() - |
| 246 byte_range_.first_byte_position() + 1; |
| 247 DCHECK_GE(remaining_bytes_, 0); |
| 248 |
| 249 // Do the seek at the beginning of the request. |
| 250 if (remaining_bytes_ > 0 && |
| 251 byte_range_.first_byte_position() != 0 && |
| 252 byte_range_.first_byte_position() != |
| 253 stream_.Seek(net::FROM_BEGIN, byte_range_.first_byte_position())) { |
| 254 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 255 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 256 return; |
| 257 } |
| 258 |
| 259 set_expected_content_size(remaining_bytes_); |
| 260 NotifyHeadersComplete(); |
| 209 } | 261 } |
| 210 | 262 |
| 211 void URLRequestFileJob::DidRead(int result) { | 263 void URLRequestFileJob::DidRead(int result) { |
| 212 if (result > 0) { | 264 if (result > 0) { |
| 213 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status | 265 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status |
| 214 } else if (result == 0) { | 266 } else if (result == 0) { |
| 215 NotifyDone(URLRequestStatus()); | 267 NotifyDone(URLRequestStatus()); |
| 216 } else { | 268 } else { |
| 217 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); | 269 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); |
| 218 } | 270 } |
| 271 |
| 272 remaining_bytes_ -= result; |
| 273 DCHECK_GE(remaining_bytes_, 0); |
| 274 |
| 219 NotifyReadComplete(result); | 275 NotifyReadComplete(result); |
| 220 } | 276 } |
| 221 | 277 |
| 222 bool URLRequestFileJob::IsRedirectResponse( | 278 bool URLRequestFileJob::IsRedirectResponse( |
| 223 GURL* location, int* http_status_code) { | 279 GURL* location, int* http_status_code) { |
| 224 #if defined(OS_WIN) | 280 #if defined(OS_WIN) |
| 225 std::wstring extension = | 281 std::wstring extension = |
| 226 file_util::GetFileExtensionFromPath(file_path_.value()); | 282 file_util::GetFileExtensionFromPath(file_path_.value()); |
| 227 | 283 |
| 228 // Follow a Windows shortcut. | 284 // Follow a Windows shortcut. |
| 229 // We just resolve .lnk file, ignore others. | 285 // We just resolve .lnk file, ignore others. |
| 230 if (!LowerCaseEqualsASCII(extension, "lnk")) | 286 if (!LowerCaseEqualsASCII(extension, "lnk")) |
| 231 return false; | 287 return false; |
| 232 | 288 |
| 233 std::wstring new_path = file_path_.value(); | 289 std::wstring new_path = file_path_.value(); |
| 234 bool resolved; | 290 bool resolved; |
| 235 resolved = file_util::ResolveShortcut(&new_path); | 291 resolved = file_util::ResolveShortcut(&new_path); |
| 236 | 292 |
| 237 // If shortcut is not resolved succesfully, do not redirect. | 293 // If shortcut is not resolved succesfully, do not redirect. |
| 238 if (!resolved) | 294 if (!resolved) |
| 239 return false; | 295 return false; |
| 240 | 296 |
| 241 *location = net::FilePathToFileURL(FilePath(new_path)); | 297 *location = net::FilePathToFileURL(FilePath(new_path)); |
| 242 *http_status_code = 301; | 298 *http_status_code = 301; |
| 243 return true; | 299 return true; |
| 244 #else | 300 #else |
| 245 return false; | 301 return false; |
| 246 #endif | 302 #endif |
| 247 } | 303 } |
| OLD | NEW |