1 // Copyright (c) 2014 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 "content/browser/android/url_request_content_job.h"
7 #include "base/android/content_uri_utils.h"
9 #include "base/files/file_util.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/task_runner.h"
12 #include "net/base/file_stream.h"
13 #include "net/base/io_buffer.h"
14 #include "net/base/net_errors.h"
15 #include "net/http/http_util.h"
16 #include "net/url_request/url_request_error_job.h"
21 // TODO(qinmin): Refactor this class to reuse the common code in
22 // url_request_file_job.cc.
23 URLRequestContentJob::ContentMetaInfo::ContentMetaInfo()
24 : content_exists(false),
28 URLRequestContentJob::URLRequestContentJob(
29 net::URLRequest
* request
,
30 net::NetworkDelegate
* network_delegate
,
31 const base::FilePath
& content_path
,
32 const scoped_refptr
<base::TaskRunner
>& content_task_runner
)
33 : net::URLRequestJob(request
, network_delegate
),
34 content_path_(content_path
),
35 stream_(new net::FileStream(content_task_runner
)),
36 content_task_runner_(content_task_runner
),
39 weak_ptr_factory_(this) {}
41 void URLRequestContentJob::Start() {
42 ContentMetaInfo
* meta_info
= new ContentMetaInfo();
43 content_task_runner_
->PostTaskAndReply(
45 base::Bind(&URLRequestContentJob::FetchMetaInfo
, content_path_
,
46 base::Unretained(meta_info
)),
47 base::Bind(&URLRequestContentJob::DidFetchMetaInfo
,
48 weak_ptr_factory_
.GetWeakPtr(),
49 base::Owned(meta_info
)));
52 void URLRequestContentJob::Kill() {
54 weak_ptr_factory_
.InvalidateWeakPtrs();
56 net::URLRequestJob::Kill();
59 bool URLRequestContentJob::ReadRawData(net::IOBuffer
* dest
,
62 DCHECK_GT(dest_size
, 0);
64 DCHECK_GE(remaining_bytes_
, 0);
66 if (remaining_bytes_
< dest_size
)
67 dest_size
= static_cast<int>(remaining_bytes_
);
69 // If we should copy zero bytes because |remaining_bytes_| is zero, short
76 int rv
= stream_
->Read(dest
,
78 base::Bind(&URLRequestContentJob::DidRead
,
79 weak_ptr_factory_
.GetWeakPtr(),
80 make_scoped_refptr(dest
)));
82 // Data is immediately available.
84 remaining_bytes_
-= rv
;
85 DCHECK_GE(remaining_bytes_
, 0);
89 // Otherwise, a read error occured. We may just need to wait...
90 if (rv
== net::ERR_IO_PENDING
) {
92 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING
, 0));
94 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED
, rv
));
99 bool URLRequestContentJob::IsRedirectResponse(GURL
* location
,
100 int* http_status_code
) {
104 bool URLRequestContentJob::GetMimeType(std::string
* mime_type
) const {
106 if (!meta_info_
.mime_type
.empty()) {
107 *mime_type
= meta_info_
.mime_type
;
113 void URLRequestContentJob::SetExtraRequestHeaders(
114 const net::HttpRequestHeaders
& headers
) {
115 std::string range_header
;
116 if (!headers
.GetHeader(net::HttpRequestHeaders::kRange
, &range_header
))
119 // We only care about "Range" header here.
120 std::vector
<net::HttpByteRange
> ranges
;
121 if (net::HttpUtil::ParseRangeHeader(range_header
, &ranges
)) {
122 if (ranges
.size() == 1) {
123 byte_range_
= ranges
[0];
125 // We don't support multiple range requests.
126 NotifyDone(net::URLRequestStatus(
127 net::URLRequestStatus::FAILED
,
128 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE
));
133 URLRequestContentJob::~URLRequestContentJob() {}
135 void URLRequestContentJob::FetchMetaInfo(const base::FilePath
& content_path
,
136 ContentMetaInfo
* meta_info
) {
137 base::File::Info file_info
;
138 meta_info
->content_exists
= base::GetFileInfo(content_path
, &file_info
);
139 if (meta_info
->content_exists
) {
140 meta_info
->content_size
= file_info
.size
;
141 meta_info
->mime_type
= base::GetContentUriMimeType(content_path
);
145 void URLRequestContentJob::DidFetchMetaInfo(const ContentMetaInfo
* meta_info
) {
146 meta_info_
= *meta_info
;
148 if (!meta_info_
.content_exists
) {
149 DidOpen(net::ERR_FILE_NOT_FOUND
);
153 int flags
= base::File::FLAG_OPEN
|
154 base::File::FLAG_READ
|
155 base::File::FLAG_ASYNC
;
156 int rv
= stream_
->Open(content_path_
, flags
,
157 base::Bind(&URLRequestContentJob::DidOpen
,
158 weak_ptr_factory_
.GetWeakPtr()));
159 if (rv
!= net::ERR_IO_PENDING
)
163 void URLRequestContentJob::DidOpen(int result
) {
164 if (result
!= net::OK
) {
165 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED
, result
));
169 if (!byte_range_
.ComputeBounds(meta_info_
.content_size
)) {
170 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED
,
171 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE
));
175 remaining_bytes_
= byte_range_
.last_byte_position() -
176 byte_range_
.first_byte_position() + 1;
177 DCHECK_GE(remaining_bytes_
, 0);
179 if (remaining_bytes_
> 0 && byte_range_
.first_byte_position() != 0) {
180 int rv
= stream_
->Seek(byte_range_
.first_byte_position(),
181 base::Bind(&URLRequestContentJob::DidSeek
,
182 weak_ptr_factory_
.GetWeakPtr()));
183 if (rv
!= net::ERR_IO_PENDING
) {
184 // stream_->Seek() failed, so pass an intentionally erroneous value
189 // We didn't need to call stream_->Seek() at all, so we pass to DidSeek()
190 // the value that would mean seek success. This way we skip the code
191 // handling seek failure.
192 DidSeek(byte_range_
.first_byte_position());
196 void URLRequestContentJob::DidSeek(int64 result
) {
197 if (result
!= byte_range_
.first_byte_position()) {
198 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED
,
199 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE
));
203 set_expected_content_size(remaining_bytes_
);
204 NotifyHeadersComplete();
207 void URLRequestContentJob::DidRead(
208 scoped_refptr
<net::IOBuffer
> buf
, int result
) {
210 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
211 remaining_bytes_
-= result
;
212 DCHECK_GE(remaining_bytes_
, 0);
219 NotifyDone(net::URLRequestStatus());
220 } else if (result
< 0) {
221 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED
, result
));
224 NotifyReadComplete(result
);
227 } // namespace content