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