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

#ifndef NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_H_
#define NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_H_

#include <stdint.h>

#include <memory>
#include <string>

#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "net/base/completion_once_callback.h"
#include "net/base/completion_repeating_callback.h"
#include "net/base/host_port_pair.h"
#include "net/base/net_export.h"
#include "net/base/proxy_chain.h"
#include "net/http/http_auth_controller.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_info.h"
#include "net/http/proxy_client_socket.h"
#include "net/log/net_log_with_source.h"
#include "net/socket/ssl_client_socket.h"
#include "net/traffic_annotation/network_traffic_annotation.h"

namespace net {

class GrowableIOBuffer;
class HttpStreamParser;
class IOBuffer;
class ProxyDelegate;
class StreamSocket;

// Tunnels a stream socket over an HTTP/1.1 connection.
//
// This class handles only _tunneled_ proxy connections, using `CONNECT`.
// Un-tunneled proxy requests are handled in the HTTP stream layer,
// specifically in `HttpBasicStream`.
class NET_EXPORT_PRIVATE HttpProxyClientSocket : public ProxyClientSocket {
 public:
  // Takes ownership of |socket|, which should already be connected by the time
  // Connect() is called. |socket| is assumed to be a fresh socket.
  HttpProxyClientSocket(std::unique_ptr<StreamSocket> socket,
                        const std::string& user_agent,
                        const HostPortPair& endpoint,
                        const ProxyChain& proxy_chain,
                        size_t proxy_chain_index,
                        scoped_refptr<HttpAuthController> http_auth_controller,
                        ProxyDelegate* proxy_delegate,
                        const NetworkTrafficAnnotationTag& traffic_annotation);

  HttpProxyClientSocket(const HttpProxyClientSocket&) = delete;
  HttpProxyClientSocket& operator=(const HttpProxyClientSocket&) = delete;

  // On destruction Disconnect() is called.
  ~HttpProxyClientSocket() override;

  // ProxyClientSocket implementation.
  const HttpResponseInfo* GetConnectResponseInfo() const override;
  int RestartWithAuth(CompletionOnceCallback callback) override;
  const scoped_refptr<HttpAuthController>& GetAuthController() const override;

  // StreamSocket implementation.
  int Connect(CompletionOnceCallback callback) override;
  void Disconnect() override;
  bool IsConnected() const override;
  bool IsConnectedAndIdle() const override;
  const NetLogWithSource& NetLog() const override;
  bool WasEverUsed() const override;
  NextProto GetNegotiatedProtocol() const override;
  bool GetSSLInfo(SSLInfo* ssl_info) override;
  int64_t GetTotalReceivedBytes() const override;
  void ApplySocketTag(const SocketTag& tag) override;

  // Socket implementation.
  int Read(IOBuffer* buf,
           int buf_len,
           CompletionOnceCallback callback) override;
  int ReadIfReady(IOBuffer* buf,
                  int buf_len,
                  CompletionOnceCallback callback) override;
  int CancelReadIfReady() override;
  int Write(IOBuffer* buf,
            int buf_len,
            CompletionOnceCallback callback,
            const NetworkTrafficAnnotationTag& traffic_annotation) override;
  int SetReceiveBufferSize(int32_t size) override;
  int SetSendBufferSize(int32_t size) override;
  int GetPeerAddress(IPEndPoint* address) const override;
  int GetLocalAddress(IPEndPoint* address) const override;

 private:
  enum State {
    STATE_NONE,
    STATE_GENERATE_AUTH_TOKEN,
    STATE_GENERATE_AUTH_TOKEN_COMPLETE,
    STATE_SEND_REQUEST,
    STATE_SEND_REQUEST_COMPLETE,
    STATE_READ_HEADERS,
    STATE_READ_HEADERS_COMPLETE,
    STATE_DRAIN_BODY,
    STATE_DRAIN_BODY_COMPLETE,
    STATE_DONE,
  };

  // The size in bytes of the buffer we use to drain the response body that
  // we want to throw away.  The response body is typically a small error
  // page just a few hundred bytes long.
  static const int kDrainBodyBufferSize = 1024;

  int PrepareForAuthRestart();
  int DidDrainBodyForAuthRestart();

  void DoCallback(int result);
  void OnIOComplete(int result);

  int DoLoop(int last_io_result);
  int DoGenerateAuthToken();
  int DoGenerateAuthTokenComplete(int result);
  int DoSendRequest();
  int DoSendRequestComplete(int result);
  int DoReadHeaders();
  int DoReadHeadersComplete(int result);
  int DoDrainBody();
  int DoDrainBodyComplete(int result);

  // Returns whether |next_state_| is STATE_DONE.
  bool CheckDone();

  CompletionRepeatingCallback io_callback_;
  State next_state_ = STATE_NONE;

  // Stores the callback provided by the caller of async operations.
  CompletionOnceCallback user_callback_;

  HttpRequestInfo request_;
  HttpResponseInfo response_;

  scoped_refptr<GrowableIOBuffer> parser_buf_;
  scoped_refptr<IOBuffer> drain_buf_;
  std::unique_ptr<StreamSocket> socket_;
  std::unique_ptr<HttpStreamParser> http_stream_parser_;

  // Whether or not |socket_| has been previously used. Once auth credentials
  // are sent, set to true.
  bool is_reused_ = false;

  // The hostname and port of the endpoint.  This is not necessarily the one
  // specified by the URL, due to Alternate-Protocol or fixed testing ports.
  const HostPortPair endpoint_;
  scoped_refptr<HttpAuthController> auth_;

  std::string request_line_;
  HttpRequestHeaders request_headers_;

  const ProxyChain proxy_chain_;
  const size_t proxy_chain_index_;

  // This delegate must outlive this proxy client socket.
  raw_ptr<ProxyDelegate> proxy_delegate_;

  // Network traffic annotation for handshaking and setup.
  const NetworkTrafficAnnotationTag traffic_annotation_;

  const NetLogWithSource net_log_;
};

}  // namespace net

#endif  // NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_H_
