// 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.
//
// This file provides base classes used to issue HTTP requests for Google
// APIs.

#ifndef GOOGLE_APIS_DRIVE_BASE_REQUESTS_H_
#define GOOGLE_APIS_DRIVE_BASE_REQUESTS_H_

#include <stdint.h>

#include <string>
#include <vector>

#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "google_apis/drive/drive_api_error_codes.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_fetcher_response_writer.h"
#include "url/gurl.h"

namespace base {
class Value;
}  // namespace base

namespace google_apis {

class FileResource;
class RequestSender;

// Content type for multipart body.
enum MultipartType {
  MULTIPART_RELATED,
  MULTIPART_MIXED
};

// Pair of content type and data.
struct ContentTypeAndData {
  std::string type;
  std::string data;
};

typedef base::Callback<void(DriveApiErrorCode)> PrepareCallback;

// Callback used for requests that the server returns FileResource data
// formatted into JSON value.
typedef base::Callback<void(DriveApiErrorCode error,
                            std::unique_ptr<FileResource> entry)>
    FileResourceCallback;

// Callback used for DownloadFileRequest and ResumeUploadRequestBase.
typedef base::Callback<void(int64_t progress, int64_t total)> ProgressCallback;

// Callback used to get the content from DownloadFileRequest.
typedef base::Callback<void(DriveApiErrorCode error,
                            std::unique_ptr<std::string> content)>
    GetContentCallback;

// Parses JSON passed in |json|. Returns NULL on failure.
std::unique_ptr<base::Value> ParseJson(const std::string& json);

// Generate multipart body. If |predetermined_boundary| is not empty, it uses
// the string as boundary. Otherwise it generates random boundary that does not
// conflict with |parts|. If |data_offset| is not nullptr, it stores the
// index of first byte of each part in multipart body.
void GenerateMultipartBody(MultipartType multipart_type,
                           const std::string& predetermined_boundary,
                           const std::vector<ContentTypeAndData>& parts,
                           ContentTypeAndData* output,
                           std::vector<uint64_t>* data_offset);

//======================= AuthenticatedRequestInterface ======================

// An interface class for implementing a request which requires OAuth2
// authentication.
class AuthenticatedRequestInterface {
 public:
  // Called when re-authentication is required. See Start() for details.
  typedef base::Callback<void(AuthenticatedRequestInterface* request)>
      ReAuthenticateCallback;

  virtual ~AuthenticatedRequestInterface() {}

  // Starts the request with |access_token|. User-Agent header will be set
  // to |custom_user_agent| if the value is not empty.
  //
  // |callback| is called when re-authentication is needed for a certain
  // number of times (see kMaxReAuthenticateAttemptsPerRequest in .cc).
  // The callback should retry by calling Start() again with a new access
  // token, or just call OnAuthFailed() if a retry is not attempted.
  // |callback| must not be null.
  virtual void Start(const std::string& access_token,
                     const std::string& custom_user_agent,
                     const ReAuthenticateCallback& callback) = 0;

  // Invoked when the authentication failed with an error code |code|.
  virtual void OnAuthFailed(DriveApiErrorCode code) = 0;

  // Gets a weak pointer to this request object. Since requests may be
  // deleted when it is canceled by user action, for posting asynchronous tasks
  // on the authentication request object, weak pointers have to be used.
  // TODO(kinaba): crbug.com/134814 use more clean life time management than
  // using weak pointers.
  virtual base::WeakPtr<AuthenticatedRequestInterface> GetWeakPtr() = 0;

  // Cancels the request. It will invoke the callback object passed in
  // each request's constructor with error code DRIVE_CANCELLED.
  virtual void Cancel() = 0;
};

//=========================== ResponseWriter ==================================

// Saves the response for the request to a file or string.
class ResponseWriter : public net::URLFetcherResponseWriter {
 public:
  // If file_path is not empty, the response will be saved with file_writer_,
  // otherwise it will be saved to data_.
  ResponseWriter(base::SequencedTaskRunner* file_task_runner,
                 const base::FilePath& file_path,
                 const GetContentCallback& get_content_callback);
  ~ResponseWriter() override;

