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/loader/throttling_resource_handler.h"
6
7#include "content/browser/loader/resource_request_info_impl.h"
8#include "content/public/browser/resource_throttle.h"
9#include "content/public/common/resource_response.h"
10#include "net/url_request/url_request.h"
11
12namespace content {
13
14ThrottlingResourceHandler::ThrottlingResourceHandler(
15    scoped_ptr<ResourceHandler> next_handler,
16    net::URLRequest* request,
17    ScopedVector<ResourceThrottle> throttles)
18    : LayeredResourceHandler(request, next_handler.Pass()),
19      deferred_stage_(DEFERRED_NONE),
20      throttles_(throttles.Pass()),
21      next_index_(0),
22      cancelled_by_resource_throttle_(false) {
23  for (size_t i = 0; i < throttles_.size(); ++i) {
24    throttles_[i]->set_controller(this);
25    // Throttles must have a name, as otherwise, bugs where a throttle fails
26    // to resume a request can be very difficult to debug.
27    DCHECK(throttles_[i]->GetNameForLogging());
28  }
29}
30
31ThrottlingResourceHandler::~ThrottlingResourceHandler() {
32}
33
34bool ThrottlingResourceHandler::OnRequestRedirected(
35    const net::RedirectInfo& redirect_info,
36    ResourceResponse* response,
37    bool* defer) {
38  DCHECK(!cancelled_by_resource_throttle_);
39
40  *defer = false;
41  while (next_index_ < throttles_.size()) {
42    int index = next_index_;
43    throttles_[index]->WillRedirectRequest(redirect_info.new_url, defer);
44    next_index_++;
45    if (cancelled_by_resource_throttle_)
46      return false;
47    if (*defer) {
48      OnRequestDefered(index);
49      deferred_stage_ = DEFERRED_REDIRECT;
50      deferred_redirect_ = redirect_info;
51      deferred_response_ = response;
52      return true;  // Do not cancel.
53    }
54  }
55
56  next_index_ = 0;  // Reset for next time.
57
58  return next_handler_->OnRequestRedirected(redirect_info, response, defer);
59}
60
61bool ThrottlingResourceHandler::OnWillStart(const GURL& url, bool* defer) {
62  DCHECK(!cancelled_by_resource_throttle_);
63
64  *defer = false;
65  while (next_index_ < throttles_.size()) {
66    int index = next_index_;
67    throttles_[index]->WillStartRequest(defer);
68    next_index_++;
69    if (cancelled_by_resource_throttle_)
70      return false;
71    if (*defer) {
72      OnRequestDefered(index);
73      deferred_stage_ = DEFERRED_START;
74      deferred_url_ = url;
75      return true;  // Do not cancel.
76    }
77  }
78
79  next_index_ = 0;  // Reset for next time.
80
81  return next_handler_->OnWillStart(url, defer);
82}
83
84bool ThrottlingResourceHandler::OnBeforeNetworkStart(const GURL& url,
85                                                     bool* defer) {
86  DCHECK(!cancelled_by_resource_throttle_);
87
88  *defer = false;
89  while (next_index_ < throttles_.size()) {
90    int index = next_index_;
91    throttles_[index]->WillStartUsingNetwork(defer);
92    next_index_++;
93    if (cancelled_by_resource_throttle_)
94      return false;
95    if (*defer) {
96      OnRequestDefered(index);
97      deferred_stage_ = DEFERRED_NETWORK_START;
98      deferred_url_ = url;
99      return true;  // Do not cancel.
100    }
101  }
102
103  next_index_ = 0;  // Reset for next time.
104
105  return next_handler_->OnBeforeNetworkStart(url, defer);
106}
107
108bool ThrottlingResourceHandler::OnResponseStarted(ResourceResponse* response,
109                                                  bool* defer) {
110  DCHECK(!cancelled_by_resource_throttle_);
111
112  while (next_index_ < throttles_.size()) {
113    int index = next_index_;
114    throttles_[index]->WillProcessResponse(defer);
115    next_index_++;
116    if (cancelled_by_resource_throttle_)
117      return false;
118    if (*defer) {
119      OnRequestDefered(index);
120      deferred_stage_ = DEFERRED_RESPONSE;
121      deferred_response_ = response;
122      return true;  // Do not cancel.
123    }
124  }
125
126  next_index_ = 0;  // Reset for next time.
127
128  return next_handler_->OnResponseStarted(response, defer);
129}
130
131void ThrottlingResourceHandler::Cancel() {
132  cancelled_by_resource_throttle_ = true;
133  controller()->Cancel();
134}
135
136void ThrottlingResourceHandler::CancelAndIgnore() {
137  cancelled_by_resource_throttle_ = true;
138  controller()->CancelAndIgnore();
139}
140
141void ThrottlingResourceHandler::CancelWithError(int error_code) {
142  cancelled_by_resource_throttle_ = true;
143  controller()->CancelWithError(error_code);
144}
145
146void ThrottlingResourceHandler::Resume() {
147  DCHECK(!cancelled_by_resource_throttle_);
148
149  DeferredStage last_deferred_stage = deferred_stage_;
150  deferred_stage_ = DEFERRED_NONE;
151  // Clear information about the throttle that delayed the request.
152  request()->LogUnblocked();
153  switch (last_deferred_stage) {
154    case DEFERRED_NONE:
155      NOTREACHED();
156      break;
157    case DEFERRED_START:
158      ResumeStart();
159      break;
160    case DEFERRED_NETWORK_START:
161      ResumeNetworkStart();
162      break;
163    case DEFERRED_REDIRECT:
164      ResumeRedirect();
165      break;
166    case DEFERRED_RESPONSE:
167      ResumeResponse();
168      break;
169  }
170}
171
172void ThrottlingResourceHandler::ResumeStart() {
173  DCHECK(!cancelled_by_resource_throttle_);
174
175  GURL url = deferred_url_;
176  deferred_url_ = GURL();
177
178  bool defer = false;
179  if (!OnWillStart(url, &defer)) {
180    controller()->Cancel();
181  } else if (!defer) {
182    controller()->Resume();
183  }
184}
185
186void ThrottlingResourceHandler::ResumeNetworkStart() {
187  DCHECK(!cancelled_by_resource_throttle_);
188
189  GURL url = deferred_url_;
190  deferred_url_ = GURL();
191
192  bool defer = false;
193  if (!OnBeforeNetworkStart(url, &defer)) {
194    controller()->Cancel();
195  } else if (!defer) {
196    controller()->Resume();
197  }
198}
199
200void ThrottlingResourceHandler::ResumeRedirect() {
201  DCHECK(!cancelled_by_resource_throttle_);
202
203  net::RedirectInfo redirect_info = deferred_redirect_;
204  deferred_redirect_ = net::RedirectInfo();
205  scoped_refptr<ResourceResponse> response;
206  deferred_response_.swap(response);
207
208  bool defer = false;
209  if (!OnRequestRedirected(redirect_info, response.get(), &defer)) {
210    controller()->Cancel();
211  } else if (!defer) {
212    controller()->Resume();
213  }
214}
215
216void ThrottlingResourceHandler::ResumeResponse() {
217  DCHECK(!cancelled_by_resource_throttle_);
218
219  scoped_refptr<ResourceResponse> response;
220  deferred_response_.swap(response);
221
222  bool defer = false;
223  if (!OnResponseStarted(response.get(), &defer)) {
224    controller()->Cancel();
225  } else if (!defer) {
226    controller()->Resume();
227  }
228}
229
230void ThrottlingResourceHandler::OnRequestDefered(int throttle_index) {
231  request()->LogBlockedBy(throttles_[throttle_index]->GetNameForLogging());
232}
233
234}  // namespace content
235