text: //
  // stream_socket_service.hpp
  // ~~~~~~~~~~~~~~~~~~~~~~~~~
  //
  // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  //
  // Distributed under the Boost Software License, Version 1.0. (See accompanying
  // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  //
  
  #ifndef SERVICES_STREAM_SOCKET_SERVICE_HPP
  #define SERVICES_STREAM_SOCKET_SERVICE_HPP
  
  #include <asio.hpp>
  #include <boost/noncopyable.hpp>
  #include <boost/lexical_cast.hpp>
  #include "logger.hpp"
  
  namespace services {
  
  /// Debugging stream socket service that wraps the normal stream socket service.
  template <typename Protocol>
  class stream_socket_service
    : public asio::io_context::service
  {
  private:
    /// The type of the wrapped stream socket service.
    typedef asio::stream_socket_service<Protocol> service_impl_type;
  
  public:
    /// The unique service identifier.
    static asio::io_context::id id;
  
    /// The protocol type.
    typedef Protocol protocol_type;
  
    /// The endpoint type.
    typedef typename Protocol::endpoint endpoint_type;
  
    /// The implementation type of a stream socket.
    typedef typename service_impl_type::implementation_type implementation_type;
  
    /// The native type of a stream socket.
    typedef typename service_impl_type::native_handle_type native_handle_type;
  
    /// Construct a new stream socket service for the specified io_context.
    explicit stream_socket_service(asio::io_context& io_context)
      : asio::io_context::service(io_context),
        service_impl_(asio::use_service<service_impl_type>(io_context)),
        logger_(io_context, "stream_socket")
    {
    }
  
    /// Destroy all user-defined handler objects owned by the service.
    void shutdown()
    {
    }
  
    /// Construct a new stream socket implementation.
    void construct(implementation_type& impl)
    {
      service_impl_.construct(impl);
    }
  
    /// Destroy a stream socket implementation.
    void destroy(implementation_type& impl)
    {
      service_impl_.destroy(impl);
    }
  
    /// Open a new stream socket implementation.
    asio::error_code open(implementation_type& impl,
        const protocol_type& protocol, asio::error_code& ec)
    {
      logger_.log("Opening new socket");
      return service_impl_.open(impl, protocol, ec);
    }
  
    /// Open a stream socket from an existing native socket.
    asio::error_code assign(implementation_type& impl,
        const protocol_type& protocol, const native_handle_type& native_socket,
        asio::error_code& ec)
    {
      logger_.log("Assigning from a native socket");
      return service_impl_.assign(impl, protocol, native_socket, ec);
    }
  
    /// Determine whether the socket is open.
    bool is_open(const implementation_type& impl) const
    {
      logger_.log("Checking if socket is open");
      return service_impl_.is_open(impl);
    }
  
    /// Close a stream socket implementation.
    asio::error_code close(implementation_type& impl,
        asio::error_code& ec)
    {
      logger_.log("Closing socket");
      return service_impl_.close(impl, ec);
    }
  
    /// Determine whether the socket is at the out-of-band data mark.
    bool at_mark(const implementation_type& impl,
        asio::error_code& ec) const
    {
      logger_.log("Checking if socket is at out-of-band data mark");
      return service_impl_.at_mark(impl, ec);
    }
  
    /// Determine the number of bytes available for reading.
    std::size_t available(const implementation_type& impl,
        asio::error_code& ec) const
    {
      logger_.log("Determining number of bytes available for reading");
      return service_impl_.available(impl, ec);
    }
  
    /// Bind the stream socket to the specified local endpoint.
    asio::error_code bind(implementation_type& impl,
        const endpoint_type& endpoint, asio::error_code& ec)
    {
      logger_.log("Binding socket");
      return service_impl_.bind(impl, endpoint, ec);
    }
  
    /// Connect the stream socket to the specified endpoint.
    asio::error_code connect(implementation_type& impl,
        const endpoint_type& peer_endpoint, asio::error_code& ec)
    {
      logger_.log("Connecting socket to " +
          boost::lexical_cast<std::string>(peer_endpoint));
      return service_impl_.connect(impl, peer_endpoint, ec);
    }
  
    /// Handler to wrap asynchronous connect completion.
    template <typename Handler>
    class connect_handler
    {
    public:
      connect_handler(Handler h, logger& l)
        : handler_(h),
          logger_(l)
      {
      }
  
      void operator()(const asio::error_code& e)
      {
        if (e)
        {
          std::string msg = "Asynchronous connect failed: ";
          msg += e.message();
          logger_.log(msg);
        }
        else
        {
          logger_.log("Asynchronous connect succeeded");
        }
  
        handler_(e);
      }
  
    private:
      Handler handler_;
      logger& logger_;
    };
  
    /// Start an asynchronous connect.
    template <typename Handler>
    void async_connect(implementation_type& impl,
        const endpoint_type& peer_endpoint, Handler handler)
    {
      logger_.log("Starting asynchronous connect to " +
          boost::lexical_cast<std::string>(peer_endpoint));
      service_impl_.async_connect(impl, peer_endpoint, 
          connect_handler<Handler>(handler, logger_));
    }
  
    /// Set a socket option.
    template <typename Option>
    asio::error_code set_option(implementation_type& impl,
        const Option& option, asio::error_code& ec)
    {
      logger_.log("Setting socket option");
      return service_impl_.set_option(impl, option, ec);
    }
  
    /// Get a socket option.
    template <typename Option>
    asio::error_code get_option(const implementation_type& impl,
        Option& option, asio::error_code& ec) const
    {
      logger_.log("Getting socket option");
      return service_impl_.get_option(impl, option, ec);
    }
  
    /// Perform an IO control command on the socket.
    template <typename IO_Control_Command>
    asio::error_code io_control(implementation_type& impl,
        IO_Control_Command& command, asio::error_code& ec)
    {
      logger_.log("Performing IO control command on socket");
      return service_impl_.io_control(impl, command, ec);
    }
  
    /// Get the local endpoint.
    endpoint_type local_endpoint(const implementation_type& impl,
        asio::error_code& ec) const
    {
      logger_.log("Getting socket's local endpoint");
      return service_impl_.local_endpoint(impl, ec);
    }
  
    /// Get the remote endpoint.
    endpoint_type remote_endpoint(const implementation_type& impl,
        asio::error_code& ec) const
    {
      logger_.log("Getting socket's remote endpoint");
      return service_impl_.remote_endpoint(impl, ec);
    }
  
    /// Disable sends or receives on the socket.
    asio::error_code shutdown(implementation_type& impl,
        asio::socket_base::shutdown_type what,
        asio::error_code& ec)
    {
      logger_.log("Shutting down socket");
      return service_impl_.shutdown(impl, what, ec);
    }
  
    /// Send the given data to the peer.
    template <typename Const_Buffers>
    std::size_t send(implementation_type& impl, const Const_Buffers& buffers,
        asio::socket_base::message_flags flags,
        asio::error_code& ec)
    {
      logger_.log("Sending data on socket");
      return service_impl_.send(impl, buffers, flags, ec);
    }
  
    /// Handler to wrap asynchronous send completion.
    template <typename Handler>
    class send_handler
    {
    public:
      send_handler(Handler h, logger& l)
        : handler_(h),
          logger_(l)
      {
      }
  
      void operator()(const asio::error_code& e,
          std::size_t bytes_transferred)
      {
        if (e)
        {
          std::string msg = "Asynchronous send failed: ";
          msg += e.message();
          logger_.log(msg);
        }
        else
        {
          logger_.log("Asynchronous send succeeded");
        }
  
        handler_(e, bytes_transferred);
      }
  
    private:
      Handler handler_;
      logger& logger_;
    };
  
    /// Start an asynchronous send.
    template <typename Const_Buffers, typename Handler>
    void async_send(implementation_type& impl, const Const_Buffers& buffers,
        asio::socket_base::message_flags flags, Handler handler)
    {
      logger_.log("Starting asynchronous send");
      service_impl_.async_send(impl, buffers, flags,
          send_handler<Handler>(handler, logger_));
    }
  
    /// Receive some data from the peer.
    template <typename Mutable_Buffers>
    std::size_t receive(implementation_type& impl,
        const Mutable_Buffers& buffers,
        asio::socket_base::message_flags flags,
        asio::error_code& ec)
    {
      logger_.log("Receiving data on socket");
      return service_impl_.receive(impl, buffers, flags, ec);
    }
  
    /// Handler to wrap asynchronous receive completion.
    template <typename Handler>
    class receive_handler
    {
    public:
      receive_handler(Handler h, logger& l)
        : handler_(h),
          logger_(l)
      {
      }
  
      void operator()(const asio::error_code& e,
          std::size_t bytes_transferred)
      {
        if (e)
        {
          std::string msg = "Asynchronous receive failed: ";
          msg += e.message();
          logger_.log(msg);
        }
        else
        {
          logger_.log("Asynchronous receive succeeded");
        }
  
        handler_(e, bytes_transferred);
      }
  
    private:
      Handler handler_;
      logger& logger_;
    };
  
    /// Start an asynchronous receive.
    template <typename Mutable_Buffers, typename Handler>
    void async_receive(implementation_type& impl, const Mutable_Buffers& buffers,
        asio::socket_base::message_flags flags, Handler handler)
    {
      logger_.log("Starting asynchronous receive");
      service_impl_.async_receive(impl, buffers, flags,
          receive_handler<Handler>(handler, logger_));
    }
  
  private:
    /// The wrapped stream socket service.
    service_impl_type& service_impl_;
  
    /// The logger used for writing debug messages.
    mutable logger logger_;
  };
  
  template <typename Protocol>
  asio::io_context::id stream_socket_service<Protocol>::id;
  
  } // namespace services
  
  #endif // SERVICES_STREAM_SOCKET_SERVICE_HPP
  