  const std::string& data() const { return data_; }

  // Disowns the output file.
  void DisownFile();

  // URLFetcherResponseWriter overrides:
  int Initialize(const net::CompletionCallback& callback) override;
  int Write(net::IOBuffer* buffer,
            int num_bytes,
            const net::CompletionCallback& callback) override;
  int Finish(int net_error, const net::CompletionCallback& callback) override;

 private:
  void DidWrite(scoped_refptr<net::IOBuffer> buffer,
                const net::CompletionCallback& callback,
                int result);

  const GetContentCallback get_content_callback_;
  std::string data_;
  std::unique_ptr<net::URLFetcherFileWriter> file_writer_;
  base::WeakPtrFactory<ResponseWriter> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(ResponseWriter);
};

//============================ UrlFetchRequestBase ===========================

// Base class for requests that are fetching URLs.
class UrlFetchRequestBase : public AuthenticatedRequestInterface,
                            public net::URLFetcherDelegate {
 public:
  // AuthenticatedRequestInterface overrides.
  void Start(const std::string& access_token,
             const std::string& custom_user_agent,
             const ReAuthenticateCallback& callback) override;
  base::WeakPtr<AuthenticatedRequestInterface> GetWeakPtr() override;
  void Cancel() override;

 protected:
  explicit UrlFetchRequestBase(RequestSender* sender);
  ~UrlFetchRequestBase() override;

  // Does async initialization for the request. |Start| calls this method so you
  // don't need to call this before |Start|.
  virtual void Prepare(const PrepareCallback& callback);

  // Gets URL for the request.
  virtual GURL GetURL() const = 0;

  // Returns the request type. A derived class should override this method
  // for a request type other than HTTP GET.
  virtual net::URLFetcher::RequestType GetRequestType() const;

  // Returns the extra HTTP headers for the request. A derived class should
  // override this method to specify any extra headers needed for the request.
  virtual std::vector<std::string> GetExtraRequestHeaders() const;

  // Used by a derived class to add any content data to the request.
  // Returns true if |upload_content_type| and |upload_content| are updated
  // with the content type and data for the request.
  // Note that this and GetContentFile() cannot be used together.
  virtual bool GetContentData(std::string* upload_content_type,
                              std::string* upload_content);

  // Used by a derived class to add content data which is the whole file or
  // a part of the file at |local_file_path|.
  // Returns true if all the arguments are updated for the content being
  // uploaded.
  // Note that this and GetContentData() cannot be used together.
  virtual bool GetContentFile(base::FilePath* local_file_path,
                              int64_t* range_offset,
                              int64_t* range_length,
                              std::string* upload_content_type);

  // Used by a derived class to set an output file path if they want to save
  // the downloaded content to a file at a specific path.
  // Sets |get_content_callback|, which is called when some part of the response
  // is read.
  virtual void GetOutputFilePath(base::FilePath* local_file_path,
                                 GetContentCallback* get_content_callback);

  // Invoked by OnURLFetchComplete when the request completes without an
  // authentication error. Must be implemented by a derived class.
  virtual void ProcessURLFetchResults(const net::URLFetcher* source) = 0;

  // Invoked by this base class upon an authentication error or cancel by
  // a user request. Must be implemented by a derived class.
  virtual void RunCallbackOnPrematureFailure(DriveApiErrorCode code) = 0;

  // Invoked from derived classes when ProcessURLFetchResults() is completed.
  void OnProcessURLFetchResultsComplete();

  // Returns an appropriate DriveApiErrorCode based on the HTTP response code
  // and the status of the URLFetcher.
  DriveApiErrorCode GetErrorCode();

  // Returns true if called on the thread where the constructor was called.
  bool CalledOnValidThread();

  // Returns the writer which is used to save the response for the request.
  ResponseWriter* response_writer() const { return response_writer_; }

  // Returns the task runner that should be used for blocking tasks.
  base::SequencedTaskRunner* blocking_task_runner() const;

 private:
  // Continues |Start| function after |Prepare|.
  void StartAfterPrepare(const std::string& access_token,
                         const std::string& custom_user_agent,
                         const ReAuthenticateCallback& callback,
                         DriveApiErrorCode code);

  // Invokes callback with |code| and request to delete the request to
  // |sender_|.
  void CompleteRequestWithError(DriveApiErrorCode code);

  // URLFetcherDelegate overrides.
  void OnURLFetchComplete(const net::URLFetcher* source) override;

  // AuthenticatedRequestInterface overrides.
  void OnAuthFailed(DriveApiErrorCode code) override;

  ReAuthenticateCallback re_authenticate_callback_;
  int re_authenticate_count_;
  std::unique_ptr<net::URLFetcher> url_fetcher_;
  ResponseWriter* response_writer_;  // Owned by |url_fetcher_|.
  RequestSender* sender_;
  DriveApiErrorCode error_code_;

  base::ThreadChecker thread_checker_;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<UrlFetchRequestBase> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(UrlFetchRequestBase);
};

//============================ BatchableDelegate ============================

// Delegate to be used by |SingleBatchableDelegateRequest| and
// |BatchUploadRequest|.
class BatchableDelegate {
 public:
  virtual ~BatchableDelegate() {}

