text: // Copyright 2021 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_DRIVE_BASE_REQUESTS_H_
  #define GOOGLE_APIS_DRIVE_DRIVE_BASE_REQUESTS_H_
  
  #include <stdint.h>
  
  #include <memory>
  #include <string>
  #include <utility>
  #include <vector>
  
  #include "base/callback.h"
  #include "base/files/file_path.h"
  #include "base/macros.h"
  #include "base/memory/weak_ptr.h"
  #include "base/task/sequenced_task_runner.h"
  #include "base/threading/thread_checker.h"
  #include "google_apis/common/api_error_codes.h"
  #include "google_apis/common/base_requests.h"
  #include "services/network/public/cpp/simple_url_loader.h"
  #include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
  #include "services/network/public/mojom/url_response_head.mojom-forward.h"
  #include "url/gurl.h"
  
  namespace base {
  class Value;
  }  // namespace base
  
  namespace google_apis {
  
  class FileResource;
  class RequestSender;
  
  // Content type for multipart body.
  enum class MultipartType { kRelated, kMixed };
  
  // Pair of content type and data.
  struct ContentTypeAndData {
    std::string type;
    std::string data;
  };
  
  // Callback used for requests that the server returns FileResource data
  // formatted into JSON value.
  typedef base::OnceCallback<void(ApiErrorCode error,
                                  std::unique_ptr<FileResource> entry)>
      FileResourceCallback;
  
  // 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);
  
  //============================ DriveUrlFetchRequestBase =======================
  
  // Base class for Drive requests that are fetching URLs.
  class DriveUrlFetchRequestBase : public UrlFetchRequestBase {
   public:
    DriveUrlFetchRequestBase(const DriveUrlFetchRequestBase&) = delete;
    DriveUrlFetchRequestBase& operator=(const DriveUrlFetchRequestBase&) = delete;
  
   protected:
    DriveUrlFetchRequestBase(RequestSender* sender,
                             ProgressCallback upload_progress_callback,
                             ProgressCallback download_progress_callback);
    ~DriveUrlFetchRequestBase() override;
  
    ApiErrorCode MapReasonToError(ApiErrorCode code,
                                  const std::string& reason) override;
  
    bool IsSuccessfulErrorCode(ApiErrorCode error) override;
  };
  
  //============================ BatchableDelegate ============================
  
  // Delegate to be used by |SingleBatchableDelegateRequest| and
  // |BatchUploadRequest|.
  class BatchableDelegate {
   public:
    virtual ~BatchableDelegate() = default;
  
    // See UrlFetchRequestBase.
    virtual GURL GetURL() const = 0;
    virtual std::string GetRequestType() const = 0;
    virtual std::vector<std::string> GetExtraRequestHeaders() const = 0;
    virtual void Prepare(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(ApiErrorCode code,
                              const std::string& response_body,
                              base::OnceClosure callback) = 0;
  
    // Notifies error. Unlike |NotifyResult|, it must report error
    // synchronously. The instance may be deleted just after calling
    // NotifyError.
    virtual void NotifyError(ApiErrorCode code) = 0;
  
    // Notifies progress.
    virtual void NotifyUploadProgress(int64_t current, int64_t total) = 0;
  };
  
  //============================ EntryActionRequest ============================
  
  // Callback type for requests that return only error status, like: Delete/Move.
  using EntryActionCallback = base::OnceCallback<void(ApiErrorCode error)>;
  
  // 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 DriveUrlFetchRequestBase {
   public:
    // |callback| is called when the request is finished either by success or by
    // failure. It must not be null.
    EntryActionRequest(RequestSender* sender, EntryActionCallback callback);
    EntryActionRequest(const EntryActionRequest&) = delete;
    EntryActionRequest& operator=(const EntryActionRequest&) = delete;
    ~EntryActionRequest() override;
  
   protected:
    // Overridden from UrlFetchRequestBase.
    void ProcessURLFetchResults(
        const network::mojom::URLResponseHead* response_head,
        base::FilePath response_file,
        std::string response_body) override;
    void RunCallbackOnPrematureFailure(ApiErrorCode code) override;
  
   private:
    EntryActionCallback callback_;
  };
  
  //=========================== InitiateUploadRequestBase=======================
  
  // Callback type for DriveServiceInterface::InitiateUpload.
  typedef base::OnceCallback<void(ApiErrorCode 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 DriveUrlFetchRequestBase {
   public:
    InitiateUploadRequestBase(const InitiateUploadRequestBase&) = delete;
    InitiateUploadRequestBase& operator=(const InitiateUploadRequestBase&) =
        delete;
  
   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,
                              InitiateUploadCallback callback,
                              const std::string& content_type,
                              int64_t content_length);
    ~InitiateUploadRequestBase() override;
  
    // UrlFetchRequestBase overrides.
    void ProcessURLFetchResults(
        const network::mojom::URLResponseHead* response_head,
        base::FilePath response_file,
        std::string response_body) override;
    void RunCallbackOnPrematureFailure(ApiErrorCode code) override;
    std::vector<std::string> GetExtraRequestHeaders() const override;
  
   private:
    InitiateUploadCallback callback_;
    const std::string content_type_;
    const int64_t content_length_;
  };
  
  //========================== UploadRangeRequestBase ==========================
  
  // Struct for response to ResumeUpload and GetUploadStatus.
  struct UploadRangeResponse {
    UploadRangeResponse();
    UploadRangeResponse(ApiErrorCode code,
                        int64_t start_position_received,
                        int64_t end_position_received);
    ~UploadRangeResponse();
  
    ApiErrorCode code = HTTP_SUCCESS;
    // 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 = 0;
    int64_t end_position_received = 0;
  };
  
  // 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 DriveUrlFetchRequestBase {
   public:
    UploadRangeRequestBase(const UploadRangeRequestBase&) = delete;
    UploadRangeRequestBase& operator=(const UploadRangeRequestBase&) = delete;
  
   protected:
    // |upload_url| is the URL of where to upload the file to.
    UploadRangeRequestBase(RequestSender* sender,
                           const GURL& upload_url,
                           ProgressCallback upload_progress_callback);
    ~UploadRangeRequestBase() override;
  
    // UrlFetchRequestBase overrides.
    GURL GetURL() const override;
    std::string GetRequestType() const override;
    void ProcessURLFetchResults(
        const network::mojom::URLResponseHead* response_head,
        base::FilePath response_file,
        std::string response_body) override;
    void RunCallbackOnPrematureFailure(ApiErrorCode 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(ApiErrorCode 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_{this};
  };
  
  //========================== 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 {
   public:
    ResumeUploadRequestBase(const ResumeUploadRequestBase&) = delete;
    ResumeUploadRequestBase& operator=(const ResumeUploadRequestBase&) = delete;
  
   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,
                            ProgressCallback progress_callback);
    ~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_;
  };
  
  //======================== 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(const GetUploadStatusRequestBase&) = delete;
    GetUploadStatusRequestBase& operator=(const GetUploadStatusRequestBase&) =
        delete;
    ~GetUploadStatusRequestBase() override;
  
   protected:
    // UrlFetchRequestBase overrides.
    std::vector<std::string> GetExtraRequestHeaders() const override;
  
   private:
    const int64_t content_length_;
  };
  
  //=========================== MultipartUploadRequestBase=======================
  
  // This class provides base implementation for performing the request for
  // uploading a file by multipart body.
  class MultipartUploadRequestBase : public BatchableDelegate {
   public:
    MultipartUploadRequestBase(const MultipartUploadRequestBase&) = delete;
    MultipartUploadRequestBase& operator=(const MultipartUploadRequestBase&) =
        delete;
  
    // 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,
                               FileResourceCallback callback,
                               ProgressCallback progress_callback);
    ~MultipartUploadRequestBase() override;
  
    // BatchableDelegate.
    std::vector<std::string> GetExtraRequestHeaders() const override;
    void Prepare(PrepareCallback callback) override;
    bool GetContentData(std::string* upload_content_type,
                        std::string* upload_content) override;
    void NotifyResult(ApiErrorCode code,
                      const std::string& body,
                      base::OnceClosure callback) override;
    void NotifyError(ApiErrorCode code) override;
    void NotifyUploadProgress(int64_t current, int64_t total) override;
    // Parses the response value and invokes |callback_| with |FileResource|.
    void OnDataParsed(ApiErrorCode code,
                      base::OnceClosure callback,
                      std::unique_ptr<base::Value> value);
  
   private:
    // Continues to rest part of |Start| method after determining boundary string
    // of multipart/related.
    void OnPrepareUploadContent(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_;
    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_;
  
    THREAD_CHECKER(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_{this};
  };
  
  //============================ DownloadFileRequest ===========================
  
  // Callback type for receiving the completion of DownloadFileRequest.
  typedef base::OnceCallback<void(ApiErrorCode error,
                                  const base::FilePath& temp_file)>
      DownloadActionCallback;
  
  // This is a base class for performing the request for downloading a file.
  class DownloadFileRequestBase : public DriveUrlFetchRequestBase {
   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,
                            DownloadActionCallback download_action_callback,
                            const GetContentCallback& get_content_callback,
                            ProgressCallback progress_callback,
                            const GURL& download_url,
                            const base::FilePath& output_file_path);
    DownloadFileRequestBase(const DownloadFileRequestBase&) = delete;
    DownloadFileRequestBase& operator=(const DownloadFileRequestBase&) = delete;
    ~DownloadFileRequestBase() override;
  
   protected:
    // UrlFetchRequestBase overrides.
    GURL GetURL() const override;
    void GetOutputFilePath(base::FilePath* local_file_path,
                           GetContentCallback* get_content_callback) override;
    void ProcessURLFetchResults(
        const network::mojom::URLResponseHead* response_head,
        base::FilePath response_file,
        std::string response_body) override;
    void RunCallbackOnPrematureFailure(ApiErrorCode code) override;
  
   private:
    DownloadActionCallback download_action_callback_;
    const GetContentCallback get_content_callback_;
    const GURL download_url_;
    const base::FilePath output_file_path_;
  };
  
  }  // namespace google_apis
  
  #endif  // GOOGLE_APIS_DRIVE_DRIVE_BASE_REQUESTS_H_
  