1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be
3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file.
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// For loading files, we make use of overlapped i/o to ensure that reading from
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// the filesystem (e.g., a network filesystem) does not block the calling
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// thread.  An alternative approach would be to use a background thread or pool
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// of threads, but it seems better to leverage the operating system's ability
9c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// to do background file reads for us.
10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Since overlapped reads require a 'static' buffer for the duration of the
12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// asynchronous read, the URLRequestFileJob keeps a buffer as a member var.  In
13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// URLRequestFileJob::Read, data is simply copied from the object's buffer into
14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// the given buffer.  If there is no data to copy, the URLRequestFileJob
15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// attempts to read more from the file to fill its buffer.  If reading from the
16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// file does not complete synchronously, then the URLRequestFileJob waits for a
17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// signal from the OS that the overlapped read has completed.  It does so by
18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// leveraging the MessageLoop::WatchObject API.
19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/url_request/url_request_file_job.h"
21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/compiler_specific.h"
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/message_loop.h"
24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/platform_file.h"
25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_util.h"
2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/synchronization/lock.h"
273f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/worker_pool.h"
283f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread_restrictions.h"
293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "build/build_config.h"
30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "googleurl/src/gurl.h"
31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/io_buffer.h"
32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/load_flags.h"
33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/mime_util.h"
34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/net_errors.h"
35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/net_util.h"
36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_util.h"
37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/url_request/url_request.h"
384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "net/url_request/url_request_error_job.h"
39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/url_request/url_request_file_dir_job.h"
40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
4121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsennamespace net {
4221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#if defined(OS_WIN)
444a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochclass URLRequestFileJob::AsyncResolver
454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    : public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> {
46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott public:
47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  explicit AsyncResolver(URLRequestFileJob* owner)
48c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      : owner_(owner), owner_loop_(MessageLoop::current()) {
49c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
50c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
51c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  void Resolve(const FilePath& file_path) {
523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    base::PlatformFileInfo file_info;
53c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    bool exists = file_util::GetFileInfo(file_path, &file_info);
5472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::AutoLock locked(lock_);
55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (owner_loop_) {
56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          this, &AsyncResolver::ReturnResults, exists, file_info));
58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
60c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
61c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  void Cancel() {
62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    owner_ = NULL;
63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
6472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::AutoLock locked(lock_);
65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    owner_loop_ = NULL;
66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott private:
69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  friend class base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver>;
70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  ~AsyncResolver() {}
72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  void ReturnResults(bool exists, const base::PlatformFileInfo& file_info) {
74c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (owner_)
75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      owner_->DidResolve(exists, file_info);
76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  URLRequestFileJob* owner_;
79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
8072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::Lock lock_;
81c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  MessageLoop* owner_loop_;
82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott};
83c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif
84c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
8572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenURLRequestFileJob::URLRequestFileJob(URLRequest* request,
8672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                     const FilePath& file_path)
8772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    : URLRequestJob(request),
8872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      file_path_(file_path),
8972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      ALLOW_THIS_IN_INITIALIZER_LIST(
9072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          io_callback_(this, &URLRequestFileJob::DidRead)),
9172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      is_directory_(false),
9272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      remaining_bytes_(0),
9372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
9472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
9572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
96c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
9721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenURLRequestJob* URLRequestFileJob::Factory(URLRequest* request,
9821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen                                          const std::string& scheme) {
994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  FilePath file_path;
10121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const bool is_file = FileURLToFilePath(request->url(), &file_path);
1024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#if defined(OS_CHROMEOS)
1044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // Check file access.
1054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (AccessDisabled(file_path))
10621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    return new URLRequestErrorJob(request, ERR_ACCESS_DENIED);
1074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#endif
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We need to decide whether to create URLRequestFileJob for file access or
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // URLRequestFileDirJob for directory access. To avoid accessing the
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // filesystem, we only look at the path string here.
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The code in the URLRequestFileJob::Start() method discovers that a path,
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // which doesn't end with a slash, should really be treated as a directory,
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // and it then redirects to the URLRequestFileDirJob.
1154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (is_file &&
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      file_util::EndsWithSeparator(file_path) &&
117c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      file_path.IsAbsolute())
118c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return new URLRequestFileDirJob(request, file_path);
119c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
120c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Use a regular file request job for all non-directories (including invalid
121c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // file names).
122c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return new URLRequestFileJob(request, file_path);
123c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
124c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
12572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#if defined(OS_CHROMEOS)
12672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenstatic const char* const kLocalAccessWhiteList[] = {
12772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  "/home/chronos/user/Downloads",
12872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  "/media",
129dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  "/opt/oem",
13072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  "/usr/share/chromeos-assets",
13172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  "/tmp",
13272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  "/var/log",
13372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen};
134c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
13572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// static
13672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool URLRequestFileJob::AccessDisabled(const FilePath& file_path) {
13772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (URLRequest::IsFileAccessAllowed()) {  // for tests.
13872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return false;
13972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
14072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
14172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  for (size_t i = 0; i < arraysize(kLocalAccessWhiteList); ++i) {
14272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const FilePath white_listed_path(kLocalAccessWhiteList[i]);
14372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // FilePath::operator== should probably handle trailing seperators.
14472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (white_listed_path == file_path.StripTrailingSeparators() ||
14572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        white_listed_path.IsParent(file_path)) {
14672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return false;
14772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
14872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
14972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return true;
150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
15172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#endif
152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid URLRequestFileJob::Start() {
154c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#if defined(OS_WIN)
155c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Resolve UNC paths on a background thread.
156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!file_path_.value().compare(0, 2, L"\\\\")) {
157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DCHECK(!async_resolver_);
158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    async_resolver_ = new AsyncResolver(this);
1593f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    base::WorkerPool::PostTask(FROM_HERE, NewRunnableMethod(
160c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        async_resolver_.get(), &AsyncResolver::Resolve, file_path_), true);
161c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif
164513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
165513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // URL requests should not block on the disk!
166513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  //   http://code.google.com/p/chromium/issues/detail?id=59849
167513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  bool exists;
1683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  base::PlatformFileInfo file_info;
169513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  {
170513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    base::ThreadRestrictions::ScopedAllowIO allow_io;
171513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    exists = file_util::GetFileInfo(file_path_, &file_info);
172513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  }
173c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
174c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Continue asynchronously.
17521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  MessageLoop::current()->PostTask(
17621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      FROM_HERE,
17721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      method_factory_.NewRunnableMethod(
17821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen          &URLRequestFileJob::DidResolve, exists, file_info));
179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
180c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid URLRequestFileJob::Kill() {
182c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  stream_.Close();
183c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
184c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#if defined(OS_WIN)
185c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (async_resolver_) {
186c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    async_resolver_->Cancel();
187c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    async_resolver_ = NULL;
188c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
189c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif
190c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
191c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  URLRequestJob::Kill();
19221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  method_factory_.RevokeAll();
193c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
194c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
19521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenbool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size,
196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                    int *bytes_read) {
197c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK_NE(dest_size, 0);
198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK(bytes_read);
199c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK_GE(remaining_bytes_, 0);
200c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
201c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (remaining_bytes_ < dest_size)
202c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    dest_size = static_cast<int>(remaining_bytes_);
203c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
204c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // If we should copy zero bytes because |remaining_bytes_| is zero, short
205c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // circuit here.
206c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!dest_size) {
207c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    *bytes_read = 0;
208c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
209c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
210c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
211c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int rv = stream_.Read(dest->data(), dest_size, &io_callback_);
212c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (rv >= 0) {
213c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Data is immediately available.
214c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    *bytes_read = rv;
215c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    remaining_bytes_ -= rv;
216c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DCHECK_GE(remaining_bytes_, 0);
217c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
218c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
219c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
220c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Otherwise, a read error occured.  We may just need to wait...
22121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (rv == ERR_IO_PENDING) {
222c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
223c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else {
224c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
225c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
226c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return false;
227c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
228c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
22972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool URLRequestFileJob::IsRedirectResponse(GURL* location,
23072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                           int* http_status_code) {
23172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (is_directory_) {
23272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // This happens when we discovered the file is a directory, so needs a
23372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // slash at the end of the path.
23472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    std::string new_path = request_->url().path();
23572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    new_path.push_back('/');
23672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    GURL::Replacements replacements;
23772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    replacements.SetPathStr(new_path);
23872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
23972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    *location = request_->url().ReplaceComponents(replacements);
24072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    *http_status_code = 301;  // simulate a permanent redirect
24172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return true;
24272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
24372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
24472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#if defined(OS_WIN)
24572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Follow a Windows shortcut.
24672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // We just resolve .lnk file, ignore others.
24772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!LowerCaseEqualsASCII(file_path_.Extension(), ".lnk"))
24872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return false;
24972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
25072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  FilePath new_path = file_path_;
25172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  bool resolved;
25272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  resolved = file_util::ResolveShortcut(&new_path);
25372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
25472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // If shortcut is not resolved succesfully, do not redirect.
25572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!resolved)
25672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return false;
25772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
25872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  *location = FilePathToFileURL(new_path);
25972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  *http_status_code = 301;
26072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return true;
26172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#else
26272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return false;
26372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#endif
26472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
26572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
266ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenFilter* URLRequestFileJob::SetupFilter() const {
267c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Bug 9936 - .svgz files needs to be decompressed.
268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz")
269ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ? Filter::GZipFactory() : NULL;
270c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
271c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
272c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool URLRequestFileJob::GetMimeType(std::string* mime_type) const {
273513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // URL requests should not block on the disk!  On Windows this goes to the
274513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // registry.
275513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  //   http://code.google.com/p/chromium/issues/detail?id=59849
276513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::ScopedAllowIO allow_io;
277c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK(request_);
27821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  return GetMimeTypeFromFile(file_path_, mime_type);
279c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
280c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid URLRequestFileJob::SetExtraRequestHeaders(
28221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    const HttpRequestHeaders& headers) {
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string range_header;
28421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) {
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We only care about "Range" header here.
28621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    std::vector<HttpByteRange> ranges;
28721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    if (HttpUtil::ParseRangeHeader(range_header, &ranges)) {
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (ranges.size() == 1) {
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        byte_range_ = ranges[0];
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // We don't support multiple range requests in one single URL request,
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // because we need to do multipart encoding here.
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // TODO(hclam): decide whether we want to support multiple range
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // requests.
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
29621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen                                    ERR_REQUEST_RANGE_NOT_SATISFIABLE));
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
298c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
299c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
300c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
301c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
30272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenURLRequestFileJob::~URLRequestFileJob() {
30372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#if defined(OS_WIN)
30472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DCHECK(!async_resolver_);
30572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#endif
30672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
30772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
308c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid URLRequestFileJob::DidResolve(
3093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    bool exists, const base::PlatformFileInfo& file_info) {
310c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#if defined(OS_WIN)
311c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  async_resolver_ = NULL;
312c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif
313c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
314c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We may have been orphaned...
315c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!request_)
316c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
317c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  is_directory_ = file_info.is_directory;
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
32021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  int rv = OK;
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We use URLRequestFileJob to handle files as well as directories without
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // trailing slash.
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise,
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // we will append trailing slash and redirect to FileDirJob.
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // A special case is "\" on Windows. We should resolve as invalid.
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // However, Windows resolves "\" to "C:\", thus reports it as existent.
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // So what happens is we append it with trailing slash and redirect it to
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // FileDirJob where it is resolved as invalid.
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!exists) {
33021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    rv = ERR_FILE_NOT_FOUND;
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (!is_directory_) {
332513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    // URL requests should not block on the disk!
333513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    //   http://code.google.com/p/chromium/issues/detail?id=59849
334513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    base::ThreadRestrictions::ScopedAllowIO allow_io;
335513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
336c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    int flags = base::PLATFORM_FILE_OPEN |
337c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                base::PLATFORM_FILE_READ |
338c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                base::PLATFORM_FILE_ASYNC;
339c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    rv = stream_.Open(file_path_, flags);
340c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
341c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
34221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (rv != OK) {
343c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
344c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
345c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
346c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
347c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!byte_range_.ComputeBounds(file_info.size)) {
348c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
34921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen               ERR_REQUEST_RANGE_NOT_SATISFIABLE));
350c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
351c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
352c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
353c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  remaining_bytes_ = byte_range_.last_byte_position() -
354c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     byte_range_.first_byte_position() + 1;
355c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK_GE(remaining_bytes_, 0);
356c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
357ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // URL requests should not block on the disk!
358ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  //   http://code.google.com/p/chromium/issues/detail?id=59849
359ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  {
360ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    base::ThreadRestrictions::ScopedAllowIO allow_io;
361ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Do the seek at the beginning of the request.
362ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (remaining_bytes_ > 0 &&
363ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        byte_range_.first_byte_position() != 0 &&
364ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        byte_range_.first_byte_position() !=
365ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        stream_.Seek(FROM_BEGIN, byte_range_.first_byte_position())) {
366ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
367ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                  ERR_REQUEST_RANGE_NOT_SATISFIABLE));
368ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return;
369ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
370c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
371c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
372c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  set_expected_content_size(remaining_bytes_);
373c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  NotifyHeadersComplete();
374c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
375c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
376c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid URLRequestFileJob::DidRead(int result) {
377c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (result > 0) {
378c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SetStatus(URLRequestStatus());  // Clear the IO_PENDING status
379c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else if (result == 0) {
380c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    NotifyDone(URLRequestStatus());
381c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else {
382c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
383c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
384c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
385c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  remaining_bytes_ -= result;
386c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK_GE(remaining_bytes_, 0);
387c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
388c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  NotifyReadComplete(result);
389c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
390c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
39121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen}  // namespace net
392