  // See UrlFetchRequestBase.
  virtual GURL GetURL() const = 0;
  virtual net::URLFetcher::RequestType GetRequestType() const = 0;
  virtual std::vector<std::string> GetExtraRequestHeaders() const = 0;
  virtual void Prepare(const PrepareCallback& callback) = 0;
  virtual bool GetContentData(std::string* upload_content_type,
                              std::string* upload_content) = 0;

  // Notifies result of the request. Usually, it parses the |code| and
  // |response_body|, then notifies the parsed value to client code of the
  // API.  |callback| must be called on completion. The instance must not
  // do anything after calling |callback| since the instance may be deleted in
  // |callback|.
  virtual void NotifyResult(DriveApiErrorCode code,
                            const std::string& response_body,
                            const base::Closure& callback) = 0;

  // Notifies error. Unlike |NotifyResult|, it must report error
  // synchronously. The instance may be deleted just after calling
  // NotifyError.
  virtual void NotifyError(DriveApiErrorCode code) = 0;

  // Notifies progress.
  virtual void NotifyUploadProgress(const net::URLFetcher* source,
                                    int64_t current,
                                    int64_t total) = 0;
};

//============================ EntryActionRequest ============================

// Callback type for requests that return only error status, like: Delete/Move.
typedef base::Callback<void(DriveApiErrorCode error)> EntryActionCallback;

// This class performs a simple action over a given entry (document/file).
// It is meant to be used for requests that return no JSON blobs.
class EntryActionRequest : public UrlFetchRequestBase {
 public:
  // |callback| is called when the request is finished either by success or by
  // failure. It must not be null.
  EntryActionRequest(RequestSender* sender,
                     const EntryActionCallback& callback);
  ~EntryActionRequest() override;

 protected:
  // Overridden from UrlFetchRequestBase.
  void ProcessURLFetchResults(const net::URLFetcher* source) override;
  void RunCallbackOnPrematureFailure(DriveApiErrorCode code) override;

 private:
  const EntryActionCallback callback_;

