// Copyright 2018 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_WEBSOCKETS_WEBSOCKET_BASIC_STREAM_ADAPTERS_H_
#define NET_WEBSOCKETS_WEBSOCKET_BASIC_STREAM_ADAPTERS_H_

#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "net/base/completion_once_callback.h"
#include "net/base/net_export.h"
#include "net/spdy/spdy_read_queue.h"
#include "net/spdy/spdy_stream.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/websockets/websocket_basic_stream.h"
#include "net/websockets/websocket_quic_spdy_stream.h"

namespace net {

class ClientSocketHandle;
class IOBuffer;
class SpdyBuffer;

// Trivial adapter to make WebSocketBasicStream use a TCP/IP or TLS socket.
class NET_EXPORT_PRIVATE WebSocketClientSocketHandleAdapter
    : public WebSocketBasicStream::Adapter {
 public:
  WebSocketClientSocketHandleAdapter() = delete;
  explicit WebSocketClientSocketHandleAdapter(
      std::unique_ptr<ClientSocketHandle> connection);
  ~WebSocketClientSocketHandleAdapter() override;

  int Read(IOBuffer* buf,
           int buf_len,
           CompletionOnceCallback callback) override;
  int Write(IOBuffer* buf,
            int buf_len,
            CompletionOnceCallback callback,
            const NetworkTrafficAnnotationTag& traffic_annotation) override;
  void Disconnect() override;
  bool is_initialized() const override;

 private:
  std::unique_ptr<ClientSocketHandle> connection_;
};

// Adapter to make WebSocketBasicStream use an HTTP/2 stream.
// Sets itself as a delegate of the SpdyStream, and forwards headers-related
// methods to WebSocketHttp2HandshakeStream, which implements
// WebSocketSpdyStreamAdapter::Delegate.  After the handshake, ownership of this
// object can be passed to WebSocketBasicStream, which can read and write using
// a ClientSocketHandle-like interface.
class NET_EXPORT_PRIVATE WebSocketSpdyStreamAdapter
    : public WebSocketBasicStream::Adapter,
      public SpdyStream::Delegate {
 public:
  // Interface for forwarding SpdyStream::Delegate methods necessary for the
  // handshake.
  class Delegate {
   public:
    virtual ~Delegate() = default;
    virtual void OnHeadersSent() = 0;
    virtual void OnHeadersReceived(
        const spdy::Http2HeaderBlock& response_headers) = 0;
    // Might destroy |this|.
    virtual void OnClose(int status) = 0;
  };

  // |delegate| must be valid until DetachDelegate() is called.
  WebSocketSpdyStreamAdapter(base::WeakPtr<SpdyStream> stream,
                             Delegate* delegate,
                             NetLogWithSource net_log);
  ~WebSocketSpdyStreamAdapter() override;

  // Called by WebSocketSpdyStreamAdapter::Delegate before it is destroyed.
  void DetachDelegate();

  // WebSocketBasicStream::Adapter methods.

  int Read(IOBuffer* buf,
           int buf_len,
           CompletionOnceCallback callback) override;

  // Write() must not be called before Delegate::OnHeadersSent() is called.
  // Write() always returns asynchronously.
  int Write(IOBuffer* buf,
            int buf_len,
            CompletionOnceCallback callback,
            const NetworkTrafficAnnotationTag& traffic_annotation) override;

  void Disconnect() override;
  bool is_initialized() const override;

  // SpdyStream::Delegate methods.

  void OnHeadersSent() override;
  void OnEarlyHintsReceived(const spdy::Http2HeaderBlock& headers) override;
  void OnHeadersReceived(
      const spdy::Http2HeaderBlock& response_headers,
      const spdy::Http2HeaderBlock* pushed_request_headers) override;
  void OnDataReceived(std::unique_ptr<SpdyBuffer> buffer) override;
  void OnDataSent() override;
  void OnTrailers(const spdy::Http2HeaderBlock& trailers) override;
  void OnClose(int status) override;
  bool CanGreaseFrameType() const override;
  NetLogSource source_dependency() const override;

 private:
  // Copy data from read_data_ to read_buffer_.
  int CopySavedReadDataIntoBuffer();

  // Call WebSocketSpdyStreamAdapter::Delegate::OnClose().
  void CallDelegateOnClose();

  // True if SpdyStream::Delegate::OnHeadersSent() has been called.
  // SpdyStream::SendData() must not be called before that.
  bool headers_sent_ = false;

  // The underlying SpdyStream.
  base::WeakPtr<SpdyStream> stream_;

  // The error code with which SpdyStream was closed.
  int stream_error_ = ERR_CONNECTION_CLOSED;

  raw_ptr<Delegate> delegate_;

  // Buffer data pushed by SpdyStream until read through Read().
  SpdyReadQueue read_data_;

  // Read buffer and length used for both synchronous and asynchronous
  // read operations.
  raw_ptr<IOBuffer, DanglingUntriaged> read_buffer_ = nullptr;
  size_t read_length_ = 0u;

  // Read callback saved for asynchronous reads.
  // Whenever |read_data_| is not empty, |read_callback_| must be null.
  CompletionOnceCallback read_callback_;

  // Write length saved to be passed to |write_callback_|.  This is necessary
  // because SpdyStream::Delegate::OnDataSent() does not pass number of bytes
  // written.
  int write_length_ = 0;

  // Write callback saved for asynchronous writes (all writes are asynchronous).
  CompletionOnceCallback write_callback_;

  NetLogWithSource net_log_;

  base::WeakPtrFactory<WebSocketSpdyStreamAdapter> weak_factory_{this};
};

// Adapter to make WebSocketBasicStream use an HTTP/3 stream.
// Sets itself as a delegate of the WebSocketQuicSpdyStream. Forwards
// headers-related methods to Delegate.
class NET_EXPORT_PRIVATE WebSocketQuicStreamAdapter
    : public WebSocketBasicStream::Adapter,
      public WebSocketQuicSpdyStream::Delegate {
 public:
  // The Delegate interface is implemented by WebSocketHttp3HandshakeStream the
  // user of the WebSocketQuicStreamAdapter to receive events related to the
  // lifecycle of the Adapter.
  class Delegate {
   public:
    virtual ~Delegate() = default;
    virtual void OnHeadersSent() = 0;
    virtual void OnHeadersReceived(
        const spdy::Http2HeaderBlock& response_headers) = 0;
    virtual void OnClose(int status) = 0;
  };

  explicit WebSocketQuicStreamAdapter(
      WebSocketQuicSpdyStream* websocket_quic_spdy_stream,
      Delegate* delegate);

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

  ~WebSocketQuicStreamAdapter() override;

  // Called by WebSocketQuicStreamAdapter::Delegate before it is destroyed.
  void clear_delegate() { delegate_ = nullptr; }

  size_t WriteHeaders(spdy::Http2HeaderBlock header_block, bool fin);

  // WebSocketBasicStream::Adapter methods.
  // TODO(momoka): Add functions that are needed to implement
  // WebSocketHttp3HandshakeStream.
  int Read(IOBuffer* buf,
           int buf_len,
           CompletionOnceCallback callback) override;
  int Write(IOBuffer* buf,
            int buf_len,
            CompletionOnceCallback callback,
            const NetworkTrafficAnnotationTag& traffic_annotation) override;
  void Disconnect() override;
  bool is_initialized() const override;

  // WebSocketQuicSpdyStream::Delegate methods.
  void OnInitialHeadersComplete(
      bool fin,
      size_t frame_len,
      const quic::QuicHeaderList& header_list) override;
  void OnBodyAvailable() override;
  void ClearStream() override;

 private:
  //  `websocket_quic_spdy_stream_` notifies this object of its destruction,
  //  because they may be destroyed in any order.
  raw_ptr<WebSocketQuicSpdyStream> websocket_quic_spdy_stream_;

  raw_ptr<Delegate> delegate_;

  // Read buffer, length and callback used for asynchronous read operations.
  raw_ptr<IOBuffer> read_buffer_ = nullptr;
  int read_length_ = 0u;
  CompletionOnceCallback read_callback_;
};

}  // namespace net

#endif  // NET_WEBSOCKETS_WEBSOCKET_BASIC_STREAM_ADAPTERS_H_
