1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/drive/drive_url_request_job.h"
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/chromeos/drive/file_system_util.h"
15 #include "chrome/browser/extensions/api/file_handlers/mime_util.h"
16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "chrome/common/url_constants.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/storage_partition.h"
20 #include "net/base/net_errors.h"
21 #include "net/http/http_byte_range.h"
22 #include "net/http/http_request_headers.h"
23 #include "net/http/http_response_info.h"
24 #include "net/http/http_util.h"
25 #include "net/url_request/url_request.h"
26 #include "net/url_request/url_request_status.h"
27 #include "storage/browser/fileapi/file_system_backend.h"
28 #include "storage/browser/fileapi/file_system_context.h"
29 #include "storage/browser/fileapi/file_system_operation_runner.h"
31 using content::BrowserThread
;
36 const char kMimeTypeForRFC822
[] = "message/rfc822";
37 const char kMimeTypeForMHTML
[] = "multipart/related";
39 // Check if the |url| points a valid location or not.
40 bool IsValidURL(const storage::FileSystemURL
& url
) {
42 case storage::kFileSystemTypeDrive
: {
43 const base::FilePath my_drive_path
= util::GetDriveMyDriveRootPath();
44 const base::FilePath drive_other_path
=
45 util::GetDriveGrandRootPath().Append(util::kDriveOtherDirName
);
46 const base::FilePath url_drive_path
=
47 util::ExtractDrivePathFromFileSystemUrl(url
);
48 return my_drive_path
== url_drive_path
||
49 my_drive_path
.IsParent(url_drive_path
) ||
50 drive_other_path
.IsParent(url_drive_path
);
57 // Helper for obtaining FileSystemContext, FileSystemURL, and mime type on the
61 // The scoped pointer to control lifetime of the instance itself. The pointer
62 // is passed to callback functions and binds the lifetime of the instance to
63 // the callback's lifetime.
64 typedef scoped_ptr
<URLHelper
> Lifetime
;
66 URLHelper(void* profile_id
,
68 const DriveURLRequestJob::HelperCallback
& callback
)
69 : profile_id_(profile_id
), url_(url
), callback_(callback
) {
70 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
71 Lifetime
lifetime(this);
72 BrowserThread::PostTask(BrowserThread::UI
,
74 base::Bind(&URLHelper::RunOnUIThread
,
75 base::Unretained(this),
76 base::Passed(&lifetime
)));
80 void RunOnUIThread(Lifetime lifetime
) {
81 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
82 Profile
* const profile
= reinterpret_cast<Profile
*>(profile_id_
);
83 if (!g_browser_process
->profile_manager()->IsValidProfile(profile
)) {
84 ReplyResult(net::ERR_FAILED
);
87 content::StoragePartition
* const storage
=
88 content::BrowserContext::GetStoragePartitionForSite(profile
, url_
);
91 scoped_refptr
<storage::FileSystemContext
> context
=
92 storage
->GetFileSystemContext();
93 DCHECK(context
.get());
95 // Obtain the absolute path in the file system.
96 base::FilePath path
= drive::util::GetDriveMountPointPath(profile
);
97 drive::util::GetDriveGrandRootPath().AppendRelativePath(
98 util::DriveURLToFilePath(url_
), &path
);
100 storage::ExternalFileSystemBackend
* const backend
=
101 context
->external_backend();
104 // Obtain the virtual path.
105 base::FilePath virtual_path
;
106 if (!backend
->GetVirtualPath(path
, &virtual_path
)) {
107 ReplyResult(net::ERR_FILE_NOT_FOUND
);
111 // Obtain the file system URL.
112 // TODO(hirono): After removing MHTML support, stop to use the special
113 // drive: scheme and use filesystem: URL directly. crbug.com/415455
114 file_system_url_
= context
->CreateCrackedFileSystemURL(
115 GURL(std::string(chrome::kDriveScheme
) + ":"),
116 storage::kFileSystemTypeExternal
,
118 if (!IsValidURL(file_system_url_
)) {
119 ReplyResult(net::ERR_INVALID_URL
);
123 file_system_context_
= context
;
125 extensions::app_file_handler_util::GetMimeTypeForLocalPath(
127 file_system_url_
.path(),
128 base::Bind(&URLHelper::OnGotMimeTypeOnUIThread
,
129 base::Unretained(this),
130 base::Passed(&lifetime
)));
133 void OnGotMimeTypeOnUIThread(Lifetime lifetime
,
134 const std::string
& mime_type
) {
135 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
136 mime_type_
= mime_type
;
138 if (mime_type_
== kMimeTypeForRFC822
)
139 mime_type_
= kMimeTypeForMHTML
;
141 ReplyResult(net::OK
);
144 void ReplyResult(net::Error error
) {
145 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
147 BrowserThread::PostTask(BrowserThread::IO
,
149 base::Bind(callback_
,
151 file_system_context_
,
156 void* const profile_id_
;
158 const DriveURLRequestJob::HelperCallback callback_
;
159 scoped_refptr
<storage::FileSystemContext
> file_system_context_
;
160 storage::FileSystemURL file_system_url_
;
161 std::string mime_type_
;
163 DISALLOW_COPY_AND_ASSIGN(URLHelper
);
168 DriveURLRequestJob::DriveURLRequestJob(void* profile_id
,
169 net::URLRequest
* request
,
170 net::NetworkDelegate
* network_delegate
)
171 : net::URLRequestJob(request
, network_delegate
),
172 profile_id_(profile_id
),
174 weak_ptr_factory_(this) {
177 void DriveURLRequestJob::SetExtraRequestHeaders(
178 const net::HttpRequestHeaders
& headers
) {
179 std::string range_header
;
180 if (headers
.GetHeader(net::HttpRequestHeaders::kRange
, &range_header
)) {
181 // Note: We only support single range requests.
182 std::vector
<net::HttpByteRange
> ranges
;
183 if (net::HttpUtil::ParseRangeHeader(range_header
, &ranges
) &&
184 ranges
.size() == 1) {
185 byte_range_
= ranges
[0];
187 // Failed to parse Range: header, so notify the error.
189 net::URLRequestStatus(net::URLRequestStatus::FAILED
,
190 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE
));
195 void DriveURLRequestJob::Start() {
196 DVLOG(1) << "Starting request";
197 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
198 DCHECK(!stream_reader_
);
200 // We only support GET request.
201 if (request()->method() != "GET") {
202 LOG(WARNING
) << "Failed to start request: "
203 << request()->method() << " method is not supported";
204 NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED
,
205 net::ERR_METHOD_NOT_SUPPORTED
));
209 // Check if the scheme is correct.
210 if (!request()->url().SchemeIs(chrome::kDriveScheme
)) {
211 NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED
,
212 net::ERR_INVALID_URL
));
217 new URLHelper(profile_id_
,
219 base::Bind(&DriveURLRequestJob::OnHelperResultObtained
,
220 weak_ptr_factory_
.GetWeakPtr()));
223 void DriveURLRequestJob::OnHelperResultObtained(
225 const scoped_refptr
<storage::FileSystemContext
>& file_system_context
,
226 const storage::FileSystemURL
& file_system_url
,
227 const std::string
& mime_type
) {
228 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
230 if (error
!= net::OK
) {
232 net::URLRequestStatus(net::URLRequestStatus::FAILED
, error
));
236 DCHECK(file_system_context
.get());
237 file_system_context_
= file_system_context
;
238 file_system_url_
= file_system_url
;
239 mime_type_
= mime_type
;
241 // Check if the entry has a redirect URL.
242 file_system_context_
->external_backend()->GetRedirectURLForContents(
244 base::Bind(&DriveURLRequestJob::OnRedirectURLObtained
,
245 weak_ptr_factory_
.GetWeakPtr()));
248 void DriveURLRequestJob::OnRedirectURLObtained(const GURL
& redirect_url
) {
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
250 redirect_url_
= redirect_url
;
251 if (!redirect_url_
.is_empty()) {
252 NotifyHeadersComplete();
256 // Obtain file system context.
257 file_system_context_
->operation_runner()->GetMetadata(
259 base::Bind(&DriveURLRequestJob::OnFileInfoObtained
,
260 weak_ptr_factory_
.GetWeakPtr()));
263 void DriveURLRequestJob::OnFileInfoObtained(base::File::Error result
,
264 const base::File::Info
& file_info
) {
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
267 if (result
== base::File::FILE_ERROR_NOT_FOUND
) {
268 NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED
,
269 net::ERR_FILE_NOT_FOUND
));
273 if (result
!= base::File::FILE_OK
|| file_info
.is_directory
||
274 file_info
.size
< 0) {
276 net::URLRequestStatus(net::URLRequestStatus::FAILED
, net::ERR_FAILED
));
280 // Compute content size.
281 if (!byte_range_
.ComputeBounds(file_info
.size
)) {
282 NotifyStartError(net::URLRequestStatus(
283 net::URLRequestStatus::FAILED
, net::ERR_REQUEST_RANGE_NOT_SATISFIABLE
));
286 const int64 offset
= byte_range_
.first_byte_position();
288 byte_range_
.last_byte_position() + 1 - byte_range_
.first_byte_position();
289 set_expected_content_size(size
);
290 remaining_bytes_
= size
;
292 // Create file stream reader.
293 stream_reader_
= file_system_context_
->CreateFileStreamReader(
294 file_system_url_
, offset
, size
, base::Time());
295 if (!stream_reader_
) {
296 NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED
,
297 net::ERR_FILE_NOT_FOUND
));
301 NotifyHeadersComplete();
304 void DriveURLRequestJob::Kill() {
305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
307 stream_reader_
.reset();
308 file_system_context_
= NULL
;
309 net::URLRequestJob::Kill();
310 weak_ptr_factory_
.InvalidateWeakPtrs();
313 bool DriveURLRequestJob::GetMimeType(std::string
* mime_type
) const {
314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
315 mime_type
->assign(mime_type_
);
316 return !mime_type
->empty();
319 bool DriveURLRequestJob::IsRedirectResponse(
320 GURL
* location
, int* http_status_code
) {
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
322 if (redirect_url_
.is_empty())
325 // Redirect a hosted document.
326 *location
= redirect_url_
;
327 const int kHttpFound
= 302;
328 *http_status_code
= kHttpFound
;
332 bool DriveURLRequestJob::ReadRawData(
333 net::IOBuffer
* buf
, int buf_size
, int* bytes_read
) {
334 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
335 DCHECK(stream_reader_
);
337 if (remaining_bytes_
== 0) {
343 stream_reader_
->Read(buf
,
344 std::min
<int64
>(buf_size
, remaining_bytes_
),
345 base::Bind(&DriveURLRequestJob::OnReadCompleted
,
346 weak_ptr_factory_
.GetWeakPtr()));
348 if (result
== net::ERR_IO_PENDING
) {
349 // The data is not yet available.
350 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING
, 0));
355 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED
, result
));
359 // Reading has been finished immediately.
360 *bytes_read
= result
;
361 remaining_bytes_
-= result
;
365 DriveURLRequestJob::~DriveURLRequestJob() {
368 void DriveURLRequestJob::OnReadCompleted(int read_result
) {
369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
371 if (read_result
< 0) {
372 DCHECK_NE(read_result
, net::ERR_IO_PENDING
);
373 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED
,
377 remaining_bytes_
-= read_result
;
378 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status.
379 NotifyReadComplete(read_result
);