Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(381)

Side by Side Diff: content/browser/download/download_request_core.cc

Issue 148133007: [Downloads] Always call DM::StartDownload() for explicit downloads. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Ketchup with upstream. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698