Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: content/browser/download/download_resource_handler.cc

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

Powered by Google App Engine
This is Rietveld 408576698