  DISALLOW_COPY_AND_ASSIGN(EntryActionRequest);
};

//=========================== InitiateUploadRequestBase=======================

// Callback type for DriveServiceInterface::InitiateUpload.
typedef base::Callback<void(DriveApiErrorCode error,
                            const GURL& upload_url)> InitiateUploadCallback;

// This class provides base implementation for performing the request for
// initiating the upload of a file.
// |callback| will be called with the obtained upload URL. The URL will be
// used with requests for resuming the file uploading.
//
// Here's the flow of uploading:
// 1) Get the upload URL with a class inheriting InitiateUploadRequestBase.
// 2) Upload the first 1GB (see kUploadChunkSize in drive_uploader.cc)
//    of the target file to the upload URL
// 3) If there is more data to upload, go to 2).
//
class InitiateUploadRequestBase : public UrlFetchRequestBase {
 protected:
  // |callback| will be called with the upload URL, where upload data is
  // uploaded to with ResumeUploadRequestBase. It must not be null.
  // |content_type| and |content_length| should be the attributes of the
  // uploading file.
  InitiateUploadRequestBase(RequestSender* sender,
                            const InitiateUploadCallback& callback,
                            const std::string& content_type,
                            int64_t content_length);
  ~InitiateUploadRequestBase() override;

  // UrlFetchRequestBase overrides.
  void ProcessURLFetchResults(const net::URLFetcher* source) override;
  void RunCallbackOnPrematureFailure(DriveApiErrorCode code) override;
  std::vector<std::string> GetExtraRequestHeaders() const override;

 private:
  const InitiateUploadCallback callback_;
  const std::string content_type_;
  const int64_t content_length_;

  DISALLOW_COPY_AND_ASSIGN(InitiateUploadRequestBase);
};

//========================== UploadRangeRequestBase ==========================

// Struct for response to ResumeUpload and GetUploadStatus.
struct UploadRangeResponse {
  UploadRangeResponse();
  UploadRangeResponse(DriveApiErrorCode code,
                      int64_t start_position_received,
                      int64_t end_position_received);
  ~UploadRangeResponse();

  DriveApiErrorCode code;
  // The values of "Range" header returned from the server. The values are
  // used to continue uploading more data. These are set to -1 if an upload
  // is complete.
  // |start_position_received| is inclusive and |end_position_received| is
  // exclusive to follow the common C++ manner, although the response from
  // the server has "Range" header in inclusive format at both sides.
  int64_t start_position_received;
  int64_t end_position_received;
};

// Base class for a URL fetch request expecting the response containing the
// current uploading range. This class processes the response containing
// "Range" header and invoke OnRangeRequestComplete.
class UploadRangeRequestBase : public UrlFetchRequestBase {
 protected:
  // |upload_url| is the URL of where to upload the file to.
  UploadRangeRequestBase(RequestSender* sender, const GURL& upload_url);
  ~UploadRangeRequestBase() override;

  // UrlFetchRequestBase overrides.
  GURL GetURL() const override;
  net::URLFetcher::RequestType GetRequestType() const override;
  void ProcessURLFetchResults(const net::URLFetcher* source) override;
  void RunCallbackOnPrematureFailure(DriveApiErrorCode code) override;

  // This method will be called when the request is done, regardless of
  // whether it is succeeded or failed.
  //
  // 1) If there is more data to upload, |code| of |response| is set to
  // HTTP_RESUME_INCOMPLETE, and positions are set appropriately. Also, |value|
  // will be set to NULL.
  // 2) If the upload is complete, |code| is set to HTTP_CREATED for a new file
  // or HTTP_SUCCESS for an existing file. Positions are set to -1, and |value|
  // is set to a parsed JSON value representing the uploaded file.
  // 3) If a premature failure is found, |code| is set to a value representing
  // the situation. Positions are set to 0, and |value| is set to NULL.
  //
  // See also the comments for UploadRangeResponse.
  // Note: Subclasses should have responsibility to run some callback
  // in this method to notify the finish status to its clients (or ignore it
  // under its responsibility).
  virtual void OnRangeRequestComplete(const UploadRangeResponse& response,
                                      std::unique_ptr<base::Value> value) = 0;

