OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 27 matching lines...) Expand all Loading... |
38 #include "net/http/http_util.h" | 38 #include "net/http/http_util.h" |
39 #include "net/url_request/url_request_error_job.h" | 39 #include "net/url_request/url_request_error_job.h" |
40 #include "net/url_request/url_request_file_dir_job.h" | 40 #include "net/url_request/url_request_file_dir_job.h" |
41 | 41 |
42 #if defined(OS_WIN) | 42 #if defined(OS_WIN) |
43 #include "base/win/shortcut.h" | 43 #include "base/win/shortcut.h" |
44 #endif | 44 #endif |
45 | 45 |
46 namespace net { | 46 namespace net { |
47 | 47 |
48 class URLRequestFileJob::AsyncResolver | 48 URLRequestFileJob::FileMetaInfo::FileMetaInfo() |
49 : public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> { | 49 : file_size(0), |
50 public: | 50 mime_type_result(false), |
51 explicit AsyncResolver(URLRequestFileJob* owner) | 51 file_exists(false), |
52 : owner_(owner), owner_loop_(MessageLoop::current()) { | 52 is_directory(false) { |
53 } | 53 } |
54 | |
55 void Resolve(const FilePath& file_path) { | |
56 base::PlatformFileInfo file_info; | |
57 bool exists = file_util::GetFileInfo(file_path, &file_info); | |
58 base::AutoLock locked(lock_); | |
59 if (owner_loop_) { | |
60 owner_loop_->PostTask( | |
61 FROM_HERE, | |
62 base::Bind(&AsyncResolver::ReturnResults, this, exists, file_info)); | |
63 } | |
64 } | |
65 | |
66 void Cancel() { | |
67 owner_ = NULL; | |
68 | |
69 base::AutoLock locked(lock_); | |
70 owner_loop_ = NULL; | |
71 } | |
72 | |
73 private: | |
74 friend class base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver>; | |
75 | |
76 ~AsyncResolver() {} | |
77 | |
78 void ReturnResults(bool exists, const base::PlatformFileInfo& file_info) { | |
79 if (owner_) | |
80 owner_->DidResolve(exists, file_info); | |
81 } | |
82 | |
83 URLRequestFileJob* owner_; | |
84 | |
85 base::Lock lock_; | |
86 MessageLoop* owner_loop_; | |
87 }; | |
88 | 54 |
89 URLRequestFileJob::URLRequestFileJob(URLRequest* request, | 55 URLRequestFileJob::URLRequestFileJob(URLRequest* request, |
90 NetworkDelegate* network_delegate, | 56 NetworkDelegate* network_delegate, |
91 const FilePath& file_path) | 57 const FilePath& file_path) |
92 : URLRequestJob(request, network_delegate), | 58 : URLRequestJob(request, network_delegate), |
93 file_path_(file_path), | 59 file_path_(file_path), |
94 is_directory_(false), | 60 stream_(new FileStream(NULL)), |
95 remaining_bytes_(0) { | 61 remaining_bytes_(0), |
| 62 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
96 } | 63 } |
97 | 64 |
98 // static | 65 // static |
99 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, | 66 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, |
100 NetworkDelegate* network_delegate, | 67 NetworkDelegate* network_delegate, |
101 const std::string& scheme) { | 68 const std::string& scheme) { |
102 FilePath file_path; | 69 FilePath file_path; |
103 const bool is_file = FileURLToFilePath(request->url(), &file_path); | 70 const bool is_file = FileURLToFilePath(request->url(), &file_path); |
104 | 71 |
105 // Check file access permissions. | 72 // Check file access permissions. |
(...skipping 11 matching lines...) Expand all Loading... |
117 file_util::EndsWithSeparator(file_path) && | 84 file_util::EndsWithSeparator(file_path) && |
118 file_path.IsAbsolute()) | 85 file_path.IsAbsolute()) |
119 return new URLRequestFileDirJob(request, network_delegate, file_path); | 86 return new URLRequestFileDirJob(request, network_delegate, file_path); |
120 | 87 |
121 // Use a regular file request job for all non-directories (including invalid | 88 // Use a regular file request job for all non-directories (including invalid |
122 // file names). | 89 // file names). |
123 return new URLRequestFileJob(request, network_delegate, file_path); | 90 return new URLRequestFileJob(request, network_delegate, file_path); |
124 } | 91 } |
125 | 92 |
126 void URLRequestFileJob::Start() { | 93 void URLRequestFileJob::Start() { |
127 DCHECK(!async_resolver_); | 94 FileMetaInfo* meta_info = new FileMetaInfo(); |
128 async_resolver_ = new AsyncResolver(this); | 95 base::WorkerPool::PostTaskAndReply( |
129 base::WorkerPool::PostTask( | |
130 FROM_HERE, | 96 FROM_HERE, |
131 base::Bind(&AsyncResolver::Resolve, async_resolver_.get(), file_path_), | 97 base::Bind(&URLRequestFileJob::FetchMetaInfo, file_path_, |
| 98 base::Unretained(meta_info)), |
| 99 base::Bind(&URLRequestFileJob::DidFetchMetaInfo, |
| 100 weak_ptr_factory_.GetWeakPtr(), |
| 101 base::Owned(meta_info)), |
132 true); | 102 true); |
133 } | 103 } |
134 | 104 |
135 void URLRequestFileJob::Kill() { | 105 void URLRequestFileJob::Kill() { |
136 stream_.reset(); | 106 stream_.reset(); |
137 | 107 weak_ptr_factory_.InvalidateWeakPtrs(); |
138 if (async_resolver_) { | |
139 async_resolver_->Cancel(); | |
140 async_resolver_ = NULL; | |
141 } | |
142 | 108 |
143 URLRequestJob::Kill(); | 109 URLRequestJob::Kill(); |
144 } | 110 } |
145 | 111 |
146 bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size, | 112 bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size, |
147 int *bytes_read) { | 113 int *bytes_read) { |
148 DCHECK_NE(dest_size, 0); | 114 DCHECK_NE(dest_size, 0); |
149 DCHECK(bytes_read); | 115 DCHECK(bytes_read); |
150 DCHECK_GE(remaining_bytes_, 0); | 116 DCHECK_GE(remaining_bytes_, 0); |
151 | 117 |
152 if (remaining_bytes_ < dest_size) | 118 if (remaining_bytes_ < dest_size) |
153 dest_size = static_cast<int>(remaining_bytes_); | 119 dest_size = static_cast<int>(remaining_bytes_); |
154 | 120 |
155 // If we should copy zero bytes because |remaining_bytes_| is zero, short | 121 // If we should copy zero bytes because |remaining_bytes_| is zero, short |
156 // circuit here. | 122 // circuit here. |
157 if (!dest_size) { | 123 if (!dest_size) { |
158 *bytes_read = 0; | 124 *bytes_read = 0; |
159 return true; | 125 return true; |
160 } | 126 } |
161 | 127 |
162 int rv = stream_->Read(dest, dest_size, | 128 int rv = stream_->Read(dest, dest_size, |
163 base::Bind(&URLRequestFileJob::DidRead, | 129 base::Bind(&URLRequestFileJob::DidRead, |
164 base::Unretained(this))); | 130 weak_ptr_factory_.GetWeakPtr())); |
165 if (rv >= 0) { | 131 if (rv >= 0) { |
166 // Data is immediately available. | 132 // Data is immediately available. |
167 *bytes_read = rv; | 133 *bytes_read = rv; |
168 remaining_bytes_ -= rv; | 134 remaining_bytes_ -= rv; |
169 DCHECK_GE(remaining_bytes_, 0); | 135 DCHECK_GE(remaining_bytes_, 0); |
170 return true; | 136 return true; |
171 } | 137 } |
172 | 138 |
173 // Otherwise, a read error occured. We may just need to wait... | 139 // Otherwise, a read error occured. We may just need to wait... |
174 if (rv == ERR_IO_PENDING) { | 140 if (rv == ERR_IO_PENDING) { |
175 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); | 141 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
176 } else { | 142 } else { |
177 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | 143 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); |
178 } | 144 } |
179 return false; | 145 return false; |
180 } | 146 } |
181 | 147 |
182 bool URLRequestFileJob::IsRedirectResponse(GURL* location, | 148 bool URLRequestFileJob::IsRedirectResponse(GURL* location, |
183 int* http_status_code) { | 149 int* http_status_code) { |
184 if (is_directory_) { | 150 if (meta_info_.is_directory) { |
185 // This happens when we discovered the file is a directory, so needs a | 151 // This happens when we discovered the file is a directory, so needs a |
186 // slash at the end of the path. | 152 // slash at the end of the path. |
187 std::string new_path = request_->url().path(); | 153 std::string new_path = request_->url().path(); |
188 new_path.push_back('/'); | 154 new_path.push_back('/'); |
189 GURL::Replacements replacements; | 155 GURL::Replacements replacements; |
190 replacements.SetPathStr(new_path); | 156 replacements.SetPathStr(new_path); |
191 | 157 |
192 *location = request_->url().ReplaceComponents(replacements); | 158 *location = request_->url().ReplaceComponents(replacements); |
193 *http_status_code = 301; // simulate a permanent redirect | 159 *http_status_code = 301; // simulate a permanent redirect |
194 return true; | 160 return true; |
(...skipping 21 matching lines...) Expand all Loading... |
216 #endif | 182 #endif |
217 } | 183 } |
218 | 184 |
219 Filter* URLRequestFileJob::SetupFilter() const { | 185 Filter* URLRequestFileJob::SetupFilter() const { |
220 // Bug 9936 - .svgz files needs to be decompressed. | 186 // Bug 9936 - .svgz files needs to be decompressed. |
221 return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz") | 187 return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz") |
222 ? Filter::GZipFactory() : NULL; | 188 ? Filter::GZipFactory() : NULL; |
223 } | 189 } |
224 | 190 |
225 bool URLRequestFileJob::GetMimeType(std::string* mime_type) const { | 191 bool URLRequestFileJob::GetMimeType(std::string* mime_type) const { |
226 // URL requests should not block on the disk! On Windows this goes to the | |
227 // registry. | |
228 // http://code.google.com/p/chromium/issues/detail?id=59849 | |
229 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
230 DCHECK(request_); | 192 DCHECK(request_); |
231 return GetMimeTypeFromFile(file_path_, mime_type); | 193 if (meta_info_.mime_type_result) { |
| 194 *mime_type = meta_info_.mime_type; |
| 195 return true; |
| 196 } |
| 197 return false; |
232 } | 198 } |
233 | 199 |
234 void URLRequestFileJob::SetExtraRequestHeaders( | 200 void URLRequestFileJob::SetExtraRequestHeaders( |
235 const HttpRequestHeaders& headers) { | 201 const HttpRequestHeaders& headers) { |
236 std::string range_header; | 202 std::string range_header; |
237 if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) { | 203 if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) { |
238 // We only care about "Range" header here. | 204 // We only care about "Range" header here. |
239 std::vector<HttpByteRange> ranges; | 205 std::vector<HttpByteRange> ranges; |
240 if (HttpUtil::ParseRangeHeader(range_header, &ranges)) { | 206 if (HttpUtil::ParseRangeHeader(range_header, &ranges)) { |
241 if (ranges.size() == 1) { | 207 if (ranges.size() == 1) { |
242 byte_range_ = ranges[0]; | 208 byte_range_ = ranges[0]; |
243 } else { | 209 } else { |
244 // We don't support multiple range requests in one single URL request, | 210 // We don't support multiple range requests in one single URL request, |
245 // because we need to do multipart encoding here. | 211 // because we need to do multipart encoding here. |
246 // TODO(hclam): decide whether we want to support multiple range | 212 // TODO(hclam): decide whether we want to support multiple range |
247 // requests. | 213 // requests. |
248 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, | 214 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
249 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); | 215 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
250 } | 216 } |
251 } | 217 } |
252 } | 218 } |
253 } | 219 } |
254 | 220 |
255 URLRequestFileJob::~URLRequestFileJob() { | 221 URLRequestFileJob::~URLRequestFileJob() { |
256 DCHECK(!async_resolver_); | |
257 } | 222 } |
258 | 223 |
259 void URLRequestFileJob::DidResolve( | 224 void URLRequestFileJob::FetchMetaInfo(const FilePath& file_path, |
260 bool exists, const base::PlatformFileInfo& file_info) { | 225 FileMetaInfo* meta_info) { |
261 async_resolver_ = NULL; | 226 base::PlatformFileInfo platform_info; |
| 227 meta_info->file_exists = file_util::GetFileInfo(file_path, &platform_info); |
| 228 if (meta_info->file_exists) { |
| 229 meta_info->file_size = platform_info.size; |
| 230 meta_info->is_directory = platform_info.is_directory; |
| 231 } |
| 232 // On Windows GetMimeTypeFromFile() goes to the registry. Thus it should be |
| 233 // done in WorkerPool. |
| 234 meta_info->mime_type_result = GetMimeTypeFromFile(file_path, |
| 235 &meta_info->mime_type); |
| 236 } |
262 | 237 |
263 // We may have been orphaned... | 238 void URLRequestFileJob::DidFetchMetaInfo(const FileMetaInfo* meta_info) { |
264 if (!request_) | 239 meta_info_ = *meta_info; |
265 return; | |
266 | 240 |
267 is_directory_ = file_info.is_directory; | |
268 | |
269 int rv = OK; | |
270 // We use URLRequestFileJob to handle files as well as directories without | 241 // We use URLRequestFileJob to handle files as well as directories without |
271 // trailing slash. | 242 // trailing slash. |
272 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise, | 243 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise, |
273 // we will append trailing slash and redirect to FileDirJob. | 244 // we will append trailing slash and redirect to FileDirJob. |
274 // A special case is "\" on Windows. We should resolve as invalid. | 245 // A special case is "\" on Windows. We should resolve as invalid. |
275 // However, Windows resolves "\" to "C:\", thus reports it as existent. | 246 // However, Windows resolves "\" to "C:\", thus reports it as existent. |
276 // So what happens is we append it with trailing slash and redirect it to | 247 // So what happens is we append it with trailing slash and redirect it to |
277 // FileDirJob where it is resolved as invalid. | 248 // FileDirJob where it is resolved as invalid. |
278 if (!exists) { | 249 if (!meta_info_.file_exists) { |
279 rv = ERR_FILE_NOT_FOUND; | 250 DidOpen(ERR_FILE_NOT_FOUND); |
280 } else if (!is_directory_) { | 251 return; |
281 stream_.reset(new FileStream(NULL)); | |
282 | |
283 // URL requests should not block on the disk! | |
284 // http://code.google.com/p/chromium/issues/detail?id=59849 | |
285 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
286 | |
287 int flags = base::PLATFORM_FILE_OPEN | | |
288 base::PLATFORM_FILE_READ | | |
289 base::PLATFORM_FILE_ASYNC; | |
290 rv = stream_->OpenSync(file_path_, flags); | |
291 } | 252 } |
292 | 253 if (meta_info_.is_directory) { |
293 if (rv != OK) { | 254 DidOpen(OK); |
294 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | |
295 return; | 255 return; |
296 } | 256 } |
297 | 257 |
298 if (!byte_range_.ComputeBounds(file_info.size)) { | 258 int flags = base::PLATFORM_FILE_OPEN | |
| 259 base::PLATFORM_FILE_READ | |
| 260 base::PLATFORM_FILE_ASYNC; |
| 261 int rv = stream_->Open(file_path_, flags, |
| 262 base::Bind(&URLRequestFileJob::DidOpen, |
| 263 weak_ptr_factory_.GetWeakPtr())); |
| 264 if (rv != ERR_IO_PENDING) |
| 265 DidOpen(rv); |
| 266 } |
| 267 |
| 268 void URLRequestFileJob::DidOpen(int result) { |
| 269 if (result != OK) { |
| 270 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); |
| 271 return; |
| 272 } |
| 273 |
| 274 if (!byte_range_.ComputeBounds(meta_info_.file_size)) { |
299 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, | 275 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
300 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); | 276 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
301 return; | 277 return; |
302 } | 278 } |
303 | 279 |
304 remaining_bytes_ = byte_range_.last_byte_position() - | 280 remaining_bytes_ = byte_range_.last_byte_position() - |
305 byte_range_.first_byte_position() + 1; | 281 byte_range_.first_byte_position() + 1; |
306 DCHECK_GE(remaining_bytes_, 0); | 282 DCHECK_GE(remaining_bytes_, 0); |
307 | 283 |
308 // URL requests should not block on the disk! | 284 if (remaining_bytes_ > 0 && byte_range_.first_byte_position() != 0) { |
309 // http://code.google.com/p/chromium/issues/detail?id=59849 | 285 int rv = stream_->Seek(FROM_BEGIN, byte_range_.first_byte_position(), |
310 { | 286 base::Bind(&URLRequestFileJob::DidSeek, |
311 base::ThreadRestrictions::ScopedAllowIO allow_io; | 287 weak_ptr_factory_.GetWeakPtr())); |
312 // Do the seek at the beginning of the request. | 288 if (rv != ERR_IO_PENDING) { |
313 if (remaining_bytes_ > 0 && | 289 // stream_->Seek() failed, so pass an intentionally erroneous value |
314 byte_range_.first_byte_position() != 0 && | 290 // into DidSeek(). |
315 byte_range_.first_byte_position() != | 291 DidSeek(-1); |
316 stream_->SeekSync(FROM_BEGIN, byte_range_.first_byte_position())) { | |
317 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, | |
318 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); | |
319 return; | |
320 } | 292 } |
| 293 } else { |
| 294 // We didn't need to call stream_->Seek() at all, so we pass to DidSeek() |
| 295 // the value that would mean seek success. This way we skip the code |
| 296 // handling seek failure. |
| 297 DidSeek(byte_range_.first_byte_position()); |
| 298 } |
| 299 } |
| 300 |
| 301 void URLRequestFileJob::DidSeek(int64 result) { |
| 302 if (result != byte_range_.first_byte_position()) { |
| 303 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 304 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 305 return; |
321 } | 306 } |
322 | 307 |
323 set_expected_content_size(remaining_bytes_); | 308 set_expected_content_size(remaining_bytes_); |
324 NotifyHeadersComplete(); | 309 NotifyHeadersComplete(); |
325 } | 310 } |
326 | 311 |
327 void URLRequestFileJob::DidRead(int result) { | 312 void URLRequestFileJob::DidRead(int result) { |
328 if (result > 0) { | 313 if (result > 0) { |
329 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status | 314 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status |
330 } else if (result == 0) { | 315 } else if (result == 0) { |
331 NotifyDone(URLRequestStatus()); | 316 NotifyDone(URLRequestStatus()); |
332 } else { | 317 } else { |
333 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); | 318 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); |
334 } | 319 } |
335 | 320 |
336 remaining_bytes_ -= result; | 321 remaining_bytes_ -= result; |
337 DCHECK_GE(remaining_bytes_, 0); | 322 DCHECK_GE(remaining_bytes_, 0); |
338 | 323 |
339 NotifyReadComplete(result); | 324 NotifyReadComplete(result); |
340 } | 325 } |
341 | 326 |
342 } // namespace net | 327 } // namespace net |
OLD | NEW |