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

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

Powered by Google App Engine
This is Rietveld 408576698