 private:
  // Called when ParseJson() is completed.
  void OnDataParsed(DriveApiErrorCode code, std::unique_ptr<base::Value> value);

  const GURL upload_url_;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<UploadRangeRequestBase> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(UploadRangeRequestBase);
};

//========================== ResumeUploadRequestBase =========================

// This class performs the request for resuming the upload of a file.
// More specifically, this request uploads a chunk of data carried in |buf|
// of ResumeUploadResponseBase. This class is designed to share the
// implementation of upload resuming between GData WAPI and Drive API v2.
// The subclasses should implement OnRangeRequestComplete inherited by
// UploadRangeRequestBase, because the type of the response should be
// different (although the format in the server response is JSON).
class ResumeUploadRequestBase : public UploadRangeRequestBase {
 protected:
  // |start_position| is the start of range of contents currently stored in
  // |buf|. |end_position| is the end of range of contents currently stared in
  // |buf|. This is exclusive. For instance, if you are to upload the first
  // 500 bytes of data, |start_position| is 0 and |end_position| is 500.
  // |content_length| and |content_type| are the length and type of the
  // file content to be uploaded respectively.
  // |buf| holds current content to be uploaded.
  // See also UploadRangeRequestBase's comment for remaining parameters
  // meaning.
  ResumeUploadRequestBase(RequestSender* sender,
                          const GURL& upload_location,
                          int64_t start_position,
                          int64_t end_position,
                          int64_t content_length,
                          const std::string& content_type,
                          const base::FilePath& local_file_path);
  ~ResumeUploadRequestBase() override;

  // UrlFetchRequestBase overrides.
  std::vector<std::string> GetExtraRequestHeaders() const override;
  bool GetContentFile(base::FilePath* local_file_path,
                      int64_t* range_offset,
                      int64_t* range_length,
                      std::string* upload_content_type) override;

 private:
  // The parameters for the request. See ResumeUploadParams for the details.
  const int64_t start_position_;
  const int64_t end_position_;
  const int64_t content_length_;
  const std::string content_type_;
  const base::FilePath local_file_path_;

  DISALLOW_COPY_AND_ASSIGN(ResumeUploadRequestBase);
};

//======================== GetUploadStatusRequestBase ========================

// This class performs the request for getting the current upload status
// of a file.
// This request calls OnRangeRequestComplete() with:
// - HTTP_RESUME_INCOMPLETE and the range of previously uploaded data,
//   if a file has been partially uploaded. |value| is not used.
// - HTTP_SUCCESS or HTTP_CREATED (up to the upload mode) and |value|
//   for the uploaded data, if a file has been completely uploaded.
// See also UploadRangeRequestBase.
class GetUploadStatusRequestBase : public UploadRangeRequestBase {
 public:
  // |content_length| is the whole data size to be uploaded.
  // See also UploadRangeRequestBase's constructor comment for other
  // parameters.
  GetUploadStatusRequestBase(RequestSender* sender,
                             const GURL& upload_url,
                             int64_t content_length);
  ~GetUploadStatusRequestBase() override;

 protected:
  // UrlFetchRequestBase overrides.
  std::vector<std::string> GetExtraRequestHeaders() const override;

 private:
  const int64_t content_length_;

  DISALLOW_COPY_AND_ASSIGN(GetUploadStatusRequestBase);
};

//=========================== MultipartUploadRequestBase=======================

// This class provides base implementation for performing the request for
// uploading a file by multipart body.
class MultipartUploadRequestBase : public BatchableDelegate {
 public:
  // Set boundary. Only tests can use this method.
  void SetBoundaryForTesting(const std::string& boundary);

 protected:
  // |callback| will be called with the file resource.upload URL.
  // |content_type| and |content_length| should be the attributes of the
  // uploading file. Other parameters are optional and can be empty or null
  // depending on Upload URL provided by the subclasses.
  MultipartUploadRequestBase(base::SequencedTaskRunner* blocking_task_runner,
                             const std::string& metadata_json,
                             const std::string& content_type,
                             int64_t content_length,
                             const base::FilePath& local_file_path,
                             const FileResourceCallback& callback,
                             const ProgressCallback& progress_callback);
  ~MultipartUploadRequestBase() override;

