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 |