- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / download / download_resource_handler.cc
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
3 // found in the LICENSE file.
4
5 #include "content/browser/download/download_resource_handler.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/metrics/histogram.h"
13 #include "base/metrics/stats_counters.h"
14 #include "base/strings/stringprintf.h"
15 #include "content/browser/byte_stream.h"
16 #include "content/browser/download/download_create_info.h"
17 #include "content/browser/download/download_interrupt_reasons_impl.h"
18 #include "content/browser/download/download_manager_impl.h"
19 #include "content/browser/download/download_request_handle.h"
20 #include "content/browser/download/download_stats.h"
21 #include "content/browser/loader/resource_dispatcher_host_impl.h"
22 #include "content/browser/loader/resource_request_info_impl.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/download_interrupt_reasons.h"
25 #include "content/public/browser/download_item.h"
26 #include "content/public/browser/download_manager_delegate.h"
27 #include "content/public/common/resource_response.h"
28 #include "net/base/io_buffer.h"
29 #include "net/base/net_errors.h"
30 #include "net/http/http_response_headers.h"
31 #include "net/http/http_status_code.h"
32 #include "net/url_request/url_request_context.h"
33
34 namespace content {
35 namespace {
36
37 void CallStartedCBOnUIThread(
38     const DownloadUrlParameters::OnStartedCallback& started_cb,
39     DownloadItem* item,
40     net::Error error) {
41   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
42
43   if (started_cb.is_null())
44     return;
45   started_cb.Run(item, error);
46 }
47
48 // Static function in order to prevent any accidental accesses to
49 // DownloadResourceHandler members from the UI thread.
50 static void StartOnUIThread(
51     scoped_ptr<DownloadCreateInfo> info,
52     scoped_ptr<ByteStreamReader> stream,
53     const DownloadUrlParameters::OnStartedCallback& started_cb) {
54   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
55
56   DownloadManager* download_manager = info->request_handle.GetDownloadManager();
57   if (!download_manager) {
58     // NULL in unittests or if the page closed right after starting the
59     // download.
60     if (!started_cb.is_null())
61       started_cb.Run(NULL, net::ERR_ACCESS_DENIED);
62     return;
63   }
64
65   download_manager->StartDownload(info.Pass(), stream.Pass(), started_cb);
66 }
67
68 }  // namespace
69
70 const int DownloadResourceHandler::kDownloadByteStreamSize = 100 * 1024;
71
72 DownloadResourceHandler::DownloadResourceHandler(
73     uint32 id,
74     net::URLRequest* request,
75     const DownloadUrlParameters::OnStartedCallback& started_cb,
76     scoped_ptr<DownloadSaveInfo> save_info)
77     : ResourceHandler(request),
78       download_id_(id),
79       content_length_(0),
80       started_cb_(started_cb),
81       save_info_(save_info.Pass()),
82       last_buffer_size_(0),
83       bytes_read_(0),
84       pause_count_(0),
85       was_deferred_(false),
86       on_response_started_called_(false) {
87   RecordDownloadCount(UNTHROTTLED_COUNT);
88 }
89
90 bool DownloadResourceHandler::OnUploadProgress(int request_id,
91                                                uint64 position,
92                                                uint64 size) {
93   return true;
94 }
95
96 bool DownloadResourceHandler::OnRequestRedirected(
97     int request_id,
98     const GURL& url,
99     ResourceResponse* response,
100     bool* defer) {
101   // We treat a download as a main frame load, and thus update the policy URL
102   // on redirects.
103   request()->set_first_party_for_cookies(url);
104   return true;
105 }
106
107 // Send the download creation information to the download thread.
108 bool DownloadResourceHandler::OnResponseStarted(
109     int request_id,
110     ResourceResponse* response,
111     bool* defer) {
112   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
113   // There can be only one (call)
114   DCHECK(!on_response_started_called_);
115   on_response_started_called_ = true;
116
117   VLOG(20) << __FUNCTION__ << "()" << DebugString()
118            << " request_id = " << request_id;
119   download_start_time_ = base::TimeTicks::Now();
120
121   // If it's a download, we don't want to poison the cache with it.
122   request()->StopCaching();
123
124   // Lower priority as well, so downloads don't contend for resources
125   // with main frames.
126   request()->SetPriority(net::IDLE);
127
128   std::string content_disposition;
129   request()->GetResponseHeaderByName("content-disposition",
130                                      &content_disposition);
131   SetContentDisposition(content_disposition);
132   SetContentLength(response->head.content_length);
133
134   const ResourceRequestInfoImpl* request_info = GetRequestInfo();
135
136   // Deleted in DownloadManager.
137   scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo(
138       base::Time::Now(), content_length_,
139       request()->net_log(), request_info->HasUserGesture(),
140       request_info->GetPageTransition()));
141
142   // Create the ByteStream for sending data to the download sink.
143   scoped_ptr<ByteStreamReader> stream_reader;
144   CreateByteStream(
145       base::MessageLoopProxy::current(),
146       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
147       kDownloadByteStreamSize, &stream_writer_, &stream_reader);
148   stream_writer_->RegisterCallback(
149       base::Bind(&DownloadResourceHandler::ResumeRequest, AsWeakPtr()));
150
151   info->download_id = download_id_;
152   info->url_chain = request()->url_chain();
153   info->referrer_url = GURL(request()->referrer());
154   info->start_time = base::Time::Now();
155   info->total_bytes = content_length_;
156   info->has_user_gesture = request_info->HasUserGesture();
157   info->content_disposition = content_disposition_;
158   info->mime_type = response->head.mime_type;
159   info->remote_address = request()->GetSocketAddress().host();
160   RecordDownloadMimeType(info->mime_type);
161   RecordDownloadContentDisposition(info->content_disposition);
162
163   info->request_handle =
164       DownloadRequestHandle(AsWeakPtr(), request_info->GetChildID(),
165                             request_info->GetRouteID(),
166                             request_info->GetRequestID());
167
168   // Get the last modified time and etag.
169   const net::HttpResponseHeaders* headers = request()->response_headers();
170   if (headers) {
171     std::string last_modified_hdr;
172     if (headers->EnumerateHeader(NULL, "Last-Modified", &last_modified_hdr))
173       info->last_modified = last_modified_hdr;
174     if (headers->EnumerateHeader(NULL, "ETag", &etag_))
175       info->etag = etag_;
176
177     int status = headers->response_code();
178     if (2 == status / 100  && status != net::HTTP_PARTIAL_CONTENT) {
179       // Success & not range response; if we asked for a range, we didn't
180       // get it--reset the file pointers to reflect that.
181       save_info_->offset = 0;
182       save_info_->hash_state = "";
183     }
184   }
185
186   std::string content_type_header;
187   if (!response->head.headers.get() ||
188       !response->head.headers->GetMimeType(&content_type_header))
189     content_type_header = "";
190   info->original_mime_type = content_type_header;
191
192   if (!response->head.headers.get() ||
193       !response->head.headers->EnumerateHeader(
194           NULL, "Accept-Ranges", &accept_ranges_)) {
195     accept_ranges_ = "";
196   }
197
198   info->save_info = save_info_.Pass();
199
200   BrowserThread::PostTask(
201       BrowserThread::UI, FROM_HERE,
202       base::Bind(&StartOnUIThread,
203                  base::Passed(&info),
204                  base::Passed(&stream_reader),
205                  // Pass to StartOnUIThread so that variable
206                  // access is always on IO thread but function
207                  // is called on UI thread.
208                  started_cb_));
209   // Guaranteed to be called in StartOnUIThread
210   started_cb_.Reset();
211
212   return true;
213 }
214
215 void DownloadResourceHandler::CallStartedCB(
216     DownloadItem* item, net::Error error) {
217   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
218   if (started_cb_.is_null())
219     return;
220   BrowserThread::PostTask(
221       BrowserThread::UI, FROM_HERE,
222       base::Bind(&CallStartedCBOnUIThread, started_cb_, item, error));
223   started_cb_.Reset();
224 }
225
226 bool DownloadResourceHandler::OnWillStart(int request_id,
227                                           const GURL& url,
228                                           bool* defer) {
229   return true;
230 }
231
232 // Create a new buffer, which will be handed to the download thread for file
233 // writing and deletion.
234 bool DownloadResourceHandler::OnWillRead(int request_id,
235                                          scoped_refptr<net::IOBuffer>* buf,
236                                          int* buf_size,
237                                          int min_size) {
238   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
239   DCHECK(buf && buf_size);
240   DCHECK(!read_buffer_.get());
241
242   *buf_size = min_size < 0 ? kReadBufSize : min_size;
243   last_buffer_size_ = *buf_size;
244   read_buffer_ = new net::IOBuffer(*buf_size);
245   *buf = read_buffer_.get();
246   return true;
247 }
248
249 // Pass the buffer to the download file writer.
250 bool DownloadResourceHandler::OnReadCompleted(int request_id, int bytes_read,
251                                               bool* defer) {
252   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
253   DCHECK(read_buffer_.get());
254
255   base::TimeTicks now(base::TimeTicks::Now());
256   if (!last_read_time_.is_null()) {
257     double seconds_since_last_read = (now - last_read_time_).InSecondsF();
258     if (now == last_read_time_)
259       // Use 1/10 ms as a "very small number" so that we avoid
260       // divide-by-zero error and still record a very high potential bandwidth.
261       seconds_since_last_read = 0.00001;
262
263     double actual_bandwidth = (bytes_read)/seconds_since_last_read;
264     double potential_bandwidth = last_buffer_size_/seconds_since_last_read;
265     RecordBandwidth(actual_bandwidth, potential_bandwidth);
266   }
267   last_read_time_ = now;
268
269   if (!bytes_read)
270     return true;
271   bytes_read_ += bytes_read;
272   DCHECK(read_buffer_.get());
273
274   // Take the data ship it down the stream.  If the stream is full, pause the
275   // request; the stream callback will resume it.
276   if (!stream_writer_->Write(read_buffer_, bytes_read)) {
277     PauseRequest();
278     *defer = was_deferred_ = true;
279     last_stream_pause_time_ = now;
280   }
281
282   read_buffer_ = NULL;  // Drop our reference.
283
284   if (pause_count_ > 0)
285     *defer = was_deferred_ = true;
286
287   return true;
288 }
289
290 bool DownloadResourceHandler::OnResponseCompleted(
291     int request_id,
292     const net::URLRequestStatus& status,
293     const std::string& security_info) {
294   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
295   int response_code = status.is_success() ? request()->GetResponseCode() : 0;
296   VLOG(20) << __FUNCTION__ << "()" << DebugString()
297            << " request_id = " << request_id
298            << " status.status() = " << status.status()
299            << " status.error() = " << status.error()
300            << " response_code = " << response_code;
301
302   net::Error error_code = net::OK;
303   if (status.status() == net::URLRequestStatus::FAILED ||
304       // Note cancels as failures too.
305       status.status() == net::URLRequestStatus::CANCELED) {
306     error_code = static_cast<net::Error>(status.error());  // Normal case.
307     // Make sure that at least the fact of failure comes through.
308     if (error_code == net::OK)
309       error_code = net::ERR_FAILED;
310   }
311
312   // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are
313   // allowed since a number of servers in the wild close the connection too
314   // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 -
315   // treat downloads as complete in both cases, so we follow their lead.
316   if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH ||
317       error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) {
318     error_code = net::OK;
319   }
320   DownloadInterruptReason reason =
321       ConvertNetErrorToInterruptReason(
322         error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK);
323
324   if (status.status() == net::URLRequestStatus::CANCELED &&
325       status.error() == net::ERR_ABORTED) {
326     // CANCELED + ERR_ABORTED == something outside of the network
327     // stack cancelled the request.  There aren't that many things that
328     // could do this to a download request (whose lifetime is separated from
329     // the tab from which it came).  We map this to USER_CANCELLED as the
330     // case we know about (system suspend because of laptop close) corresponds
331     // to a user action.
332     // TODO(ahendrickson) -- Find a better set of codes to use here, as
333     // CANCELED/ERR_ABORTED can occur for reasons other than user cancel.
334     reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED;
335   }
336
337   if (status.is_success() &&
338       reason == DOWNLOAD_INTERRUPT_REASON_NONE &&
339       request()->response_headers()) {
340     // Handle server's response codes.
341     switch(response_code) {
342       case -1:                          // Non-HTTP request.
343       case net::HTTP_OK:
344       case net::HTTP_CREATED:
345       case net::HTTP_ACCEPTED:
346       case net::HTTP_NON_AUTHORITATIVE_INFORMATION:
347       case net::HTTP_RESET_CONTENT:
348       case net::HTTP_PARTIAL_CONTENT:
349         // Expected successful codes.
350         break;
351       case net::HTTP_NO_CONTENT:
352       case net::HTTP_NOT_FOUND:
353         reason = DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
354         break;
355       case net::HTTP_PRECONDITION_FAILED:
356         // Failed our 'If-Unmodified-Since' or 'If-Match'; see
357         // download_manager_impl.cc BeginDownload()
358         reason = DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION;
359         break;
360       case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
361         // Retry by downloading from the start automatically:
362         // If we haven't received data when we get this error, we won't.
363         reason = DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE;
364         break;
365       default:    // All other errors.
366         // Redirection and informational codes should have been handled earlier
367         // in the stack.
368         DCHECK_NE(3, response_code / 100);
369         DCHECK_NE(1, response_code / 100);
370         reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED;
371         break;
372     }
373   }
374
375   RecordAcceptsRanges(accept_ranges_, bytes_read_, etag_);
376   RecordNetworkBlockage(
377       base::TimeTicks::Now() - download_start_time_, total_pause_time_);
378
379   CallStartedCB(NULL, error_code);
380
381   // Send the info down the stream.  Conditional is in case we get
382   // OnResponseCompleted without OnResponseStarted.
383   if (stream_writer_)
384     stream_writer_->Close(reason);
385
386   // If the error mapped to something unknown, record it so that
387   // we can drill down.
388   if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) {
389     UMA_HISTOGRAM_CUSTOM_ENUMERATION("Download.MapErrorNetworkFailed",
390                                      std::abs(status.error()),
391                                      net::GetAllErrorCodesForUma());
392   }
393
394   stream_writer_.reset();  // We no longer need the stream.
395   read_buffer_ = NULL;
396
397   return true;
398 }
399
400 void DownloadResourceHandler::OnDataDownloaded(
401     int request_id,
402     int bytes_downloaded) {
403   NOTREACHED();
404 }
405
406 // If the content-length header is not present (or contains something other
407 // than numbers), the incoming content_length is -1 (unknown size).
408 // Set the content length to 0 to indicate unknown size to DownloadManager.
409 void DownloadResourceHandler::SetContentLength(const int64& content_length) {
410   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
411   content_length_ = 0;
412   if (content_length > 0)
413     content_length_ = content_length;
414 }
415
416 void DownloadResourceHandler::SetContentDisposition(
417     const std::string& content_disposition) {
418   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
419   content_disposition_ = content_disposition;
420 }
421
422 void DownloadResourceHandler::PauseRequest() {
423   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
424
425   ++pause_count_;
426 }
427
428 void DownloadResourceHandler::ResumeRequest() {
429   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
430   DCHECK_LT(0, pause_count_);
431
432   --pause_count_;
433
434   if (!was_deferred_)
435     return;
436   if (pause_count_ > 0)
437     return;
438
439   was_deferred_ = false;
440   if (!last_stream_pause_time_.is_null()) {
441     total_pause_time_ += (base::TimeTicks::Now() - last_stream_pause_time_);
442     last_stream_pause_time_ = base::TimeTicks();
443   }
444
445   controller()->Resume();
446 }
447
448 void DownloadResourceHandler::CancelRequest() {
449   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
450
451   const ResourceRequestInfo* info = GetRequestInfo();
452   ResourceDispatcherHostImpl::Get()->CancelRequest(
453       info->GetChildID(),
454       info->GetRequestID(),
455       false);
456 }
457
458 std::string DownloadResourceHandler::DebugString() const {
459   const ResourceRequestInfo* info = GetRequestInfo();
460   return base::StringPrintf("{"
461                             " url_ = " "\"%s\""
462                             " info = {"
463                             " child_id = " "%d"
464                             " request_id = " "%d"
465                             " route_id = " "%d"
466                             " }"
467                             " }",
468                             request() ?
469                                 request()->url().spec().c_str() :
470                                 "<NULL request>",
471                             info->GetChildID(),
472                             info->GetRequestID(),
473                             info->GetRouteID());
474 }
475
476 DownloadResourceHandler::~DownloadResourceHandler() {
477   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
478
479   // This won't do anything if the callback was called before.
480   // If it goes through, it will likely be because OnWillStart() returned
481   // false somewhere in the chain of resource handlers.
482   CallStartedCB(NULL, net::ERR_ACCESS_DENIED);
483
484   // Remove output stream callback if a stream exists.
485   if (stream_writer_)
486     stream_writer_->RegisterCallback(base::Closure());
487
488   UMA_HISTOGRAM_TIMES("SB2.DownloadDuration",
489                       base::TimeTicks::Now() - download_start_time_);
490 }
491
492 }  // namespace content