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 #include "content/browser/download/download_request_core.h" | 5 #include "content/browser/download/download_request_core.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback_helpers.h" | 10 #include "base/callback_helpers.h" |
11 #include "base/format_macros.h" | |
11 #include "base/location.h" | 12 #include "base/location.h" |
12 #include "base/logging.h" | 13 #include "base/logging.h" |
13 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
14 #include "base/metrics/sparse_histogram.h" | 15 #include "base/metrics/sparse_histogram.h" |
15 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
16 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
17 #include "base/thread_task_runner_handle.h" | 18 #include "base/thread_task_runner_handle.h" |
18 #include "content/browser/byte_stream.h" | 19 #include "content/browser/byte_stream.h" |
19 #include "content/browser/download/download_create_info.h" | 20 #include "content/browser/download/download_create_info.h" |
20 #include "content/browser/download/download_interrupt_reasons_impl.h" | 21 #include "content/browser/download/download_interrupt_reasons_impl.h" |
21 #include "content/browser/download/download_manager_impl.h" | 22 #include "content/browser/download/download_manager_impl.h" |
22 #include "content/browser/download/download_request_handle.h" | 23 #include "content/browser/download/download_request_handle.h" |
23 #include "content/browser/download/download_stats.h" | 24 #include "content/browser/download/download_stats.h" |
25 #include "content/browser/loader/resource_dispatcher_host_impl.h" | |
24 #include "content/public/browser/browser_thread.h" | 26 #include "content/public/browser/browser_thread.h" |
25 #include "content/public/browser/download_interrupt_reasons.h" | 27 #include "content/public/browser/download_interrupt_reasons.h" |
26 #include "content/public/browser/download_item.h" | 28 #include "content/public/browser/download_item.h" |
27 #include "content/public/browser/download_manager_delegate.h" | 29 #include "content/public/browser/download_manager_delegate.h" |
28 #include "content/public/browser/navigation_entry.h" | 30 #include "content/public/browser/navigation_entry.h" |
29 #include "content/public/browser/power_save_blocker.h" | 31 #include "content/public/browser/power_save_blocker.h" |
32 #include "content/public/browser/resource_context.h" | |
30 #include "content/public/browser/web_contents.h" | 33 #include "content/public/browser/web_contents.h" |
34 #include "net/base/elements_upload_data_stream.h" | |
31 #include "net/base/io_buffer.h" | 35 #include "net/base/io_buffer.h" |
36 #include "net/base/load_flags.h" | |
32 #include "net/base/net_errors.h" | 37 #include "net/base/net_errors.h" |
38 #include "net/base/upload_bytes_element_reader.h" | |
33 #include "net/http/http_response_headers.h" | 39 #include "net/http/http_response_headers.h" |
34 #include "net/http/http_status_code.h" | 40 #include "net/http/http_status_code.h" |
35 #include "net/url_request/url_request_context.h" | 41 #include "net/url_request/url_request_context.h" |
36 | 42 |
37 namespace content { | 43 namespace content { |
38 | 44 |
45 namespace { | |
46 | |
47 // This is a UserData::Data that will be attached to a URLRequest as a | |
48 // side-channel for passing download parameters. | |
49 class DownloadRequestData : public base::SupportsUserData::Data { | |
50 public: | |
51 ~DownloadRequestData() override {} | |
52 | |
53 static void Attach(net::URLRequest* request, | |
54 DownloadUrlParameters* download_parameters, | |
55 uint32_t download_id); | |
56 static DownloadRequestData* Get(net::URLRequest* request); | |
57 static void Detach(net::URLRequest* request); | |
58 | |
59 scoped_ptr<DownloadSaveInfo> TakeSaveInfo() { return std::move(save_info_); } | |
60 uint32_t download_id() const { return download_id_; } | |
61 const DownloadUrlParameters::OnStartedCallback& callback() const { | |
62 return on_started_callback_; | |
63 } | |
64 | |
65 private: | |
66 static const int kKey; | |
67 | |
68 scoped_ptr<DownloadSaveInfo> save_info_; | |
69 uint32_t download_id_ = DownloadItem::kInvalidId; | |
70 DownloadUrlParameters::OnStartedCallback on_started_callback_; | |
71 }; | |
72 | |
73 // static | |
74 const int DownloadRequestData::kKey = 0; | |
75 | |
76 // static | |
77 void DownloadRequestData::Attach(net::URLRequest* request, | |
78 DownloadUrlParameters* parameters, | |
79 uint32_t download_id) { | |
80 DownloadRequestData* request_data = new DownloadRequestData; | |
81 request_data->save_info_.reset(new DownloadSaveInfo); | |
82 request_data->save_info_->file_path = parameters->file_path(); | |
83 request_data->save_info_->suggested_name = parameters->suggested_name(); | |
84 request_data->save_info_->file = parameters->GetFile(); | |
85 request_data->save_info_->offset = parameters->offset(); | |
86 request_data->save_info_->hash_state = parameters->hash_state(); | |
87 request_data->save_info_->prompt_for_save_location = parameters->prompt(); | |
88 request_data->download_id_ = download_id; | |
89 request_data->on_started_callback_ = parameters->callback(); | |
90 request->SetUserData(&kKey, request_data); | |
91 } | |
92 | |
93 // static | |
94 DownloadRequestData* DownloadRequestData::Get(net::URLRequest* request) { | |
95 return static_cast<DownloadRequestData*>(request->GetUserData(&kKey)); | |
96 } | |
97 | |
98 // static | |
99 void DownloadRequestData::Detach(net::URLRequest* request) { | |
100 request->RemoveUserData(&kKey); | |
101 } | |
102 | |
103 } // namespace | |
104 | |
39 const int DownloadRequestCore::kDownloadByteStreamSize = 100 * 1024; | 105 const int DownloadRequestCore::kDownloadByteStreamSize = 100 * 1024; |
40 | 106 |
41 DownloadRequestCore::DownloadRequestCore( | 107 // static |
42 net::URLRequest* request, | 108 scoped_ptr<net::URLRequest> DownloadRequestCore::CreateRequestOnIOThread( |
43 scoped_ptr<DownloadSaveInfo> save_info, | 109 uint32_t download_id, |
44 const base::Closure& on_ready_to_read_callback) | 110 DownloadUrlParameters* params) { |
45 : on_ready_to_read_callback_(on_ready_to_read_callback), | 111 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
112 DCHECK(download_id == DownloadItem::kInvalidId || | |
113 !params->content_initiated()) | |
114 << "Content initiated downloads shouldn't specify a download ID"; | |
115 | |
116 // ResourceDispatcherHost{Base} is-not-a URLRequest::Delegate, and | |
117 // DownloadUrlParameters can-not include resource_dispatcher_host_impl.h, so | |
118 // we must down cast. RDHI is the only subclass of RDH as of 2012 May 4. | |
119 scoped_ptr<net::URLRequest> request( | |
120 params->resource_context()->GetRequestContext()->CreateRequest( | |
121 params->url(), net::DEFAULT_PRIORITY, nullptr)); | |
122 request->set_method(params->method()); | |
123 | |
124 if (!params->post_body().empty()) { | |
125 const std::string& body = params->post_body(); | |
126 scoped_ptr<net::UploadElementReader> reader( | |
127 net::UploadOwnedBytesElementReader::CreateWithString(body)); | |
128 request->set_upload( | |
129 net::ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); | |
130 } | |
131 | |
132 if (params->post_id() >= 0) { | |
133 // The POST in this case does not have an actual body, and only works | |
134 // when retrieving data from cache. This is done because we don't want | |
135 // to do a re-POST without user consent, and currently don't have a good | |
136 // plan on how to display the UI for that. | |
137 DCHECK(params->prefer_cache()); | |
138 DCHECK_EQ("POST", params->method()); | |
139 std::vector<scoped_ptr<net::UploadElementReader>> element_readers; | |
140 request->set_upload(make_scoped_ptr(new net::ElementsUploadDataStream( | |
141 std::move(element_readers), params->post_id()))); | |
142 } | |
143 | |
144 int load_flags = request->load_flags(); | |
145 if (params->prefer_cache()) { | |
146 // If there is upload data attached, only retrieve from cache because there | |
147 // is no current mechanism to prompt the user for their consent for a | |
148 // re-post. For GETs, try to retrieve data from the cache and skip | |
149 // validating the entry if present. | |
150 if (request->get_upload()) | |
151 load_flags |= net::LOAD_ONLY_FROM_CACHE; | |
152 else | |
153 load_flags |= net::LOAD_PREFERRING_CACHE; | |
154 } else { | |
155 load_flags |= net::LOAD_DISABLE_CACHE; | |
156 } | |
157 request->SetLoadFlags(load_flags); | |
158 | |
159 bool has_last_modified = !params->last_modified().empty(); | |
160 bool has_etag = !params->etag().empty(); | |
161 | |
162 // If we've asked for a range, we want to make sure that we only get that | |
163 // range if our current copy of the information is good. We shouldn't be | |
164 // asked to continue if we don't have a verifier. | |
165 DCHECK(params->offset() == 0 || has_etag || has_last_modified); | |
166 | |
167 // If we're not at the beginning of the file, retrieve only the remaining | |
168 // portion. | |
169 if (params->offset() > 0 && (has_etag || has_last_modified)) { | |
170 request->SetExtraRequestHeaderByName( | |
171 "Range", base::StringPrintf("bytes=%" PRId64 "-", params->offset()), | |
172 true); | |
173 | |
174 // In accordance with RFC 2616 Section 14.27, use If-Range to specify that | |
175 // the server return the entire entity if the validator doesn't match. | |
176 // Last-Modified can be used in the absence of ETag as a validator if the | |
177 // response headers satisfied the HttpUtil::HasStrongValidators() predicate. | |
178 // | |
179 // This function assumes that HasStrongValidators() was true and that the | |
180 // ETag and Last-Modified header values supplied are valid. | |
181 request->SetExtraRequestHeaderByName( | |
182 "If-Range", has_etag ? params->etag() : params->last_modified(), true); | |
183 } | |
184 | |
185 for (const auto& header : params->request_headers()) | |
186 request->SetExtraRequestHeaderByName(header.first, header.second, | |
187 false /*overwrite*/); | |
188 | |
189 DownloadRequestData::Attach(request.get(), std::move(params), download_id); | |
190 return request; | |
191 } | |
192 | |
193 DownloadRequestCore::DownloadRequestCore(net::URLRequest* request, | |
194 Delegate* delegate) | |
195 : delegate_(delegate), | |
46 request_(request), | 196 request_(request), |
47 save_info_(std::move(save_info)), | 197 download_id_(DownloadItem::kInvalidId), |
48 last_buffer_size_(0), | 198 last_buffer_size_(0), |
49 bytes_read_(0), | 199 bytes_read_(0), |
50 pause_count_(0), | 200 pause_count_(0), |
51 was_deferred_(false) { | 201 was_deferred_(false), |
202 started_(false), | |
203 abort_reason_(DOWNLOAD_INTERRUPT_REASON_NONE) { | |
52 DCHECK(request_); | 204 DCHECK(request_); |
53 DCHECK(save_info_); | 205 DCHECK(delegate_); |
54 DCHECK(!on_ready_to_read_callback_.is_null()); | |
55 RecordDownloadCount(UNTHROTTLED_COUNT); | 206 RecordDownloadCount(UNTHROTTLED_COUNT); |
56 power_save_blocker_ = PowerSaveBlocker::Create( | 207 power_save_blocker_ = PowerSaveBlocker::Create( |
57 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, | 208 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, |
58 PowerSaveBlocker::kReasonOther, "Download in progress"); | 209 PowerSaveBlocker::kReasonOther, "Download in progress"); |
210 DownloadRequestData* request_data = DownloadRequestData::Get(request_); | |
211 if (request_data) { | |
212 save_info_ = request_data->TakeSaveInfo(); | |
213 download_id_ = request_data->download_id(); | |
214 on_started_callback_ = request_data->callback(); | |
215 DownloadRequestData::Detach(request_); | |
216 } else { | |
217 save_info_.reset(new DownloadSaveInfo); | |
218 } | |
59 } | 219 } |
60 | 220 |
61 DownloadRequestCore::~DownloadRequestCore() { | 221 DownloadRequestCore::~DownloadRequestCore() { |
62 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 222 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
63 // Remove output stream callback if a stream exists. | 223 // Remove output stream callback if a stream exists. |
64 if (stream_writer_) | 224 if (stream_writer_) |
65 stream_writer_->RegisterCallback(base::Closure()); | 225 stream_writer_->RegisterCallback(base::Closure()); |
66 | 226 |
67 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", | 227 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", |
68 base::TimeTicks::Now() - download_start_time_); | 228 base::TimeTicks::Now() - download_start_time_); |
69 } | 229 } |
70 | 230 |
71 // Send the download creation information to the download thread. | 231 scoped_ptr<DownloadCreateInfo> DownloadRequestCore::CreateDownloadCreateInfo( |
72 void DownloadRequestCore::OnResponseStarted( | 232 DownloadInterruptReason result) { |
73 scoped_ptr<DownloadCreateInfo>* create_info, | 233 DCHECK(!started_); |
74 scoped_ptr<ByteStreamReader>* stream_reader) { | 234 started_ = true; |
235 scoped_ptr<DownloadCreateInfo> create_info(new DownloadCreateInfo( | |
236 base::Time::Now(), request()->net_log(), std::move(save_info_))); | |
237 | |
238 if (result == DOWNLOAD_INTERRUPT_REASON_NONE) | |
239 create_info->remote_address = request()->GetSocketAddress().host(); | |
240 create_info->url_chain = request()->url_chain(); | |
241 create_info->referrer_url = GURL(request()->referrer()); | |
242 create_info->result = result; | |
243 create_info->download_id = download_id_; | |
244 return create_info; | |
245 } | |
246 | |
247 bool DownloadRequestCore::OnResponseStarted() { | |
75 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 248 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
76 DCHECK(save_info_); | |
77 DVLOG(20) << __FUNCTION__ << "()" << DebugString(); | 249 DVLOG(20) << __FUNCTION__ << "()" << DebugString(); |
78 download_start_time_ = base::TimeTicks::Now(); | 250 download_start_time_ = base::TimeTicks::Now(); |
79 | 251 |
252 DownloadInterruptReason result = | |
253 request()->response_headers() | |
254 ? HandleSuccessfulServerResponse(*request()->response_headers(), | |
255 save_info_.get()) | |
256 : DOWNLOAD_INTERRUPT_REASON_NONE; | |
257 | |
258 scoped_ptr<DownloadCreateInfo> create_info = CreateDownloadCreateInfo(result); | |
259 if (result != DOWNLOAD_INTERRUPT_REASON_NONE) { | |
260 delegate_->OnStart(std::move(create_info), scoped_ptr<ByteStreamReader>(), | |
261 base::ResetAndReturn(&on_started_callback_)); | |
262 return false; | |
263 } | |
264 | |
80 // If it's a download, we don't want to poison the cache with it. | 265 // If it's a download, we don't want to poison the cache with it. |
81 request()->StopCaching(); | 266 request()->StopCaching(); |
82 | 267 |
83 // Lower priority as well, so downloads don't contend for resources | 268 // Lower priority as well, so downloads don't contend for resources |
84 // with main frames. | 269 // with main frames. |
85 request()->SetPriority(net::IDLE); | 270 request()->SetPriority(net::IDLE); |
86 | 271 |
87 // If the content-length header is not present (or contains something other | 272 // If the content-length header is not present (or contains something other |
88 // than numbers), the incoming content_length is -1 (unknown size). | 273 // than numbers), the incoming content_length is -1 (unknown size). |
89 // Set the content length to 0 to indicate unknown size to DownloadManager. | 274 // Set the content length to 0 to indicate unknown size to DownloadManager. |
90 int64_t content_length = request()->GetExpectedContentSize() > 0 | 275 int64_t content_length = request()->GetExpectedContentSize() > 0 |
91 ? request()->GetExpectedContentSize() | 276 ? request()->GetExpectedContentSize() |
92 : 0; | 277 : 0; |
93 | 278 create_info->total_bytes = content_length; |
94 // Deleted in DownloadManager. | |
95 scoped_ptr<DownloadCreateInfo> info( | |
96 new DownloadCreateInfo(base::Time::Now(), content_length, | |
97 request()->net_log(), std::move(save_info_))); | |
98 | 279 |
99 // Create the ByteStream for sending data to the download sink. | 280 // Create the ByteStream for sending data to the download sink. |
281 scoped_ptr<ByteStreamReader> stream_reader; | |
100 CreateByteStream( | 282 CreateByteStream( |
101 base::ThreadTaskRunnerHandle::Get(), | 283 base::ThreadTaskRunnerHandle::Get(), |
102 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | 284 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), |
103 kDownloadByteStreamSize, &stream_writer_, stream_reader); | 285 kDownloadByteStreamSize, &stream_writer_, &stream_reader); |
104 stream_writer_->RegisterCallback( | 286 stream_writer_->RegisterCallback( |
105 base::Bind(&DownloadRequestCore::ResumeRequest, AsWeakPtr())); | 287 base::Bind(&DownloadRequestCore::ResumeRequest, AsWeakPtr())); |
106 | 288 |
107 info->url_chain = request()->url_chain(); | 289 request()->GetMimeType(&create_info->mime_type); |
108 info->referrer_url = GURL(request()->referrer()); | |
109 string mime_type; | |
110 request()->GetMimeType(&mime_type); | |
111 info->mime_type = mime_type; | |
112 info->remote_address = request()->GetSocketAddress().host(); | |
113 if (request()->response_headers()) { | |
114 // Grab the first content-disposition header. There may be more than one, | |
115 // though as of this writing, the network stack ensures if there are, they | |
116 // are all duplicates. | |
117 request()->response_headers()->EnumerateHeader( | |
118 nullptr, "Content-Disposition", &info->content_disposition); | |
119 } | |
120 RecordDownloadMimeType(info->mime_type); | |
121 RecordDownloadContentDisposition(info->content_disposition); | |
122 | 290 |
123 // Get the last modified time and etag. | 291 // Get the last modified time and etag. |
124 const net::HttpResponseHeaders* headers = request()->response_headers(); | 292 const net::HttpResponseHeaders* headers = request()->response_headers(); |
125 if (headers) { | 293 if (headers) { |
126 if (headers->HasStrongValidators()) { | 294 if (headers->HasStrongValidators()) { |
127 // If we don't have strong validators as per RFC 2616 section 13.3.3, then | 295 // If we don't have strong validators as per RFC 2616 section 13.3.3, then |
128 // we neither store nor use them for range requests. | 296 // we neither store nor use them for range requests. |
129 if (!headers->EnumerateHeader(nullptr, "Last-Modified", | 297 if (!headers->EnumerateHeader(nullptr, "Last-Modified", |
130 &info->last_modified)) | 298 &create_info->last_modified)) |
131 info->last_modified.clear(); | 299 create_info->last_modified.clear(); |
132 if (!headers->EnumerateHeader(nullptr, "ETag", &info->etag)) | 300 if (!headers->EnumerateHeader(nullptr, "ETag", &create_info->etag)) |
133 info->etag.clear(); | 301 create_info->etag.clear(); |
134 } | 302 } |
135 | 303 |
136 int status = headers->response_code(); | 304 // Grab the first content-disposition header. There may be more than one, |
137 if (2 == status / 100 && status != net::HTTP_PARTIAL_CONTENT) { | 305 // though as of this writing, the network stack ensures if there are, they |
138 // Success & not range response; if we asked for a range, we didn't | 306 // are all duplicates. |
139 // get it--reset the file pointers to reflect that. | 307 headers->EnumerateHeader(nullptr, "Content-Disposition", |
140 info->save_info->offset = 0; | 308 &create_info->content_disposition); |
141 info->save_info->hash_state = ""; | |
142 } | |
143 | 309 |
144 if (!headers->GetMimeType(&info->original_mime_type)) | 310 if (!headers->GetMimeType(&create_info->original_mime_type)) |
145 info->original_mime_type.clear(); | 311 create_info->original_mime_type.clear(); |
146 } | 312 } |
147 | 313 |
148 // Blink verifies that the requester of this download is allowed to set a | 314 // Blink verifies that the requester of this download is allowed to set a |
149 // suggested name for the security origin of the downlaod URL. However, this | 315 // suggested name for the security origin of the download URL. However, this |
150 // assumption doesn't hold if there were cross origin redirects. Therefore, | 316 // assumption doesn't hold if there were cross origin redirects. Therefore, |
151 // clear the suggested_name for such requests. | 317 // clear the suggested_name for such requests. |
152 if (info->url_chain.size() > 1 && | 318 if (create_info->url_chain.size() > 1 && |
153 info->url_chain.front().GetOrigin() != info->url_chain.back().GetOrigin()) | 319 create_info->url_chain.front().GetOrigin() != |
154 info->save_info->suggested_name.clear(); | 320 create_info->url_chain.back().GetOrigin()) |
321 create_info->save_info->suggested_name.clear(); | |
155 | 322 |
156 info.swap(*create_info); | 323 RecordDownloadMimeType(create_info->mime_type); |
324 RecordDownloadContentDisposition(create_info->content_disposition); | |
325 | |
326 delegate_->OnStart(std::move(create_info), std::move(stream_reader), | |
327 base::ResetAndReturn(&on_started_callback_)); | |
328 return true; | |
157 } | 329 } |
158 | 330 |
159 // Create a new buffer, which will be handed to the download thread for file | 331 // Create a new buffer, which will be handed to the download thread for file |
160 // writing and deletion. | 332 // writing and deletion. |
161 bool DownloadRequestCore::OnWillRead(scoped_refptr<net::IOBuffer>* buf, | 333 bool DownloadRequestCore::OnWillRead(scoped_refptr<net::IOBuffer>* buf, |
162 int* buf_size, | 334 int* buf_size, |
163 int min_size) { | 335 int min_size) { |
164 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 336 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
165 DCHECK(buf && buf_size); | 337 DCHECK(buf && buf_size); |
166 DCHECK(!read_buffer_.get()); | 338 DCHECK(!read_buffer_.get()); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
205 } | 377 } |
206 | 378 |
207 read_buffer_ = NULL; // Drop our reference. | 379 read_buffer_ = NULL; // Drop our reference. |
208 | 380 |
209 if (pause_count_ > 0) | 381 if (pause_count_ > 0) |
210 *defer = was_deferred_ = true; | 382 *defer = was_deferred_ = true; |
211 | 383 |
212 return true; | 384 return true; |
213 } | 385 } |
214 | 386 |
215 DownloadInterruptReason DownloadRequestCore::OnResponseCompleted( | 387 void DownloadRequestCore::OnWillAbort(DownloadInterruptReason reason) { |
388 DVLOG(20) << __FUNCTION__ << "() reason=" << reason << " " << DebugString(); | |
389 DCHECK(!started_); | |
390 abort_reason_ = reason; | |
391 } | |
392 | |
393 void DownloadRequestCore::OnResponseCompleted( | |
216 const net::URLRequestStatus& status) { | 394 const net::URLRequestStatus& status) { |
217 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 395 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
218 int response_code = status.is_success() ? request()->GetResponseCode() : 0; | 396 int response_code = status.is_success() ? request()->GetResponseCode() : 0; |
219 DVLOG(20) << __FUNCTION__ << "()" << DebugString() | 397 DVLOG(20) << __FUNCTION__ << "()" << DebugString() |
220 << " status.status() = " << status.status() | 398 << " status.status() = " << status.status() |
221 << " status.error() = " << status.error() | 399 << " status.error() = " << status.error() |
222 << " response_code = " << response_code; | 400 << " response_code = " << response_code; |
223 | 401 |
224 net::Error error_code = net::OK; | 402 DownloadInterruptReason reason = HandleRequestStatus(status); |
225 if (status.status() == net::URLRequestStatus::FAILED || | |
226 // Note cancels as failures too. | |
227 status.status() == net::URLRequestStatus::CANCELED) { | |
228 error_code = static_cast<net::Error>(status.error()); // Normal case. | |
229 // Make sure that at least the fact of failure comes through. | |
230 if (error_code == net::OK) | |
231 error_code = net::ERR_FAILED; | |
232 } | |
233 | 403 |
234 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are | 404 if (status.status() == net::URLRequestStatus::CANCELED) { |
235 // allowed since a number of servers in the wild close the connection too | 405 if (abort_reason_ != DOWNLOAD_INTERRUPT_REASON_NONE) { |
236 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - | 406 // If a more specific interrupt reason was specified before the request |
237 // treat downloads as complete in both cases, so we follow their lead. | 407 // was explicitly cancelled, then use it. |
238 if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || | 408 reason = abort_reason_; |
239 error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { | 409 } else if (status.error() == net::ERR_ABORTED) { |
240 error_code = net::OK; | 410 // CANCELED + ERR_ABORTED == something outside of the network |
241 } | 411 // stack cancelled the request. There aren't that many things that |
242 DownloadInterruptReason reason = ConvertNetErrorToInterruptReason( | 412 // could do this to a download request (whose lifetime is separated from |
243 error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK); | 413 // the tab from which it came). We map this to USER_CANCELLED as the |
244 | 414 // case we know about (system suspend because of laptop close) corresponds |
245 if (status.status() == net::URLRequestStatus::CANCELED && | 415 // to a user action. |
246 status.error() == net::ERR_ABORTED) { | 416 // TODO(asanka): A lid close or other power event should result in an |
247 // CANCELED + ERR_ABORTED == something outside of the network | 417 // interruption that doesn't discard the partial state, unlike |
248 // stack cancelled the request. There aren't that many things that | 418 // USER_CANCELLED. (https://crbug.com/166179) |
249 // could do this to a download request (whose lifetime is separated from | 419 if (net::IsCertStatusError(request()->ssl_info().cert_status)) |
250 // the tab from which it came). We map this to USER_CANCELLED as the | 420 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM; |
251 // case we know about (system suspend because of laptop close) corresponds | 421 else |
252 // to a user action. | 422 reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; |
253 // TODO(ahendrickson) -- Find a better set of codes to use here, as | |
254 // CANCELED/ERR_ABORTED can occur for reasons other than user cancel. | |
255 if (net::IsCertStatusError(request()->ssl_info().cert_status)) | |
256 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM; | |
257 else | |
258 reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; | |
259 } | |
260 | |
261 if (status.is_success() && reason == DOWNLOAD_INTERRUPT_REASON_NONE && | |
262 request()->response_headers()) { | |
263 // Handle server's response codes. | |
264 switch (response_code) { | |
265 case -1: // Non-HTTP request. | |
266 case net::HTTP_OK: | |
267 case net::HTTP_CREATED: | |
268 case net::HTTP_ACCEPTED: | |
269 case net::HTTP_NON_AUTHORITATIVE_INFORMATION: | |
270 case net::HTTP_RESET_CONTENT: | |
271 case net::HTTP_PARTIAL_CONTENT: | |
272 // Expected successful codes. | |
273 break; | |
274 case net::HTTP_NO_CONTENT: | |
275 case net::HTTP_NOT_FOUND: | |
276 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; | |
277 break; | |
278 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: | |
279 // Retry by downloading from the start automatically: | |
280 // If we haven't received data when we get this error, we won't. | |
281 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; | |
282 break; | |
283 case net::HTTP_UNAUTHORIZED: | |
284 // Server didn't authorize this request. | |
285 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED; | |
286 break; | |
287 case net::HTTP_FORBIDDEN: | |
288 // Server forbids access to this resource. | |
289 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN; | |
290 break; | |
291 default: // All other errors. | |
292 // Redirection and informational codes should have been handled earlier | |
293 // in the stack. | |
294 DCHECK_NE(3, response_code / 100); | |
295 DCHECK_NE(1, response_code / 100); | |
296 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; | |
297 break; | |
298 } | 423 } |
299 } | 424 } |
300 | 425 |
301 std::string accept_ranges; | 426 std::string accept_ranges; |
302 bool has_strong_validators = false; | 427 bool has_strong_validators = false; |
303 if (request()->response_headers()) { | 428 if (request()->response_headers()) { |
304 request()->response_headers()->EnumerateHeader(nullptr, "Accept-Ranges", | 429 request()->response_headers()->EnumerateHeader(nullptr, "Accept-Ranges", |
305 &accept_ranges); | 430 &accept_ranges); |
306 has_strong_validators = | 431 has_strong_validators = |
307 request()->response_headers()->HasStrongValidators(); | 432 request()->response_headers()->HasStrongValidators(); |
(...skipping 10 matching lines...) Expand all Loading... | |
318 // If the error mapped to something unknown, record it so that | 443 // If the error mapped to something unknown, record it so that |
319 // we can drill down. | 444 // we can drill down. |
320 if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) { | 445 if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) { |
321 UMA_HISTOGRAM_SPARSE_SLOWLY("Download.MapErrorNetworkFailed", | 446 UMA_HISTOGRAM_SPARSE_SLOWLY("Download.MapErrorNetworkFailed", |
322 std::abs(status.error())); | 447 std::abs(status.error())); |
323 } | 448 } |
324 | 449 |
325 stream_writer_.reset(); // We no longer need the stream. | 450 stream_writer_.reset(); // We no longer need the stream. |
326 read_buffer_ = nullptr; | 451 read_buffer_ = nullptr; |
327 | 452 |
328 return reason; | 453 if (started_) |
Randy Smith (Not in Mondays)
2016/02/11 22:08:09
suggestion: DCHECK_EQ(reason, DOWNLOAD_INTERRUPT_R
asanka
2016/02/11 23:57:25
started_ only implies that the download started su
Randy Smith (Not in Mondays)
2016/02/12 18:01:19
Acknowledged.
| |
454 return; | |
455 | |
456 // OnResponseCompleted() called without OnResponseStarted(). This should only | |
457 // happen when the request was aborted. | |
458 DCHECK_NE(reason, DOWNLOAD_INTERRUPT_REASON_NONE); | |
459 scoped_ptr<DownloadCreateInfo> create_info = CreateDownloadCreateInfo(reason); | |
460 scoped_ptr<ByteStreamReader> empty_byte_stream; | |
461 delegate_->OnStart(std::move(create_info), std::move(empty_byte_stream), | |
462 base::ResetAndReturn(&on_started_callback_)); | |
329 } | 463 } |
330 | 464 |
331 void DownloadRequestCore::PauseRequest() { | 465 void DownloadRequestCore::PauseRequest() { |
332 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 466 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
333 | 467 |
334 ++pause_count_; | 468 ++pause_count_; |
335 } | 469 } |
336 | 470 |
337 void DownloadRequestCore::ResumeRequest() { | 471 void DownloadRequestCore::ResumeRequest() { |
338 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 472 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
339 DCHECK_LT(0, pause_count_); | 473 DCHECK_LT(0, pause_count_); |
340 | 474 |
341 --pause_count_; | 475 --pause_count_; |
342 | 476 |
343 if (!was_deferred_) | 477 if (!was_deferred_) |
344 return; | 478 return; |
345 if (pause_count_ > 0) | 479 if (pause_count_ > 0) |
346 return; | 480 return; |
347 | 481 |
348 was_deferred_ = false; | 482 was_deferred_ = false; |
349 if (!last_stream_pause_time_.is_null()) { | 483 if (!last_stream_pause_time_.is_null()) { |
350 total_pause_time_ += (base::TimeTicks::Now() - last_stream_pause_time_); | 484 total_pause_time_ += (base::TimeTicks::Now() - last_stream_pause_time_); |
351 last_stream_pause_time_ = base::TimeTicks(); | 485 last_stream_pause_time_ = base::TimeTicks(); |
352 } | 486 } |
353 | 487 |
354 on_ready_to_read_callback_.Run(); | 488 delegate_->OnReadyToRead(); |
355 } | 489 } |
356 | 490 |
357 std::string DownloadRequestCore::DebugString() const { | 491 std::string DownloadRequestCore::DebugString() const { |
358 return base::StringPrintf( | 492 return base::StringPrintf( |
359 "{" | 493 "{" |
494 " this=%p " | |
360 " url_ = " | 495 " url_ = " |
361 "\"%s\"" | 496 "\"%s\"" |
362 " }", | 497 " }", |
498 reinterpret_cast<const void*>(this), | |
363 request() ? request()->url().spec().c_str() : "<NULL request>"); | 499 request() ? request()->url().spec().c_str() : "<NULL request>"); |
364 } | 500 } |
365 | 501 |
502 // static | |
503 DownloadInterruptReason DownloadRequestCore::HandleRequestStatus( | |
504 const net::URLRequestStatus& status) { | |
505 net::Error error_code = net::OK; | |
506 if (status.status() == net::URLRequestStatus::FAILED || | |
507 // Note cancels as failures too. | |
508 status.status() == net::URLRequestStatus::CANCELED) { | |
509 error_code = static_cast<net::Error>(status.error()); // Normal case. | |
510 // Make sure that at least the fact of failure comes through. | |
511 if (error_code == net::OK) | |
512 error_code = net::ERR_FAILED; | |
513 } | |
514 | |
515 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are | |
516 // allowed since a number of servers in the wild close the connection too | |
517 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - | |
518 // treat downloads as complete in both cases, so we follow their lead. | |
519 if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || | |
520 error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { | |
521 error_code = net::OK; | |
522 } | |
523 DownloadInterruptReason reason = ConvertNetErrorToInterruptReason( | |
524 error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK); | |
525 | |
526 return reason; | |
527 } | |
528 | |
529 // static | |
530 DownloadInterruptReason DownloadRequestCore::HandleSuccessfulServerResponse( | |
531 const net::HttpResponseHeaders& http_headers, | |
532 DownloadSaveInfo* save_info) { | |
533 switch (http_headers.response_code()) { | |
534 case -1: // Non-HTTP request. | |
535 case net::HTTP_OK: | |
536 case net::HTTP_NON_AUTHORITATIVE_INFORMATION: | |
537 case net::HTTP_PARTIAL_CONTENT: | |
538 // Expected successful codes. | |
539 break; | |
540 | |
541 case net::HTTP_CREATED: | |
542 case net::HTTP_ACCEPTED: | |
543 // Per RFC 2616 the entity being transferred is metadata about the | |
544 // resource at the target URL and not the resource at that URL (or the | |
545 // resource that would be at the URL once processing is completed in the | |
546 // case of HTTP_ACCEPTED). However, we currently don't have special | |
547 // handling for these response and they are downloaded the same as a | |
548 // regular response. | |
549 break; | |
550 | |
551 case net::HTTP_NO_CONTENT: | |
552 case net::HTTP_RESET_CONTENT: | |
553 // These two status codes don't have an entity (or rather RFC 2616 | |
554 // requires that there be no entity). They are treated the same as the | |
555 // resource not being found since there is no entity to download. | |
556 | |
557 case net::HTTP_NOT_FOUND: | |
558 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; | |
559 break; | |
560 | |
561 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: | |
562 // Retry by downloading from the start automatically: | |
563 // If we haven't received data when we get this error, we won't. | |
564 return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; | |
565 break; | |
566 case net::HTTP_UNAUTHORIZED: | |
567 case net::HTTP_PROXY_AUTHENTICATION_REQUIRED: | |
568 // Server didn't authorize this request. | |
569 return DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED; | |
570 break; | |
571 case net::HTTP_FORBIDDEN: | |
572 // Server forbids access to this resource. | |
573 return DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN; | |
574 break; | |
575 default: // All other errors. | |
576 // Redirection and informational codes should have been handled earlier | |
577 // in the stack. | |
578 DCHECK_NE(3, http_headers.response_code() / 100); | |
579 DCHECK_NE(1, http_headers.response_code() / 100); | |
580 return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; | |
581 } | |
582 | |
583 if (save_info && save_info->offset > 0) { | |
584 // The caller is expecting a partial response. | |
585 | |
586 if (http_headers.response_code() != net::HTTP_PARTIAL_CONTENT) { | |
587 // Requested a partial range, but received the entire response. | |
588 save_info->offset = 0; | |
589 save_info->hash_state.clear(); | |
590 return DOWNLOAD_INTERRUPT_REASON_NONE; | |
591 } | |
592 | |
593 int64_t first_byte = -1; | |
594 int64_t last_byte = -1; | |
595 int64_t length = -1; | |
596 if (!http_headers.GetContentRange(&first_byte, &last_byte, &length)) | |
597 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; | |
598 DCHECK_GE(first_byte, 0); | |
599 | |
600 if (first_byte != save_info->offset) { | |
601 // The server returned a different range than the one we requested. Assume | |
602 // the response is bad. | |
603 // | |
604 // In the future we should consider allowing offsets that are less than | |
605 // the offset we've requested, since in theory we can truncate the partial | |
606 // file at the offset and continue. | |
607 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; | |
608 } | |
609 | |
610 return DOWNLOAD_INTERRUPT_REASON_NONE; | |
611 } | |
612 | |
613 if (http_headers.response_code() == net::HTTP_PARTIAL_CONTENT) | |
614 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; | |
615 | |
616 return DOWNLOAD_INTERRUPT_REASON_NONE; | |
617 } | |
618 | |
366 } // namespace content | 619 } // namespace content |
OLD | NEW |