chromium/components/winhttp/network_fetcher.h

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_WINHTTP_NETWORK_FETCHER_H_
#define COMPONENTS_WINHTTP_NETWORK_FETCHER_H_

#include <windows.h>

#include <stdint.h>

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "components/winhttp/proxy_configuration.h"
#include "components/winhttp/scoped_hinternet.h"
#include "components/winhttp/scoped_winttp_proxy_info.h"
#include "url/gurl.h"

namespace base {
class SequencedTaskRunner;
}

namespace winhttp {

// Implements a network fetcher in terms of WinHTTP. The class is ref-counted
// as it is accessed from the main sequence and the worker threads in WinHTTP.
class NetworkFetcher : public base::RefCountedThreadSafe<NetworkFetcher> {
 public:
  using FetchCompleteCallback = base::OnceCallback<void(int response_code)>;
  using FetchStartedCallback =
      base::OnceCallback<void(int response_code, int64_t content_length)>;
  using FetchProgressCallback = base::RepeatingCallback<void(int64_t current)>;

  NetworkFetcher(scoped_refptr<SharedHInternet> session_handle,
                 scoped_refptr<ProxyConfiguration> proxy_configuration);
  NetworkFetcher(const NetworkFetcher&) = delete;
  NetworkFetcher& operator=(const NetworkFetcher&) = delete;

  void Close();

  void PostRequest(
      const GURL& url,
      const std::string& post_data,
      const std::string& content_type,
      const base::flat_map<std::string, std::string>& post_additional_headers,
      FetchStartedCallback fetch_started_callback,
      FetchProgressCallback fetch_progress_callback,
      FetchCompleteCallback fetch_complete_callback);

  // Downloads the content of the |url| to a file identified by |file_path|.
  // The content is written to the file as it is being retrieved from the
  // network. Returns a closure that can be run to cancel the download.
  base::OnceClosure DownloadToFile(
      const GURL& url,
      const base::FilePath& file_path,
      FetchStartedCallback fetch_started_callback,
      FetchProgressCallback fetch_progress_callback,
      FetchCompleteCallback fetch_complete_callback);

  HRESULT QueryHeaderString(const std::wstring& name,
                            std::wstring* value) const;
  HRESULT QueryHeaderInt(const std::wstring& name, int* value) const;
  std::string GetResponseBody() const;
  HRESULT GetNetError() const;
  base::FilePath GetFilePath() const;

  // Returns the number of bytes retrieved from the network. This may be
  // different than the content length if an error occurred.
  int64_t GetContentSize() const;

 private:
  friend class base::RefCountedThreadSafe<NetworkFetcher>;
  using WriteDataCallback = base::RepeatingCallback<void()>;

  ~NetworkFetcher();

  static void __stdcall WinHttpStatusCallback(HINTERNET handle,
                                              DWORD_PTR context,
                                              DWORD status,
                                              void* info,
                                              DWORD info_len);

  // Invoked by the last WinHTTPstatus status callback.
  void HandleClosing();

  DWORD_PTR context() const { return reinterpret_cast<DWORD_PTR>(this); }

  HRESULT BeginFetch(
      const std::string& data,
      const base::flat_map<std::string, std::string>& additional_headers);
  std::optional<ScopedWinHttpProxyInfo> GetProxyForUrl();
  void ContinueFetch(
      const std::string& data,
      base::flat_map<std::string, std::string> additional_headers,
      std::optional<ScopedWinHttpProxyInfo> winhttp_proxy_info);

  ScopedHInternet Connect();
  ScopedHInternet OpenRequest();
  HRESULT SendRequest(const std::string& data);
  void SendRequestComplete();
  HRESULT ReceiveResponse();
  void HeadersAvailable();
  HRESULT ReadData();
  void ReadDataComplete(size_t num_bytes_read);
  void RequestError(DWORD error);
  void CompleteFetch();

  void WriteDataToMemory();
  void WriteDataToFile();
  bool WriteDataToFileBlocking();
  void WriteDataToFileComplete(bool is_eof);

  SEQUENCE_CHECKER(sequence_checker_);
  scoped_refptr<base::SequencedTaskRunner> main_task_runner_;

  scoped_refptr<SharedHInternet> session_handle_;
  scoped_refptr<ProxyConfiguration> proxy_configuration_;
  ScopedHInternet connect_handle_;
  ScopedHInternet request_handle_;

  // Keeps an outstanding reference count on itself as long as there is a
  // valid request handle and the context for the handle is set to this
  // instance.
  scoped_refptr<NetworkFetcher> self_;

  GURL url_;
  bool is_https_ = false;
  std::string host_;
  int port_ = 0;
  std::string path_for_request_;

  std::wstring_view verb_;
  std::string request_data_;
  // The value of Content-Type header, e.g. "application/json".
  std::string content_type_;
  WriteDataCallback write_data_callback_;
  HRESULT net_error_ = S_OK;
  std::vector<char> read_buffer_;
  int response_code_ = 0;
  std::string post_response_body_;
  base::FilePath file_path_;
  base::File file_;
  int64_t content_size_ = 0;

  FetchStartedCallback fetch_started_callback_;
  FetchProgressCallback fetch_progress_callback_;
  FetchCompleteCallback fetch_complete_callback_;
};

}  // namespace winhttp

#endif  // COMPONENTS_WINHTTP_NETWORK_FETCHER_H_