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