// 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.

#ifndef CONTENT_BROWSER_LOADER_RESOURCE_LOADER_H_
#define CONTENT_BROWSER_LOADER_RESOURCE_LOADER_H_

#include <memory>

#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "content/browser/loader/resource_controller.h"
#include "content/browser/loader/resource_handler.h"
#include "content/browser/ssl/ssl_client_auth_handler.h"
#include "content/browser/ssl/ssl_error_handler.h"
#include "content/common/content_export.h"
#include "net/http/http_raw_request_headers.h"
#include "net/url_request/url_request.h"
#include "url/gurl.h"

namespace net {
class HttpResponseHeaders;
class X509Certificate;
}

namespace network {
class ScopedThrottlingToken;
}

namespace content {
class LoginDelegate;
class ResourceHandler;
class ResourceLoaderDelegate;
class ResourceRequestInfoImpl;

// This class is responsible for driving the URLRequest (i.e., calling Start,
// Read, and servicing events).  It has a ResourceHandler, which is typically a
// chain of ResourceHandlers, and is the ResourceController for its handler.
class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate,
                                      public SSLErrorHandler::Delegate,
                                      public SSLClientAuthHandler::Delegate,
                                      public ResourceHandler::Delegate {
 public:
  ResourceLoader(
      std::unique_ptr<net::URLRequest> request,
      std::unique_ptr<ResourceHandler> handler,
      ResourceLoaderDelegate* delegate,
      ResourceContext* resource_context,
      std::unique_ptr<network::ScopedThrottlingToken> throttling_token);
  ~ResourceLoader() override;

  void StartRequest();
  void CancelRequest(bool from_renderer);

  net::URLRequest* request() { return request_.get(); }
  ResourceRequestInfoImpl* GetRequestInfo();

  void ClearLoginDelegate();

  // ResourceHandler::Delegate implementation:
  void OutOfBandCancel(int error_code, bool tell_renderer) override;
  void PauseReadingBodyFromNet() override;
  void ResumeReadingBodyFromNet() override;

 private:
  // ResourceController implementation for the ResourceLoader.
  class Controller;

  // net::URLRequest::Delegate implementation:
  void OnReceivedRedirect(net::URLRequest* request,
                          const net::RedirectInfo& redirect_info,
                          bool* defer) override;
  void OnAuthRequired(net::URLRequest* request,
                      net::AuthChallengeInfo* info) override;
  void OnCertificateRequested(net::URLRequest* request,
                              net::SSLCertRequestInfo* info) override;
  void OnSSLCertificateError(net::URLRequest* request,
                             const net::SSLInfo& info,
                             bool fatal) override;
  void OnResponseStarted(net::URLRequest* request, int net_error) override;
  void OnReadCompleted(net::URLRequest* request, int bytes_read) override;

  // SSLErrorHandler::Delegate implementation:
  void CancelSSLRequest(int error, const net::SSLInfo* ssl_info) override;
  void ContinueSSLRequest() override;

  // SSLClientAuthHandler::Delegate implementation.
  void ContinueWithCertificate(
      scoped_refptr<net::X509Certificate> cert,
      scoped_refptr<net::SSLPrivateKey> private_key) override;
  void CancelCertificateSelection() override;

  // These correspond to Controller's methods.
  // TODO(mmenke):  Seems like this could be simplified a little.

  // |called_from_resource_controller| is true if called directly from a
  // ResourceController, in which case |resource_handler_| must not be invoked
  // or destroyed synchronously to avoid re-entrancy issues, and false
  // otherwise. |modified_request_headers| is used for redirects only.
  void Resume(
      bool called_from_resource_controller,
      const base::Optional<net::HttpRequestHeaders>& modified_request_headers);
  void Cancel();
  void CancelWithError(int error_code);

  void StartRequestInternal();
  void CancelRequestInternal(int error, bool from_renderer);
  void FollowDeferredRedirectInternal(
      const base::Optional<net::HttpRequestHeaders>& modified_request_headers);
  void CompleteResponseStarted();
  // If |handle_result_async| is true, the result of the following read will be
  // handled asynchronously if it completes synchronously, unless it's EOF or an
  // error. This is to prevent a single request from blocking the thread for too
  // long.
  void PrepareToReadMore(bool handle_result_async);
  void ReadMore(bool handle_result_async);
  void ResumeReading();
  // Passes a read result to the handler.
  void CompleteRead(int bytes_read);
  void ResponseCompleted();
  void CallDidFinishLoading();
  void SetRawResponseHeaders(
      scoped_refptr<const net::HttpResponseHeaders> headers);

  bool is_deferred() const { return deferred_stage_ != DEFERRED_NONE; }

  // Used for categorizing loading of prefetches for reporting in histograms.
  // NOTE: This enumeration is used in histograms, so please do not add entries
  // in the middle.
  enum PrefetchStatus {
    STATUS_UNDEFINED,
    STATUS_SUCCESS_FROM_CACHE,
    STATUS_SUCCESS_FROM_NETWORK,
    STATUS_CANCELED,
    STATUS_SUCCESS_ALREADY_PREFETCHED,
    STATUS_MAX,
  };

  enum DeferredStage {
    DEFERRED_NONE,
    // Magic deferral "stage" which means that the code is currently in a
    // recursive call from the ResourceLoader. When in this state, Resume() does
    // nothing but update the deferral state, and when the stack is unwound back
    // up to the ResourceLoader, the request will be continued. This is used to
    // prevent the stack from getting too deep.
    DEFERRED_SYNC,
    DEFERRED_START,
    DEFERRED_REDIRECT,
    DEFERRED_ON_WILL_READ,
    DEFERRED_READ,
    DEFERRED_RESPONSE_COMPLETE,
    DEFERRED_FINISH
  };
  DeferredStage deferred_stage_;

  class ScopedDeferral;

  std::unique_ptr<net::URLRequest> request_;
  std::unique_ptr<ResourceHandler> handler_;
  ResourceLoaderDelegate* delegate_;

  scoped_refptr<LoginDelegate> login_delegate_;
  std::unique_ptr<SSLClientAuthHandler> ssl_client_auth_handler_;

  base::TimeTicks read_deferral_start_time_;

  // Instrumentation add to investigate http://crbug.com/503306.
  // TODO(mmenke): Remove once bug is fixed.
  int times_cancelled_before_request_start_;
  bool started_request_;
  int times_cancelled_after_request_start_;

  // Stores the URL from a deferred redirect.
  GURL deferred_redirect_url_;

  // Read buffer and its size.  Class members as OnWillRead can complete
  // asynchronously.
  scoped_refptr<net::IOBuffer> read_buffer_;
  int read_buffer_size_;

  net::HttpRawRequestHeaders raw_request_headers_;
  scoped_refptr<const net::HttpResponseHeaders> raw_response_headers_;

  ResourceContext* resource_context_;

  std::unique_ptr<network::ScopedThrottlingToken> throttling_token_;

  bool should_pause_reading_body_ = false;
  // The request is not deferred (i.e., DEFERRED_NONE) and is ready to read more
  // response body data. However, reading is paused because of
  // PauseReadingBodyFromNet().
  bool read_more_body_supressed_ = false;

  // Whether to update |body_read_before_paused_| after the pending read is
  // completed (or when this resource loader is destroyed).
  bool update_body_read_before_paused_ = false;
  // The number of bytes obtained by the reads initiated before the last
  // PauseReadingBodyFromNet() call. -1 means the request hasn't been paused.
  // The body may be read from cache or network. So even if this value is not
  // -1, we still need to check whether it is from network before reporting it
  // as BodyReadFromNetBeforePaused.
  int64_t body_read_before_paused_ = -1;

  bool pending_read_ = false;

  base::ThreadChecker thread_checker_;

  base::WeakPtrFactory<ResourceLoader> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(ResourceLoader);
};

}  // namespace content

#endif  // CONTENT_BROWSER_LOADER_RESOURCE_LOADER_H_
