1// Copyright (c) 2011 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 "chrome/browser/renderer_host/download_throttling_resource_handler.h"
6
7#include "base/logging.h"
8#include "chrome/browser/download/download_util.h"
9#include "chrome/browser/renderer_host/download_resource_handler.h"
10#include "content/browser/renderer_host/resource_dispatcher_host.h"
11#include "content/common/resource_response.h"
12#include "net/base/io_buffer.h"
13#include "net/base/mime_sniffer.h"
14
15DownloadThrottlingResourceHandler::DownloadThrottlingResourceHandler(
16    ResourceDispatcherHost* host,
17    net::URLRequest* request,
18    const GURL& url,
19    int render_process_host_id,
20    int render_view_id,
21    int request_id,
22    bool in_complete)
23    : host_(host),
24      request_(request),
25      url_(url),
26      render_process_host_id_(render_process_host_id),
27      render_view_id_(render_view_id),
28      request_id_(request_id),
29      tmp_buffer_length_(0),
30      ignore_on_read_complete_(in_complete),
31      request_closed_(false) {
32  download_util::RecordDownloadCount(
33      download_util::INITIATED_BY_NAVIGATION_COUNT);
34
35  // Pause the request.
36  host_->PauseRequest(render_process_host_id_, request_id_, true);
37
38  // Add a reference to ourselves to keep this object alive until we
39  // receive a callback from DownloadRequestLimiter. The reference is
40  // released in ContinueDownload() and CancelDownload().
41  AddRef();
42
43  host_->download_request_limiter()->CanDownloadOnIOThread(
44      render_process_host_id_, render_view_id, request_id, this);
45  BrowserThread::PostTask(
46      BrowserThread::UI, FROM_HERE,
47      NewRunnableFunction(&download_util::NotifyDownloadInitiated,
48                          render_process_host_id_, render_view_id_));
49}
50
51DownloadThrottlingResourceHandler::~DownloadThrottlingResourceHandler() {
52}
53
54bool DownloadThrottlingResourceHandler::OnUploadProgress(int request_id,
55                                                         uint64 position,
56                                                         uint64 size) {
57  DCHECK(!request_closed_);
58  if (download_handler_.get())
59    return download_handler_->OnUploadProgress(request_id, position, size);
60  return true;
61}
62
63bool DownloadThrottlingResourceHandler::OnRequestRedirected(
64    int request_id,
65    const GURL& url,
66    ResourceResponse* response,
67    bool* defer) {
68  DCHECK(!request_closed_);
69  if (download_handler_.get()) {
70    return download_handler_->OnRequestRedirected(
71        request_id, url, response, defer);
72  }
73  url_ = url;
74  return true;
75}
76
77bool DownloadThrottlingResourceHandler::OnResponseStarted(
78    int request_id,
79    ResourceResponse* response) {
80  DCHECK(!request_closed_);
81  if (download_handler_.get())
82    return download_handler_->OnResponseStarted(request_id, response);
83  response_ = response;
84  return true;
85}
86
87bool DownloadThrottlingResourceHandler::OnWillStart(int request_id,
88                                                    const GURL& url,
89                                                    bool* defer) {
90  DCHECK(!request_closed_);
91  if (download_handler_.get())
92    return download_handler_->OnWillStart(request_id, url, defer);
93  return true;
94}
95
96bool DownloadThrottlingResourceHandler::OnWillRead(int request_id,
97                                                   net::IOBuffer** buf,
98                                                   int* buf_size,
99                                                   int min_size) {
100  DCHECK(!request_closed_);
101  if (download_handler_.get())
102    return download_handler_->OnWillRead(request_id, buf, buf_size, min_size);
103
104  // We should only have this invoked once, as such we only deal with one
105  // tmp buffer.
106  DCHECK(!tmp_buffer_.get());
107  // If the caller passed a negative |min_size| then chose an appropriate
108  // default. The BufferedResourceHandler requires this to be at least 2 times
109  // the size required for mime detection.
110  if (min_size < 0)
111    min_size = 2 * net::kMaxBytesToSniff;
112  tmp_buffer_ = new net::IOBuffer(min_size);
113  *buf = tmp_buffer_.get();
114  *buf_size = min_size;
115  return true;
116}
117
118bool DownloadThrottlingResourceHandler::OnReadCompleted(int request_id,
119                                                        int* bytes_read) {
120  DCHECK(!request_closed_);
121  if (ignore_on_read_complete_) {
122    // See comments above definition for details on this.
123    ignore_on_read_complete_ = false;
124    return true;
125  }
126  if (!*bytes_read)
127    return true;
128
129  if (tmp_buffer_.get()) {
130    DCHECK(!tmp_buffer_length_);
131    tmp_buffer_length_ = *bytes_read;
132    if (download_handler_.get())
133      CopyTmpBufferToDownloadHandler();
134    return true;
135  }
136  if (download_handler_.get())
137    return download_handler_->OnReadCompleted(request_id, bytes_read);
138  return true;
139}
140
141bool DownloadThrottlingResourceHandler::OnResponseCompleted(
142    int request_id,
143    const net::URLRequestStatus& status,
144    const std::string& security_info) {
145  DCHECK(!request_closed_);
146  if (download_handler_.get())
147    return download_handler_->OnResponseCompleted(request_id, status,
148                                                  security_info);
149
150  // For a download, if ResourceDispatcher::Read() fails,
151  // ResourceDispatcher::OnresponseStarted() will call
152  // OnResponseCompleted(), and we will end up here with an error
153  // status.
154  if (!status.is_success())
155    return false;
156  NOTREACHED();
157  return true;
158}
159
160void DownloadThrottlingResourceHandler::OnRequestClosed() {
161  DCHECK(!request_closed_);
162  if (download_handler_.get())
163    download_handler_->OnRequestClosed();
164  request_closed_ = true;
165}
166
167void DownloadThrottlingResourceHandler::CancelDownload() {
168  if (!request_closed_)
169    host_->CancelRequest(render_process_host_id_, request_id_, false);
170  Release();  // Release the additional reference from constructor.
171}
172
173void DownloadThrottlingResourceHandler::ContinueDownload() {
174  DCHECK(!download_handler_.get());
175  if (!request_closed_) {
176    download_handler_ =
177        new DownloadResourceHandler(host_,
178                                    render_process_host_id_,
179                                    render_view_id_,
180                                    request_id_,
181                                    url_,
182                                    host_->download_file_manager(),
183                                    request_,
184                                    false,
185                                    DownloadSaveInfo());
186    if (response_.get())
187      download_handler_->OnResponseStarted(request_id_, response_.get());
188
189    if (tmp_buffer_length_)
190      CopyTmpBufferToDownloadHandler();
191
192    // And let the request continue.
193    host_->PauseRequest(render_process_host_id_, request_id_, false);
194  }
195  Release();  // Release the addtional reference from constructor.
196}
197
198void DownloadThrottlingResourceHandler::CopyTmpBufferToDownloadHandler() {
199  // Copy over the tmp buffer.
200  net::IOBuffer* buffer;
201  int buf_size;
202  if (download_handler_->OnWillRead(request_id_, &buffer, &buf_size,
203                                    tmp_buffer_length_)) {
204    CHECK(buf_size >= tmp_buffer_length_);
205    memcpy(buffer->data(), tmp_buffer_->data(), tmp_buffer_length_);
206    download_handler_->OnReadCompleted(request_id_, &tmp_buffer_length_);
207  }
208  tmp_buffer_length_ = 0;
209  tmp_buffer_ = NULL;
210}
211