  // BatchableDelegate.
  std::vector<std::string> GetExtraRequestHeaders() const override;
  void Prepare(const PrepareCallback& callback) override;
  bool GetContentData(std::string* upload_content_type,
                      std::string* upload_content) override;
  void NotifyResult(DriveApiErrorCode code,
                    const std::string& body,
                    const base::Closure& callback) override;
  void NotifyError(DriveApiErrorCode code) override;
  void NotifyUploadProgress(const net::URLFetcher* source,
                            int64_t current,
                            int64_t total) override;
  // Parses the response value and invokes |callback_| with |FileResource|.
  void OnDataParsed(DriveApiErrorCode code,
                    const base::Closure& callback,
                    std::unique_ptr<base::Value> value);

 private:
  // Continues to rest part of |Start| method after determining boundary string
  // of multipart/related.
  void OnPrepareUploadContent(const PrepareCallback& callback,
                              std::string* upload_content_type,
                              std::string* upload_content_data,
                              bool result);

  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
  const std::string metadata_json_;
  const std::string content_type_;
  const base::FilePath local_path_;
  const FileResourceCallback callback_;
  const ProgressCallback progress_callback_;

  // Boundary of multipart body.
  std::string boundary_;

  // Upload content of multipart body.
  std::string upload_content_type_;
  std::string upload_content_data_;

  base::ThreadChecker thread_checker_;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<MultipartUploadRequestBase> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(MultipartUploadRequestBase);
};

//============================ DownloadFileRequest ===========================

// Callback type for receiving the completion of DownloadFileRequest.
typedef base::Callback<void(DriveApiErrorCode error,
                            const base::FilePath& temp_file)>
    DownloadActionCallback;

// This is a base class for performing the request for downloading a file.
class DownloadFileRequestBase : public UrlFetchRequestBase {
 public:
  // download_action_callback:
  //   This callback is called when the download is complete. Must not be null.
  //
  // get_content_callback:
  //   This callback is called when some part of the content is
  //   read. Used to read the download content progressively. May be null.
  //
  // progress_callback:
  //   This callback is called for periodically reporting the number of bytes
  //   downloaded so far. May be null.
  //
  // download_url:
  //   Specifies the target file to download.
  //
  // output_file_path:
  //   Specifies the file path to save the downloaded file.
  //
  DownloadFileRequestBase(
      RequestSender* sender,
      const DownloadActionCallback& download_action_callback,
      const GetContentCallback& get_content_callback,
      const ProgressCallback& progress_callback,
      const GURL& download_url,
      const base::FilePath& output_file_path);
  ~DownloadFileRequestBase() override;

 protected:
  // UrlFetchRequestBase overrides.
  GURL GetURL() const override;
  void GetOutputFilePath(base::FilePath* local_file_path,
                         GetContentCallback* get_content_callback) override;
  void ProcessURLFetchResults(const net::URLFetcher* source) override;
  void RunCallbackOnPrematureFailure(DriveApiErrorCode code) override;

  // net::URLFetcherDelegate overrides.
  void OnURLFetchDownloadProgress(const net::URLFetcher* source,
                                  int64_t current,
                                  int64_t total,
                                  int64_t current_network_bytes) override;

 private:
  const DownloadActionCallback download_action_callback_;
  const GetContentCallback get_content_callback_;
  const ProgressCallback progress_callback_;
  const GURL download_url_;
  const base::FilePath output_file_path_;

  DISALLOW_COPY_AND_ASSIGN(DownloadFileRequestBase);
};

}  // namespace google_apis

#endif  // GOOGLE_APIS_DRIVE_BASE_REQUESTS_H_
