text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "content/browser/loader/throttling_resource_handler.h"
  
  #include <utility>
  
  #include "content/browser/loader/resource_request_info_impl.h"
  #include "content/public/browser/resource_throttle.h"
  #include "content/public/common/resource_response.h"
  #include "net/url_request/url_request.h"
  
  namespace content {
  
  ThrottlingResourceHandler::ThrottlingResourceHandler(
      scoped_ptr<ResourceHandler> next_handler,
      net::URLRequest* request,
      ScopedVector<ResourceThrottle> throttles)
      : LayeredResourceHandler(request, std::move(next_handler)),
        deferred_stage_(DEFERRED_NONE),
        throttles_(std::move(throttles)),
        next_index_(0),
        cancelled_by_resource_throttle_(false) {
    for (size_t i = 0; i < throttles_.size(); ++i) {
      throttles_[i]->set_controller(this);
      // Throttles must have a name, as otherwise, bugs where a throttle fails
      // to resume a request can be very difficult to debug.
      DCHECK(throttles_[i]->GetNameForLogging());
    }
  }
  
  ThrottlingResourceHandler::~ThrottlingResourceHandler() {
  }
  
  bool ThrottlingResourceHandler::OnRequestRedirected(
      const net::RedirectInfo& redirect_info,
      ResourceResponse* response,
      bool* defer) {
    DCHECK(!cancelled_by_resource_throttle_);
  
    *defer = false;
    while (next_index_ < throttles_.size()) {
      int index = next_index_;
      throttles_[index]->WillRedirectRequest(redirect_info, defer);
      next_index_++;
      if (cancelled_by_resource_throttle_)
        return false;
      if (*defer) {
        OnRequestDefered(index);
        deferred_stage_ = DEFERRED_REDIRECT;
        deferred_redirect_ = redirect_info;
        deferred_response_ = response;
        return true;  // Do not cancel.
      }
    }
  
    next_index_ = 0;  // Reset for next time.
  
    return next_handler_->OnRequestRedirected(redirect_info, response, defer);
  }
  
  bool ThrottlingResourceHandler::OnWillStart(const GURL& url, bool* defer) {
    DCHECK(!cancelled_by_resource_throttle_);
  
    *defer = false;
    while (next_index_ < throttles_.size()) {
      int index = next_index_;
      throttles_[index]->WillStartRequest(defer);
      next_index_++;
      if (cancelled_by_resource_throttle_)
        return false;
      if (*defer) {
        OnRequestDefered(index);
        deferred_stage_ = DEFERRED_START;
        deferred_url_ = url;
        return true;  // Do not cancel.
      }
    }
  
    next_index_ = 0;  // Reset for next time.
  
    return next_handler_->OnWillStart(url, defer);
  }
  
  bool ThrottlingResourceHandler::OnBeforeNetworkStart(const GURL& url,
                                                       bool* defer) {
    DCHECK(!cancelled_by_resource_throttle_);
  
    *defer = false;
    while (next_index_ < throttles_.size()) {
      int index = next_index_;
      throttles_[index]->WillStartUsingNetwork(defer);
      next_index_++;
      if (cancelled_by_resource_throttle_)
        return false;
      if (*defer) {
        OnRequestDefered(index);
        deferred_stage_ = DEFERRED_NETWORK_START;
        deferred_url_ = url;
        return true;  // Do not cancel.
      }
    }
  
    next_index_ = 0;  // Reset for next time.
  
    return next_handler_->OnBeforeNetworkStart(url, defer);
  }
  
  bool ThrottlingResourceHandler::OnResponseStarted(ResourceResponse* response,
                                                    bool* defer,
                                                    bool open_when_done,
                                                    bool ask_for_target) {
    DCHECK(!cancelled_by_resource_throttle_);
  
    while (next_index_ < throttles_.size()) {
      int index = next_index_;
      throttles_[index]->WillProcessResponse(defer);
      next_index_++;
      if (cancelled_by_resource_throttle_)
        return false;
      if (*defer) {
        OnRequestDefered(index);
        deferred_stage_ = DEFERRED_RESPONSE;
        deferred_response_ = response;
        return true;  // Do not cancel.
      }
    }
  
    next_index_ = 0;  // Reset for next time.
  
    return next_handler_->OnResponseStarted(response,
                                            defer,
                                            open_when_done,
                                            ask_for_target);
  }
  
  void ThrottlingResourceHandler::Cancel() {
    cancelled_by_resource_throttle_ = true;
    controller()->Cancel();
  }
  
  void ThrottlingResourceHandler::CancelAndIgnore() {
    cancelled_by_resource_throttle_ = true;
    controller()->CancelAndIgnore();
  }
  
  void ThrottlingResourceHandler::CancelWithError(int error_code) {
    cancelled_by_resource_throttle_ = true;
    controller()->CancelWithError(error_code);
  }
  
  void ThrottlingResourceHandler::Resume(bool open_when_done, bool ask_for_target) {
    DCHECK(!cancelled_by_resource_throttle_);
  
    DeferredStage last_deferred_stage = deferred_stage_;
    deferred_stage_ = DEFERRED_NONE;
    // Clear information about the throttle that delayed the request.
    request()->LogUnblocked();
    switch (last_deferred_stage) {
      case DEFERRED_NONE:
        NOTREACHED();
        break;
      case DEFERRED_START:
        ResumeStart();
        break;
      case DEFERRED_NETWORK_START:
        ResumeNetworkStart();
        break;
      case DEFERRED_REDIRECT:
        ResumeRedirect();
        break;
      case DEFERRED_RESPONSE:
        ResumeResponse(open_when_done, ask_for_target);
        break;
    }
  }
  
  void ThrottlingResourceHandler::ResumeStart() {
    DCHECK(!cancelled_by_resource_throttle_);
  
    GURL url = deferred_url_;
    deferred_url_ = GURL();
  
    bool defer = false;
    if (!OnWillStart(url, &defer)) {
      controller()->Cancel();
    } else if (!defer) {
      controller()->Resume();
    }
  }
  
  void ThrottlingResourceHandler::ResumeNetworkStart() {
    DCHECK(!cancelled_by_resource_throttle_);
  
    GURL url = deferred_url_;
    deferred_url_ = GURL();
  
    bool defer = false;
    if (!OnBeforeNetworkStart(url, &defer)) {
      controller()->Cancel();
    } else if (!defer) {
      controller()->Resume();
    }
  }
  
  void ThrottlingResourceHandler::ResumeRedirect() {
    DCHECK(!cancelled_by_resource_throttle_);
  
    net::RedirectInfo redirect_info = deferred_redirect_;
    deferred_redirect_ = net::RedirectInfo();
    scoped_refptr<ResourceResponse> response;
    deferred_response_.swap(response);
  
    bool defer = false;
    if (!OnRequestRedirected(redirect_info, response.get(), &defer)) {
      controller()->Cancel();
    } else if (!defer) {
      controller()->Resume();
    }
  }
  
  void ThrottlingResourceHandler::ResumeResponse(bool open_when_done, bool ask_for_target) {
    DCHECK(!cancelled_by_resource_throttle_);
  
    scoped_refptr<ResourceResponse> response;
    deferred_response_.swap(response);
  
    bool defer = false;
    if (!OnResponseStarted(response.get(), &defer, open_when_done, ask_for_target)) {
      controller()->Cancel();
    } else if (!defer) {
      controller()->Resume(open_when_done);
    }
  }
  
  void ThrottlingResourceHandler::OnRequestDefered(int throttle_index) {
    request()->LogBlockedBy(throttles_[throttle_index]->GetNameForLogging());
  }
  
  }  // namespace content
  