| 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_resource_handler.h" | 5 #include "content/browser/download/download_resource_handler.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/message_loop_proxy.h" |
| 11 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
| 12 #include "base/metrics/stats_counters.h" | 13 #include "base/metrics/stats_counters.h" |
| 13 #include "base/stringprintf.h" | 14 #include "base/stringprintf.h" |
| 14 #include "content/browser/download/download_buffer.h" | |
| 15 #include "content/browser/download/download_create_info.h" | 15 #include "content/browser/download/download_create_info.h" |
| 16 #include "content/browser/download/download_file_manager.h" | 16 #include "content/browser/download/download_file_manager.h" |
| 17 #include "content/browser/download/download_interrupt_reasons_impl.h" | 17 #include "content/browser/download/download_interrupt_reasons_impl.h" |
| 18 #include "content/browser/download/download_manager_impl.h" | 18 #include "content/browser/download/download_manager_impl.h" |
| 19 #include "content/browser/download/download_request_handle.h" | 19 #include "content/browser/download/download_request_handle.h" |
| 20 #include "content/browser/download/byte_stream.h" |
| 20 #include "content/browser/download/download_stats.h" | 21 #include "content/browser/download/download_stats.h" |
| 21 #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" | 22 #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" |
| 22 #include "content/browser/renderer_host/resource_request_info_impl.h" | 23 #include "content/browser/renderer_host/resource_request_info_impl.h" |
| 23 #include "content/public/browser/browser_thread.h" | 24 #include "content/public/browser/browser_thread.h" |
| 24 #include "content/public/browser/download_interrupt_reasons.h" | 25 #include "content/public/browser/download_interrupt_reasons.h" |
| 25 #include "content/public/browser/download_item.h" | 26 #include "content/public/browser/download_item.h" |
| 26 #include "content/public/browser/download_manager_delegate.h" | 27 #include "content/public/browser/download_manager_delegate.h" |
| 27 #include "content/public/common/resource_response.h" | 28 #include "content/public/common/resource_response.h" |
| 28 #include "net/base/io_buffer.h" | 29 #include "net/base/io_buffer.h" |
| 29 #include "net/base/net_errors.h" | 30 #include "net/base/net_errors.h" |
| 30 #include "net/http/http_response_headers.h" | 31 #include "net/http/http_response_headers.h" |
| 31 #include "net/url_request/url_request_context.h" | 32 #include "net/url_request/url_request_context.h" |
| 32 | 33 |
| 33 using content::BrowserThread; | 34 using content::BrowserThread; |
| 34 using content::DownloadId; | 35 using content::DownloadId; |
| 35 using content::DownloadItem; | 36 using content::DownloadItem; |
| 36 using content::DownloadManager; | 37 using content::DownloadManager; |
| 37 using content::ResourceDispatcherHostImpl; | 38 using content::ResourceDispatcherHostImpl; |
| 38 using content::ResourceRequestInfoImpl; | 39 using content::ResourceRequestInfoImpl; |
| 39 | 40 |
| 40 namespace { | 41 namespace { |
| 41 | 42 |
| 43 static const int kDownloadByteStreamSize = 100 * 1024; |
| 44 |
| 42 void CallStartedCBOnUIThread( | 45 void CallStartedCBOnUIThread( |
| 43 const DownloadResourceHandler::OnStartedCallback& started_cb, | 46 const DownloadResourceHandler::OnStartedCallback& started_cb, |
| 44 DownloadId id, | 47 DownloadId id, |
| 45 net::Error error) { | 48 net::Error error) { |
| 46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 47 if (started_cb.is_null()) | 50 if (started_cb.is_null()) |
| 48 return; | 51 return; |
| 49 started_cb.Run(id, error); | 52 started_cb.Run(id, error); |
| 50 } | 53 } |
| 51 | 54 |
| 55 // Static function in order to prevent any accidental accesses to |
| 56 // DownloadResourceHandler members from the UI thread. |
| 57 static void StartOnUIThread( |
| 58 scoped_ptr<DownloadCreateInfo> info, |
| 59 scoped_ptr<content::ByteStreamReader> stream, |
| 60 scoped_refptr<DownloadFileManager> download_file_manager, |
| 61 const DownloadRequestHandle& handle, |
| 62 const DownloadResourceHandler::OnStartedCallback& started_cb) { |
| 63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 64 |
| 65 DownloadManager* download_manager = handle.GetDownloadManager(); |
| 66 if (!download_manager) { |
| 67 // NULL in unittests or if the page closed right after starting the |
| 68 // download. |
| 69 if (!started_cb.is_null()) |
| 70 started_cb.Run(DownloadId(), net::ERR_ACCESS_DENIED); |
| 71 return; |
| 72 } |
| 73 DownloadId download_id = download_manager->delegate()->GetNextId(); |
| 74 info->download_id = download_id; |
| 75 |
| 76 download_file_manager->StartDownload(info.Pass(), stream.Pass(), handle); |
| 77 |
| 78 if (!started_cb.is_null()) |
| 79 started_cb.Run(download_id, net::OK); |
| 80 } |
| 81 |
| 52 } // namespace | 82 } // namespace |
| 53 | 83 |
| 54 DownloadResourceHandler::DownloadResourceHandler( | 84 DownloadResourceHandler::DownloadResourceHandler( |
| 55 int render_process_host_id, | 85 int render_process_host_id, |
| 56 int render_view_id, | 86 int render_view_id, |
| 57 int request_id, | 87 int request_id, |
| 58 const GURL& url, | 88 const GURL& url, |
| 59 DownloadFileManager* download_file_manager, | 89 scoped_refptr<DownloadFileManager> download_file_manager, |
| 60 net::URLRequest* request, | 90 net::URLRequest* request, |
| 61 const DownloadResourceHandler::OnStartedCallback& started_cb, | 91 const DownloadResourceHandler::OnStartedCallback& started_cb, |
| 62 const content::DownloadSaveInfo& save_info) | 92 const content::DownloadSaveInfo& save_info) |
| 63 : download_id_(DownloadId::Invalid()), | 93 : global_id_(render_process_host_id, request_id), |
| 64 global_id_(render_process_host_id, request_id), | |
| 65 render_view_id_(render_view_id), | 94 render_view_id_(render_view_id), |
| 66 content_length_(0), | 95 content_length_(0), |
| 67 download_file_manager_(download_file_manager), | 96 download_file_manager_(download_file_manager), |
| 68 request_(request), | 97 request_(request), |
| 69 started_cb_(started_cb), | 98 started_cb_(started_cb), |
| 70 save_info_(save_info), | 99 save_info_(save_info), |
| 71 buffer_(new content::DownloadBuffer), | |
| 72 last_buffer_size_(0), | 100 last_buffer_size_(0), |
| 73 bytes_read_(0), | 101 bytes_read_(0), |
| 74 pause_count_(0), | 102 pause_count_(0), |
| 75 was_deferred_(false), | 103 was_deferred_(false), |
| 76 on_response_started_called_(false) { | 104 on_response_started_called_(false) { |
| 77 download_stats::RecordDownloadCount(download_stats::UNTHROTTLED_COUNT); | 105 download_stats::RecordDownloadCount(download_stats::UNTHROTTLED_COUNT); |
| 78 } | 106 } |
| 79 | 107 |
| 80 bool DownloadResourceHandler::OnUploadProgress(int request_id, | 108 bool DownloadResourceHandler::OnUploadProgress(int request_id, |
| 81 uint64 position, | 109 uint64 position, |
| 82 uint64 size) { | 110 uint64 size) { |
| 83 return true; | 111 return true; |
| 84 } | 112 } |
| 85 | 113 |
| 86 // Not needed, as this event handler ought to be the final resource. | 114 // Not needed, as this event handler ought to be the final resource. |
| 87 bool DownloadResourceHandler::OnRequestRedirected( | 115 bool DownloadResourceHandler::OnRequestRedirected( |
| 88 int request_id, | 116 int request_id, |
| 89 const GURL& url, | 117 const GURL& url, |
| 90 content::ResourceResponse* response, | 118 content::ResourceResponse* response, |
| 91 bool* defer) { | 119 bool* defer) { |
| 92 return true; | 120 return true; |
| 93 } | 121 } |
| 94 | 122 |
| 95 // Send the download creation information to the download thread. | 123 // Send the download creation information to the download thread. |
| 96 bool DownloadResourceHandler::OnResponseStarted( | 124 bool DownloadResourceHandler::OnResponseStarted( |
| 97 int request_id, | 125 int request_id, |
| 98 content::ResourceResponse* response, | 126 content::ResourceResponse* response, |
| 99 bool* defer) { | 127 bool* defer) { |
| 128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 100 // There can be only one (call) | 129 // There can be only one (call) |
| 101 DCHECK(!on_response_started_called_); | 130 DCHECK(!on_response_started_called_); |
| 102 on_response_started_called_ = true; | 131 on_response_started_called_ = true; |
| 103 | 132 |
| 104 VLOG(20) << __FUNCTION__ << "()" << DebugString() | 133 VLOG(20) << __FUNCTION__ << "()" << DebugString() |
| 105 << " request_id = " << request_id; | 134 << " request_id = " << request_id; |
| 106 download_start_time_ = base::TimeTicks::Now(); | 135 download_start_time_ = base::TimeTicks::Now(); |
| 107 | 136 |
| 108 // If it's a download, we don't want to poison the cache with it. | 137 // If it's a download, we don't want to poison the cache with it. |
| 109 request_->StopCaching(); | 138 request_->StopCaching(); |
| 110 | 139 |
| 111 std::string content_disposition; | 140 std::string content_disposition; |
| 112 request_->GetResponseHeaderByName("content-disposition", | 141 request_->GetResponseHeaderByName("content-disposition", |
| 113 &content_disposition); | 142 &content_disposition); |
| 114 SetContentDisposition(content_disposition); | 143 SetContentDisposition(content_disposition); |
| 115 SetContentLength(response->content_length); | 144 SetContentLength(response->content_length); |
| 116 | 145 |
| 117 const ResourceRequestInfoImpl* request_info = | 146 const ResourceRequestInfoImpl* request_info = |
| 118 ResourceRequestInfoImpl::ForRequest(request_); | 147 ResourceRequestInfoImpl::ForRequest(request_); |
| 119 | 148 |
| 120 // Deleted in DownloadManager. | 149 // Deleted in DownloadManager. |
| 121 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo( | 150 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo( |
| 122 base::Time::Now(), 0, content_length_, DownloadItem::IN_PROGRESS, | 151 base::Time::Now(), 0, content_length_, DownloadItem::IN_PROGRESS, |
| 123 request_->net_log(), request_info->has_user_gesture(), | 152 request_->net_log(), request_info->has_user_gesture(), |
| 124 request_info->transition_type())); | 153 request_info->transition_type())); |
| 154 |
| 155 // Create the ByteStream for sending data to the download sink. |
| 156 scoped_ptr<content::ByteStreamReader> stream_reader; |
| 157 CreateByteStream( |
| 158 base::MessageLoopProxy::current(), |
| 159 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), |
| 160 kDownloadByteStreamSize, &stream_writer_, &stream_reader); |
| 161 stream_writer_->RegisterCallback( |
| 162 // We're refcounted, so this is a safe callback to pass around. |
| 163 base::Bind(&DownloadResourceHandler::ResumeRequest, this)); |
| 164 |
| 125 info->url_chain = request_->url_chain(); | 165 info->url_chain = request_->url_chain(); |
| 126 info->referrer_url = GURL(request_->referrer()); | 166 info->referrer_url = GURL(request_->referrer()); |
| 127 info->start_time = base::Time::Now(); | 167 info->start_time = base::Time::Now(); |
| 128 info->received_bytes = save_info_.offset; | 168 info->received_bytes = save_info_.offset; |
| 129 info->total_bytes = content_length_; | 169 info->total_bytes = content_length_; |
| 130 info->state = DownloadItem::IN_PROGRESS; | 170 info->state = DownloadItem::IN_PROGRESS; |
| 131 info->has_user_gesture = request_info->has_user_gesture(); | 171 info->has_user_gesture = request_info->has_user_gesture(); |
| 132 info->content_disposition = content_disposition_; | 172 info->content_disposition = content_disposition_; |
| 133 info->mime_type = response->mime_type; | 173 info->mime_type = response->mime_type; |
| 134 info->remote_address = request_->GetSocketAddress().host(); | 174 info->remote_address = request_->GetSocketAddress().host(); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 161 accept_ranges_ = ""; | 201 accept_ranges_ = ""; |
| 162 } | 202 } |
| 163 | 203 |
| 164 info->prompt_user_for_save_location = | 204 info->prompt_user_for_save_location = |
| 165 save_info_.prompt_for_save_location && save_info_.file_path.empty(); | 205 save_info_.prompt_for_save_location && save_info_.file_path.empty(); |
| 166 info->referrer_charset = request_->context()->referrer_charset(); | 206 info->referrer_charset = request_->context()->referrer_charset(); |
| 167 info->save_info = save_info_; | 207 info->save_info = save_info_; |
| 168 | 208 |
| 169 BrowserThread::PostTask( | 209 BrowserThread::PostTask( |
| 170 BrowserThread::UI, FROM_HERE, | 210 BrowserThread::UI, FROM_HERE, |
| 171 base::Bind(&DownloadResourceHandler::StartOnUIThread, this, | 211 base::Bind(&StartOnUIThread, |
| 172 base::Passed(&info), request_handle)); | 212 base::Passed(info.Pass()), |
| 213 base::Passed(stream_reader.Pass()), |
| 214 download_file_manager_, |
| 215 request_handle, |
| 216 // Pass to StartOnUIThread so that variable |
| 217 // access is always on IO thread but function |
| 218 // is called on UI thread. |
| 219 started_cb_)); |
| 220 // Guaranteed to be called in StartOnUIThread |
| 221 started_cb_.Reset(); |
| 173 | 222 |
| 174 return true; | 223 return true; |
| 175 } | 224 } |
| 176 | 225 |
| 177 void DownloadResourceHandler::CallStartedCB(DownloadId id, net::Error error) { | 226 void DownloadResourceHandler::CallStartedCB(DownloadId id, net::Error error) { |
| 227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 178 if (started_cb_.is_null()) | 228 if (started_cb_.is_null()) |
| 179 return; | 229 return; |
| 180 BrowserThread::PostTask( | 230 BrowserThread::PostTask( |
| 181 BrowserThread::UI, FROM_HERE, | 231 BrowserThread::UI, FROM_HERE, |
| 182 base::Bind(&CallStartedCBOnUIThread, started_cb_, id, error)); | 232 base::Bind(&CallStartedCBOnUIThread, started_cb_, id, error)); |
| 183 started_cb_.Reset(); | 233 started_cb_.Reset(); |
| 184 } | 234 } |
| 185 | 235 |
| 186 bool DownloadResourceHandler::OnWillStart(int request_id, | 236 bool DownloadResourceHandler::OnWillStart(int request_id, |
| 187 const GURL& url, | 237 const GURL& url, |
| 188 bool* defer) { | 238 bool* defer) { |
| 189 return true; | 239 return true; |
| 190 } | 240 } |
| 191 | 241 |
| 192 // Create a new buffer, which will be handed to the download thread for file | 242 // Create a new buffer, which will be handed to the download thread for file |
| 193 // writing and deletion. | 243 // writing and deletion. |
| 194 bool DownloadResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, | 244 bool DownloadResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, |
| 195 int* buf_size, int min_size) { | 245 int* buf_size, int min_size) { |
| 246 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 196 DCHECK(buf && buf_size); | 247 DCHECK(buf && buf_size); |
| 197 if (!read_buffer_) { | 248 if (!read_buffer_) { |
| 198 *buf_size = min_size < 0 ? kReadBufSize : min_size; | 249 *buf_size = min_size < 0 ? kReadBufSize : min_size; |
| 199 last_buffer_size_ = *buf_size; | 250 last_buffer_size_ = *buf_size; |
| 200 read_buffer_ = new net::IOBuffer(*buf_size); | 251 read_buffer_ = new net::IOBuffer(*buf_size); |
| 201 } | 252 } |
| 202 *buf = read_buffer_.get(); | 253 *buf = read_buffer_.get(); |
| 203 return true; | 254 return true; |
| 204 } | 255 } |
| 205 | 256 |
| 206 // Pass the buffer to the download file writer. | 257 // Pass the buffer to the download file writer. |
| 207 bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read, | 258 bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read, |
| 208 bool* defer) { | 259 bool* defer) { |
| 260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 209 if (!read_buffer_) { | 261 if (!read_buffer_) { |
| 210 // Ignore spurious OnReadCompleted! Deferring from OnReadCompleted tells | 262 // Ignore spurious OnReadCompleted! Deferring from OnReadCompleted tells |
| 211 // the ResourceDispatcherHost that we did not consume the data. | 263 // the ResourceDispatcherHost that we did not consume the data. |
| 212 // ResumeDeferredRequest then repeats the last OnReadCompleted call. | 264 // ResumeDeferredRequest then repeats the last OnReadCompleted call. |
| 213 // TODO(darin): Fix the ResourceDispatcherHost to avoid this hack! | 265 // TODO(darin): Fix the ResourceDispatcherHost to avoid this hack! |
| 214 return true; | 266 return true; |
| 215 } | 267 } |
| 216 | 268 |
| 217 if (pause_count_ > 0) { | 269 if (pause_count_ > 0) { |
| 218 *defer = was_deferred_ = true; | 270 *defer = was_deferred_ = true; |
| 219 return true; | 271 return true; |
| 220 } | 272 } |
| 221 | 273 |
| 222 if (download_id_ == DownloadId::Invalid()) { | |
| 223 // We can't start saving the data before we create the file on disk and | |
| 224 // have a download id. The request will be un-paused in | |
| 225 // DownloadFileManager::CreateDownloadFile. | |
| 226 *defer = was_deferred_ = true; | |
| 227 return true; | |
| 228 } | |
| 229 | |
| 230 base::TimeTicks now(base::TimeTicks::Now()); | 274 base::TimeTicks now(base::TimeTicks::Now()); |
| 231 if (!last_read_time_.is_null()) { | 275 if (!last_read_time_.is_null()) { |
| 232 double seconds_since_last_read = (now - last_read_time_).InSecondsF(); | 276 double seconds_since_last_read = (now - last_read_time_).InSecondsF(); |
| 233 if (now == last_read_time_) | 277 if (now == last_read_time_) |
| 234 // Use 1/10 ms as a "very small number" so that we avoid | 278 // Use 1/10 ms as a "very small number" so that we avoid |
| 235 // divide-by-zero error and still record a very high potential bandwidth. | 279 // divide-by-zero error and still record a very high potential bandwidth. |
| 236 seconds_since_last_read = 0.00001; | 280 seconds_since_last_read = 0.00001; |
| 237 | 281 |
| 238 double actual_bandwidth = (*bytes_read)/seconds_since_last_read; | 282 double actual_bandwidth = (*bytes_read)/seconds_since_last_read; |
| 239 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; | 283 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; |
| 240 download_stats::RecordBandwidth(actual_bandwidth, potential_bandwidth); | 284 download_stats::RecordBandwidth(actual_bandwidth, potential_bandwidth); |
| 241 } | 285 } |
| 242 last_read_time_ = now; | 286 last_read_time_ = now; |
| 243 | 287 |
| 244 if (!*bytes_read) | 288 if (!*bytes_read) |
| 245 return true; | 289 return true; |
| 246 bytes_read_ += *bytes_read; | 290 bytes_read_ += *bytes_read; |
| 247 DCHECK(read_buffer_); | 291 DCHECK(read_buffer_); |
| 248 // Swap the data. | |
| 249 net::IOBuffer* io_buffer = NULL; | |
| 250 read_buffer_.swap(&io_buffer); | |
| 251 size_t vector_size = buffer_->AddData(io_buffer, *bytes_read); | |
| 252 bool need_update = (vector_size == 1); // Buffer was empty. | |
| 253 | 292 |
| 254 // We are passing ownership of this buffer to the download file manager. | 293 // Take the data ship it down the stream. If the stream is full, pause the |
| 255 if (need_update) { | 294 // request; the stream callback will resume it. |
| 256 BrowserThread::PostTask( | 295 if (!stream_writer_->Write(read_buffer_, *bytes_read)) { |
| 257 BrowserThread::FILE, FROM_HERE, | 296 PauseRequest(); |
| 258 base::Bind(&DownloadFileManager::UpdateDownload, | 297 *defer = was_deferred_ = true; |
| 259 download_file_manager_, download_id_, buffer_)); | 298 last_stream_pause_time_ = now; |
| 260 } | 299 } |
| 261 | 300 |
| 262 // We schedule a pause outside of the read loop if there is too much file | 301 read_buffer_ = NULL; // Drop our reference. |
| 263 // writing work to do. | |
| 264 if (vector_size > kLoadsToWrite) { | |
| 265 *defer = was_deferred_ = true; | |
| 266 CheckWriteProgressLater(); | |
| 267 } | |
| 268 | 302 |
| 269 return true; | 303 return true; |
| 270 } | 304 } |
| 271 | 305 |
| 272 bool DownloadResourceHandler::OnResponseCompleted( | 306 bool DownloadResourceHandler::OnResponseCompleted( |
| 273 int request_id, | 307 int request_id, |
| 274 const net::URLRequestStatus& status, | 308 const net::URLRequestStatus& status, |
| 275 const std::string& security_info) { | 309 const std::string& security_info) { |
| 310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 276 VLOG(20) << __FUNCTION__ << "()" << DebugString() | 311 VLOG(20) << __FUNCTION__ << "()" << DebugString() |
| 277 << " request_id = " << request_id | 312 << " request_id = " << request_id |
| 278 << " status.status() = " << status.status() | 313 << " status.status() = " << status.status() |
| 279 << " status.error() = " << status.error(); | 314 << " status.error() = " << status.error(); |
| 280 int response = status.is_success() ? request_->GetResponseCode() : 0; | 315 int response_code = status.is_success() ? request_->GetResponseCode() : 0; |
| 281 if (download_id_.IsValid()) { | |
| 282 OnResponseCompletedInternal(request_id, status, security_info, response); | |
| 283 } else { | |
| 284 // We got cancelled before the task which sets the id ran on the IO thread. | |
| 285 // Wait for it. | |
| 286 BrowserThread::PostTaskAndReply( | |
| 287 BrowserThread::UI, FROM_HERE, | |
| 288 base::Bind(&base::DoNothing), | |
| 289 base::Bind(&DownloadResourceHandler::OnResponseCompletedInternal, this, | |
| 290 request_id, status, security_info, response)); | |
| 291 } | |
| 292 // Can't trust request_ being value after this point. | |
| 293 request_ = NULL; | |
| 294 return true; | |
| 295 } | |
| 296 | 316 |
| 297 void DownloadResourceHandler::OnResponseCompletedInternal( | |
| 298 int request_id, | |
| 299 const net::URLRequestStatus& status, | |
| 300 const std::string& security_info, | |
| 301 int response_code) { | |
| 302 // NOTE: |request_| may be a dangling pointer at this point. | |
| 303 VLOG(20) << __FUNCTION__ << "()" | |
| 304 << " request_id = " << request_id | |
| 305 << " status.status() = " << status.status() | |
| 306 << " status.error() = " << status.error(); | |
| 307 net::Error error_code = net::OK; | 317 net::Error error_code = net::OK; |
| 308 if (status.status() == net::URLRequestStatus::FAILED) | 318 if (status.status() == net::URLRequestStatus::FAILED) |
| 309 error_code = static_cast<net::Error>(status.error()); // Normal case. | 319 error_code = static_cast<net::Error>(status.error()); // Normal case. |
| 310 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are | 320 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are |
| 311 // allowed since a number of servers in the wild close the connection too | 321 // allowed since a number of servers in the wild close the connection too |
| 312 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - | 322 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - |
| 313 // treat downloads as complete in both cases, so we follow their lead. | 323 // treat downloads as complete in both cases, so we follow their lead. |
| 314 if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || | 324 if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || |
| 315 error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { | 325 error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { |
| 316 error_code = net::OK; | 326 error_code = net::OK; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 340 break; | 350 break; |
| 341 default: | 351 default: |
| 342 reason = content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; | 352 reason = content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; |
| 343 break; | 353 break; |
| 344 } | 354 } |
| 345 } | 355 } |
| 346 } | 356 } |
| 347 | 357 |
| 348 download_stats::RecordAcceptsRanges(accept_ranges_, bytes_read_); | 358 download_stats::RecordAcceptsRanges(accept_ranges_, bytes_read_); |
| 349 | 359 |
| 350 // If the callback was already run on the UI thread, this will be a noop. | 360 CallStartedCB(DownloadId(), error_code); |
| 351 CallStartedCB(download_id_, error_code); | |
| 352 | 361 |
| 353 // We transfer ownership to |DownloadFileManager| to delete |buffer_|, | 362 // Send the info down the stream. Conditional is in case we get |
| 354 // so that any functions queued up on the FILE thread are executed | 363 // OnResponseCompleted without OnResponseStarted. |
| 355 // before deletion. | 364 if (stream_writer_.get()) |
| 356 BrowserThread::PostTask( | 365 stream_writer_->Close(reason); |
| 357 BrowserThread::FILE, FROM_HERE, | 366 |
| 358 base::Bind(&DownloadFileManager::OnResponseCompleted, | 367 stream_writer_.reset(); // We no longer need the stream. |
| 359 download_file_manager_, download_id_, reason, security_info)); | |
| 360 buffer_ = NULL; // The buffer is longer needed by |DownloadResourceHandler|. | |
| 361 read_buffer_ = NULL; | 368 read_buffer_ = NULL; |
| 369 |
| 370 // Stats |
| 371 download_stats::RecordNetworkBandwidth( |
| 372 bytes_read_, base::TimeTicks::Now() - download_start_time_, |
| 373 total_pause_time_); |
| 374 |
| 375 return true; |
| 362 } | 376 } |
| 363 | 377 |
| 364 void DownloadResourceHandler::OnRequestClosed() { | 378 void DownloadResourceHandler::OnRequestClosed() { |
| 379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 365 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", | 380 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", |
| 366 base::TimeTicks::Now() - download_start_time_); | 381 base::TimeTicks::Now() - download_start_time_); |
| 367 } | |
| 368 | 382 |
| 369 void DownloadResourceHandler::StartOnUIThread( | 383 // Can't trust request_ being valid after this point. |
| 370 scoped_ptr<DownloadCreateInfo> info, | 384 request_ = NULL; |
| 371 const DownloadRequestHandle& handle) { | |
| 372 DownloadManager* download_manager = handle.GetDownloadManager(); | |
| 373 if (!download_manager) { | |
| 374 // NULL in unittests or if the page closed right after starting the | |
| 375 // download. | |
| 376 CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); | |
| 377 return; | |
| 378 } | |
| 379 DownloadId download_id = download_manager->delegate()->GetNextId(); | |
| 380 info->download_id = download_id; | |
| 381 | |
| 382 // NOTE: StartDownload triggers creation of the download destination file | |
| 383 // that will hold the downloaded data. SetDownloadID unblocks the | |
| 384 // DownloadResourceHandler to begin forwarding network data to the download | |
| 385 // destination file. The sequence of these two steps is critical as creation | |
| 386 // of the downloaded destination file has to happen before we attempt to | |
| 387 // append data to it. Both of those operations happen on the FILE thread. | |
| 388 | |
| 389 download_file_manager_->StartDownload(info.release(), handle); | |
| 390 | |
| 391 BrowserThread::PostTask( | |
| 392 BrowserThread::IO, FROM_HERE, | |
| 393 base::Bind(&DownloadResourceHandler::SetDownloadID, this, | |
| 394 download_id)); | |
| 395 | |
| 396 CallStartedCB(download_id, net::OK); | |
| 397 } | |
| 398 | |
| 399 void DownloadResourceHandler::SetDownloadID(content::DownloadId id) { | |
| 400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 401 | |
| 402 download_id_ = id; | |
| 403 MaybeResumeRequest(); | |
| 404 } | 385 } |
| 405 | 386 |
| 406 // If the content-length header is not present (or contains something other | 387 // If the content-length header is not present (or contains something other |
| 407 // than numbers), the incoming content_length is -1 (unknown size). | 388 // than numbers), the incoming content_length is -1 (unknown size). |
| 408 // Set the content length to 0 to indicate unknown size to DownloadManager. | 389 // Set the content length to 0 to indicate unknown size to DownloadManager. |
| 409 void DownloadResourceHandler::SetContentLength(const int64& content_length) { | 390 void DownloadResourceHandler::SetContentLength(const int64& content_length) { |
| 391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 410 content_length_ = 0; | 392 content_length_ = 0; |
| 411 if (content_length > 0) | 393 if (content_length > 0) |
| 412 content_length_ = content_length; | 394 content_length_ = content_length; |
| 413 } | 395 } |
| 414 | 396 |
| 415 void DownloadResourceHandler::SetContentDisposition( | 397 void DownloadResourceHandler::SetContentDisposition( |
| 416 const std::string& content_disposition) { | 398 const std::string& content_disposition) { |
| 399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 417 content_disposition_ = content_disposition; | 400 content_disposition_ = content_disposition; |
| 418 } | 401 } |
| 419 | 402 |
| 420 void DownloadResourceHandler::CheckWriteProgress() { | |
| 421 if (!buffer_.get()) | |
| 422 return; // The download completed while we were waiting to run. | |
| 423 | |
| 424 if (buffer_->size() > kLoadsToWrite) { | |
| 425 // We'll come back later and see if it's okay to unpause the request. | |
| 426 CheckWriteProgressLater(); | |
| 427 return; | |
| 428 } | |
| 429 | |
| 430 MaybeResumeRequest(); | |
| 431 } | |
| 432 | |
| 433 void DownloadResourceHandler::PauseRequest() { | 403 void DownloadResourceHandler::PauseRequest() { |
| 434 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 435 | 405 |
| 436 ++pause_count_; | 406 ++pause_count_; |
| 437 } | 407 } |
| 438 | 408 |
| 439 void DownloadResourceHandler::ResumeRequest() { | 409 void DownloadResourceHandler::ResumeRequest() { |
| 440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 441 DCHECK_LT(0, pause_count_); | 411 DCHECK_LT(0, pause_count_); |
| 442 | 412 |
| 443 --pause_count_; | 413 --pause_count_; |
| 444 MaybeResumeRequest(); | 414 |
| 415 if (!was_deferred_) |
| 416 return; |
| 417 if (pause_count_ > 0) |
| 418 return; |
| 419 |
| 420 was_deferred_ = false; |
| 421 if (!last_stream_pause_time_.is_null()) { |
| 422 total_pause_time_ += (base::TimeTicks::Now() - last_stream_pause_time_); |
| 423 last_stream_pause_time_ = base::TimeTicks(); |
| 424 } |
| 425 ResourceDispatcherHostImpl::Get()->ResumeDeferredRequest( |
| 426 global_id_.child_id, |
| 427 global_id_.request_id); |
| 445 } | 428 } |
| 446 | 429 |
| 447 void DownloadResourceHandler::CancelRequest() { | 430 void DownloadResourceHandler::CancelRequest() { |
| 448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 431 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 449 | 432 |
| 450 ResourceDispatcherHostImpl::Get()->CancelRequest( | 433 ResourceDispatcherHostImpl::Get()->CancelRequest( |
| 451 global_id_.child_id, | 434 global_id_.child_id, |
| 452 global_id_.request_id, | 435 global_id_.request_id, |
| 453 false); | 436 false); |
| 454 } | 437 } |
| 455 | 438 |
| 456 std::string DownloadResourceHandler::DebugString() const { | 439 std::string DownloadResourceHandler::DebugString() const { |
| 457 return base::StringPrintf("{" | 440 return base::StringPrintf("{" |
| 458 " url_ = " "\"%s\"" | 441 " url_ = " "\"%s\"" |
| 459 " download_id_ = " "%d" | |
| 460 " global_id_ = {" | 442 " global_id_ = {" |
| 461 " child_id = " "%d" | 443 " child_id = " "%d" |
| 462 " request_id = " "%d" | 444 " request_id = " "%d" |
| 463 " }" | 445 " }" |
| 464 " render_view_id_ = " "%d" | 446 " render_view_id_ = " "%d" |
| 465 " save_info_.file_path = \"%" PRFilePath "\"" | 447 " save_info_.file_path = \"%" PRFilePath "\"" |
| 466 " }", | 448 " }", |
| 467 request_ ? | 449 request_ ? |
| 468 request_->url().spec().c_str() : | 450 request_->url().spec().c_str() : |
| 469 "<NULL request>", | 451 "<NULL request>", |
| 470 download_id_.local(), | |
| 471 global_id_.child_id, | 452 global_id_.child_id, |
| 472 global_id_.request_id, | 453 global_id_.request_id, |
| 473 render_view_id_, | 454 render_view_id_, |
| 474 save_info_.file_path.value().c_str()); | 455 save_info_.file_path.value().c_str()); |
| 475 } | 456 } |
| 476 | 457 |
| 477 DownloadResourceHandler::~DownloadResourceHandler() { | 458 DownloadResourceHandler::~DownloadResourceHandler() { |
| 459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 460 |
| 478 // This won't do anything if the callback was called before. | 461 // This won't do anything if the callback was called before. |
| 479 // If it goes through, it will likely be because OnWillStart() returned | 462 // If it goes through, it will likely be because OnWillStart() returned |
| 480 // false somewhere in the chain of resource handlers. | 463 // false somewhere in the chain of resource handlers. |
| 481 CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); | 464 CallStartedCB(DownloadId(), net::ERR_ACCESS_DENIED); |
| 465 |
| 466 // Remove output stream callback if a stream exists. |
| 467 if (stream_writer_.get()) |
| 468 stream_writer_->RegisterCallback(base::Closure()); |
| 482 } | 469 } |
| 483 | 470 |
| 484 void DownloadResourceHandler::CheckWriteProgressLater() { | |
| 485 if (!check_write_progress_timer_.IsRunning()) { | |
| 486 check_write_progress_timer_.Start( | |
| 487 FROM_HERE, | |
| 488 base::TimeDelta::FromMilliseconds(kThrottleTimeMs), | |
| 489 this, | |
| 490 &DownloadResourceHandler::CheckWriteProgress); | |
| 491 } | |
| 492 } | |
| 493 | |
| 494 void DownloadResourceHandler::MaybeResumeRequest() { | |
| 495 if (!was_deferred_) | |
| 496 return; | |
| 497 | |
| 498 if (pause_count_ > 0) | |
| 499 return; | |
| 500 if (download_id_ == DownloadId::Invalid()) | |
| 501 return; | |
| 502 if (buffer_.get() && (buffer_->size() > kLoadsToWrite)) | |
| 503 return; | |
| 504 | |
| 505 was_deferred_ = false; | |
| 506 ResourceDispatcherHostImpl::Get()->ResumeDeferredRequest( | |
| 507 global_id_.child_id, | |
| 508 global_id_.request_id); | |
| 509 } | |
| OLD | NEW |