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