// Copyright 2018 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.

#ifndef SERVICES_NETWORK_SOCKET_DATA_PUMP_H_
#define SERVICES_NETWORK_SOCKET_DATA_PUMP_H_

#include <memory>

#include "base/component_export.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "mojo/public/cpp/system/simple_watcher.h"
#include "net/base/address_family.h"
#include "net/base/completion_callback.h"
#include "net/base/ip_endpoint.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/net_adapters.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/tcp_socket.mojom.h"

namespace net {
class StreamSocket;
}  // namespace net

namespace network {

// This class handles reading/writing logic between a network socket and a mojo
// pipe. Specifically, it (1) reads from the network socket and writes to a mojo
// producer pipe, and (2) reads from a mojo consumer pipe and writes to the
// network socket. On network read/write errors, it (3) also notifies the
// mojom::SocketObserverPtr appropriately.
class COMPONENT_EXPORT(NETWORK_SERVICE) SocketDataPump {
 public:
  // Interface to notify a consumer that about network errors and whether both
  // data pipes have been shut down from the client side.
  class Delegate {
   public:
    Delegate() {}
    ~Delegate() {}

    // Called when SocketDataPump detects a network read error.
    virtual void OnNetworkReadError(int net_error) = 0;

    // Called when SocketDataPump detects a network write error.
    virtual void OnNetworkWriteError(int net_error) = 0;

    // Called when SocketDataPump detects both send and receive pipes have shut
    // down.
    virtual void OnShutdown() = 0;
  };

  // Constructs a data pump that pumps data between |socket| and mojo data
  // pipe handles. Data are read from |send_pipe_handle| and sent to |socket|.
  // Data are read from |socket| and written to |receive_pipe_handle|.
  // |traffic_annotation| is attached to all writes to |socket|. Note that
  // |socket| must outlive |this|.
  SocketDataPump(net::StreamSocket* socket,
                 Delegate* delegate,
                 mojo::ScopedDataPipeProducerHandle receive_pipe_handle,
                 mojo::ScopedDataPipeConsumerHandle send_pipe_handle,
                 const net::NetworkTrafficAnnotationTag& traffic_annotation);
  ~SocketDataPump();

 private:
  // Maybe notifies |delegate_| of the shutdown if both pipes are closed.
  void MaybeNotifyDelegate();

  // "Receiving" in this context means reading from |socket_| and writing to
  // the Mojo |receive_stream_|.
  void ReceiveMore();
  void OnReceiveStreamWritable(MojoResult result);
  void OnReceiveStreamClosed(MojoResult result);
  void OnNetworkReadIfReadyCompleted(int result);
  void ShutdownReceive();

  // "Writing" is reading from the Mojo |send_stream_| and writing to the
  // |socket_|.
  void SendMore();
  void OnSendStreamReadable(MojoResult result);
  void OnNetworkWriteCompleted(int result);
  void ShutdownSend();

  net::StreamSocket* const socket_;
  Delegate* const delegate_;

  // The *stream handles will be null when there's a pending read from |socket_|
  // to |pending_receive_buffer_|, or while there is a pending write from
  // |pending_send_buffer_| to |socket_|. During this time, the handles will
  // be owned by the *PendingBuffer.
  //
  // The watchers need to be declared after the corresponding handle, so they
  // can be cleaned up before the handles are.

  // For reading from the network and writing to Mojo pipe.
  mojo::ScopedDataPipeProducerHandle receive_stream_;
  mojo::SimpleWatcher receive_stream_watcher_;
  // A separate watcher is needed to observe |receive_stream_|'s close event, so
  // that when the client shuts down their end of the pipe for
  // TCPConnectedSocket::UpgradeToTLS() during the waiting of the async callback
  // of ReadIfReady, |this| can be notified of the shutdown.
  mojo::SimpleWatcher receive_stream_close_watcher_;
  bool read_if_ready_pending_ = false;

  // For reading from the Mojo pipe and writing to the network.
  mojo::ScopedDataPipeConsumerHandle send_stream_;
  scoped_refptr<MojoToNetPendingBuffer> pending_send_buffer_;
  mojo::SimpleWatcher send_stream_watcher_;

  const net::NetworkTrafficAnnotationTag traffic_annotation_;

  base::WeakPtrFactory<SocketDataPump> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(SocketDataPump);
};

}  // namespace network

#endif  // SERVICES_NETWORK_SOCKET_DATA_PUMP_H_
