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 // 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 Loading... |
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 Loading... |
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 |
OLD | NEW |