1// Copyright 2013 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/loader/detachable_resource_handler.h"
6
7#include "base/logging.h"
8#include "base/time/time.h"
9#include "content/browser/loader/resource_request_info_impl.h"
10#include "net/base/io_buffer.h"
11#include "net/base/net_errors.h"
12#include "net/url_request/url_request.h"
13#include "net/url_request/url_request_status.h"
14
15namespace {
16// This matches the maximum allocation size of AsyncResourceHandler.
17const int kReadBufSize = 32 * 1024;
18}
19
20namespace content {
21
22DetachableResourceHandler::DetachableResourceHandler(
23    net::URLRequest* request,
24    base::TimeDelta cancel_delay,
25    scoped_ptr<ResourceHandler> next_handler)
26    : ResourceHandler(request),
27      next_handler_(next_handler.Pass()),
28      cancel_delay_(cancel_delay),
29      is_deferred_(false),
30      is_finished_(false) {
31  GetRequestInfo()->set_detachable_handler(this);
32}
33
34DetachableResourceHandler::~DetachableResourceHandler() {
35  // Cleanup back-pointer stored on the request info.
36  GetRequestInfo()->set_detachable_handler(NULL);
37}
38
39void DetachableResourceHandler::Detach() {
40  if (is_detached())
41    return;
42
43  if (!is_finished_) {
44    // Simulate a cancel on the next handler before destroying it.
45    net::URLRequestStatus status(net::URLRequestStatus::CANCELED,
46                                 net::ERR_ABORTED);
47    bool defer_ignored = false;
48    next_handler_->OnResponseCompleted(status, std::string(), &defer_ignored);
49    DCHECK(!defer_ignored);
50    // If |next_handler_| were to defer its shutdown in OnResponseCompleted,
51    // this would destroy it anyway. Fortunately, AsyncResourceHandler never
52    // does this anyway, so DCHECK it. BufferedResourceHandler and RVH shutdown
53    // already ignore deferred ResourceHandler shutdown, but
54    // DetachableResourceHandler and the detach-on-renderer-cancel logic
55    // introduces a case where this occurs when the renderer cancels a resource.
56  }
57  // A OnWillRead / OnReadCompleted pair may still be in progress, but
58  // OnWillRead passes back a scoped_refptr, so downstream handler's buffer will
59  // survive long enough to complete that read. From there, future reads will
60  // drain into |read_buffer_|. (If |next_handler_| is an AsyncResourceHandler,
61  // the net::IOBuffer takes a reference to the ResourceBuffer which owns the
62  // shared memory.)
63  next_handler_.reset();
64
65  // Time the request out if it takes too long.
66  detached_timer_.reset(new base::OneShotTimer<DetachableResourceHandler>());
67  detached_timer_->Start(
68      FROM_HERE, cancel_delay_, this, &DetachableResourceHandler::Cancel);
69
70  // Resume if necessary. The request may have been deferred, say, waiting on a
71  // full buffer in AsyncResourceHandler. Now that it has been detached, resume
72  // and drain it.
73  if (is_deferred_) {
74    // The nested ResourceHandler may have logged that it's blocking the
75    // request.  Log it as no longer doing so, to avoid a DCHECK on resume.
76    request()->LogUnblocked();
77    Resume();
78  }
79}
80
81void DetachableResourceHandler::SetController(ResourceController* controller) {
82  ResourceHandler::SetController(controller);
83
84  // Intercept the ResourceController for downstream handlers to keep track of
85  // whether the request is deferred.
86  if (next_handler_)
87    next_handler_->SetController(this);
88}
89
90bool DetachableResourceHandler::OnUploadProgress(uint64 position, uint64 size) {
91  if (!next_handler_)
92    return true;
93
94  return next_handler_->OnUploadProgress(position, size);
95}
96
97bool DetachableResourceHandler::OnRequestRedirected(
98    const net::RedirectInfo& redirect_info,
99    ResourceResponse* response,
100    bool* defer) {
101  DCHECK(!is_deferred_);
102
103  if (!next_handler_)
104    return true;
105
106  bool ret = next_handler_->OnRequestRedirected(
107      redirect_info, response, &is_deferred_);
108  *defer = is_deferred_;
109  return ret;
110}
111
112bool DetachableResourceHandler::OnResponseStarted(ResourceResponse* response,
113                                                  bool* defer) {
114  DCHECK(!is_deferred_);
115
116  if (!next_handler_)
117    return true;
118
119  bool ret =
120      next_handler_->OnResponseStarted(response, &is_deferred_);
121  *defer = is_deferred_;
122  return ret;
123}
124
125bool DetachableResourceHandler::OnWillStart(const GURL& url, bool* defer) {
126  DCHECK(!is_deferred_);
127
128  if (!next_handler_)
129    return true;
130
131  bool ret = next_handler_->OnWillStart(url, &is_deferred_);
132  *defer = is_deferred_;
133  return ret;
134}
135
136bool DetachableResourceHandler::OnBeforeNetworkStart(const GURL& url,
137                                                     bool* defer) {
138  DCHECK(!is_deferred_);
139
140  if (!next_handler_)
141    return true;
142
143  bool ret =
144      next_handler_->OnBeforeNetworkStart(url, &is_deferred_);
145  *defer = is_deferred_;
146  return ret;
147}
148
149bool DetachableResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
150                                           int* buf_size,
151                                           int min_size) {
152  if (!next_handler_) {
153    DCHECK_EQ(-1, min_size);
154    if (!read_buffer_.get())
155      read_buffer_ = new net::IOBuffer(kReadBufSize);
156    *buf = read_buffer_;
157    *buf_size = kReadBufSize;
158    return true;
159  }
160
161  return next_handler_->OnWillRead(buf, buf_size, min_size);
162}
163
164bool DetachableResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
165  DCHECK(!is_deferred_);
166
167  if (!next_handler_)
168    return true;
169
170  bool ret =
171      next_handler_->OnReadCompleted(bytes_read, &is_deferred_);
172  *defer = is_deferred_;
173  return ret;
174}
175
176void DetachableResourceHandler::OnResponseCompleted(
177    const net::URLRequestStatus& status,
178    const std::string& security_info,
179    bool* defer) {
180  // No DCHECK(!is_deferred_) as the request may have been cancelled while
181  // deferred.
182
183  if (!next_handler_)
184    return;
185
186  is_finished_ = true;
187
188  next_handler_->OnResponseCompleted(status, security_info, &is_deferred_);
189  *defer = is_deferred_;
190}
191
192void DetachableResourceHandler::OnDataDownloaded(int bytes_downloaded) {
193  if (!next_handler_)
194    return;
195
196  next_handler_->OnDataDownloaded(bytes_downloaded);
197}
198
199void DetachableResourceHandler::Resume() {
200  DCHECK(is_deferred_);
201  is_deferred_ = false;
202  controller()->Resume();
203}
204
205void DetachableResourceHandler::Cancel() {
206  controller()->Cancel();
207}
208
209void DetachableResourceHandler::CancelAndIgnore() {
210  controller()->CancelAndIgnore();
211}
212
213void DetachableResourceHandler::CancelWithError(int error_code) {
214  controller()->CancelWithError(error_code);
215}
216
217}  // namespace content
218