text: /*
   * Copyright (c) 2013, Peter Thorson. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *     * Redistributions of source code must retain the above copyright
   *       notice, this list of conditions and the following disclaimer.
   *     * Redistributions in binary form must reproduce the above copyright
   *       notice, this list of conditions and the following disclaimer in the
   *       documentation and/or other materials provided with the distribution.
   *     * Neither the name of the WebSocket++ Project nor the
   *       names of its contributors may be used to endorse or promote products
   *       derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
   * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   */
  
  #ifndef WEBSOCKETPP_ENDPOINT_HPP
  #define WEBSOCKETPP_ENDPOINT_HPP
  
  #include <websocketpp/connection.hpp>
  #include <websocketpp/logger/levels.hpp>
  #include <websocketpp/version.hpp>
  
  #include <iostream>
  #include <set>
  
  namespace websocketpp {
  
  /// Creates and manages connections associated with a WebSocket endpoint
  template <typename connection, typename config>
  class endpoint : public config::transport_type, public config::endpoint_base {
  public:
      // Import appropriate types from our helper class
      // See endpoint_types for more details.
      typedef endpoint<connection,config> type;
  
      /// Type of the transport component of this endpoint
      typedef typename config::transport_type transport_type;
      /// Type of the concurrency component of this endpoint
      typedef typename config::concurrency_type concurrency_type;
  
      /// Type of the connections that this endpoint creates
      typedef connection connection_type;
      /// Shared pointer to connection_type
      typedef typename connection_type::ptr connection_ptr;
      /// Weak pointer to connection type
      typedef typename connection_type::weak_ptr connection_weak_ptr;
  
      /// Type of the transport component of the connections that this endpoint
      /// creates
      typedef typename transport_type::transport_con_type transport_con_type;
      /// Type of a shared pointer to the transport component of the connections
      /// that this endpoint creates.
      typedef typename transport_con_type::ptr transport_con_ptr;
  
      /// Type of message_handler
      typedef typename connection_type::message_handler message_handler;
      /// Type of message pointers that this endpoint uses
      typedef typename connection_type::message_ptr message_ptr;
  
      /// Type of error logger
      typedef typename config::elog_type elog_type;
      /// Type of access logger
      typedef typename config::alog_type alog_type;
  
      /// Type of our concurrency policy's scoped lock object
      typedef typename concurrency_type::scoped_lock_type scoped_lock_type;
      /// Type of our concurrency policy's mutex object
      typedef typename concurrency_type::mutex_type mutex_type;
  
      /// Type of RNG
      typedef typename config::rng_type rng_type;
  
      // TODO: organize these
      typedef typename connection_type::termination_handler termination_handler;
  
      typedef lib::shared_ptr<connection_weak_ptr> hdl_type;
  
      explicit endpoint(bool is_server)
        : m_alog(config::alog_level, &std::cout)
        , m_elog(config::elog_level, &std::cerr)
        , m_user_agent(::websocketpp::user_agent)
        , m_open_handshake_timeout_dur(config::timeout_open_handshake)
        , m_close_handshake_timeout_dur(config::timeout_close_handshake)
        , m_pong_timeout_dur(config::timeout_pong)
        , m_is_server(is_server)
      {
          m_alog.set_channels(config::alog_level);
          m_elog.set_channels(config::elog_level);
  
          m_alog.write(log::alevel::devel,"endpoint constructor");
  
          transport_type::init_logging(&m_alog,&m_elog);
      }
  
      /// Returns the user agent string that this endpoint will use
      /**
       * Returns the user agent string that this endpoint will use when creating
       * new connections.
       *
       * The default value for this version is stored in websocketpp::user_agent
       *
       * @return The user agent string.
       */
      std::string get_user_agent() const {
          scoped_lock_type guard(m_mutex);
          return m_user_agent;
      }
  
      /// Sets the user agent string that this endpoint will use
      /**
       * Sets the identifier that this endpoint will use when creating new
       * connections. Changing this value will only affect future connections.
       * For client endpoints this will be sent as the "User-Agent" header in
       * outgoing requests. For server endpoints this will be sent in the "Server"
       * response header.
       *
       * Setting this value to the empty string will suppress the use of the
       * Server and User-Agent headers. This is typically done to hide
       * implementation details for security purposes.
       *
       * For best results set this before accepting or opening connections.
       *
       * The default value for this version is stored in websocketpp::user_agent
       *
       * This can be overridden on an individual connection basis by setting a
       * custom "Server" header during the validate handler or "User-Agent"
       * header on a connection before calling connect().
       *
       * @param ua The string to set the user agent to.
       */
      void set_user_agent(std::string const & ua) {
          scoped_lock_type guard(m_mutex);
          m_user_agent = ua;
      }
  
      /// Returns whether or not this endpoint is a server.
      /**
       * @return Whether or not this endpoint is a server
       */
      bool is_server() const {
          return m_is_server;
      }
  
      /********************************/
      /* Pass-through logging adaptor */
      /********************************/
  
      /// Set Access logging channel
      /**
       * Set the access logger's channel value. The value is a number whose
       * interpretation depends on the logging policy in use.
       *
       * @param channels The channel value(s) to set
       */
      void set_access_channels(log::level channels) {
          m_alog.set_channels(channels);
      }
  
      /// Clear Access logging channels
      /**
       * Clear the access logger's channel value. The value is a number whose
       * interpretation depends on the logging policy in use.
       *
       * @param channels The channel value(s) to clear
       */
      void clear_access_channels(log::level channels) {
          m_alog.clear_channels(channels);
      }
  
      /// Set Error logging channel
      /**
       * Set the error logger's channel value. The value is a number whose
       * interpretation depends on the logging policy in use.
       *
       * @param channels The channel value(s) to set
       */
      void set_error_channels(log::level channels) {
          m_elog.set_channels(channels);
      }
  
      /// Clear Error logging channels
      /**
       * Clear the error logger's channel value. The value is a number whose
       * interpretation depends on the logging policy in use.
       *
       * @param channels The channel value(s) to clear
       */
      void clear_error_channels(log::level channels) {
          m_elog.clear_channels(channels);
      }
  
      /// Get reference to access logger
      /**
       * @return A reference to the access logger
       */
      alog_type & get_alog() {
          return m_alog;
      }
  
      /// Get reference to error logger
      /**
       * @return A reference to the error logger
       */
      elog_type & get_elog() {
          return m_elog;
      }
  
      /*************************/
      /* Set Handler functions */
      /*************************/
  
      void set_open_handler(open_handler h) {
          m_alog.write(log::alevel::devel,"set_open_handler");
          scoped_lock_type guard(m_mutex);
          m_open_handler = h;
      }
      void set_close_handler(close_handler h) {
          m_alog.write(log::alevel::devel,"set_close_handler");
          scoped_lock_type guard(m_mutex);
          m_close_handler = h;
      }
      void set_fail_handler(fail_handler h) {
          m_alog.write(log::alevel::devel,"set_fail_handler");
          scoped_lock_type guard(m_mutex);
          m_fail_handler = h;
      }
      void set_ping_handler(ping_handler h) {
          m_alog.write(log::alevel::devel,"set_ping_handler");
          scoped_lock_type guard(m_mutex);
          m_ping_handler = h;
      }
      void set_pong_handler(pong_handler h) {
          m_alog.write(log::alevel::devel,"set_pong_handler");
          scoped_lock_type guard(m_mutex);
          m_pong_handler = h;
      }
      void set_pong_timeout_handler(pong_timeout_handler h) {
          m_alog.write(log::alevel::devel,"set_pong_timeout_handler");
          scoped_lock_type guard(m_mutex);
          m_pong_timeout_handler = h;
      }
      void set_interrupt_handler(interrupt_handler h) {
          m_alog.write(log::alevel::devel,"set_interrupt_handler");
          scoped_lock_type guard(m_mutex);
          m_interrupt_handler = h;
      }
      void set_http_handler(http_handler h) {
          m_alog.write(log::alevel::devel,"set_http_handler");
          scoped_lock_type guard(m_mutex);
          m_http_handler = h;
      }
      void set_validate_handler(validate_handler h) {
          m_alog.write(log::alevel::devel,"set_validate_handler");
          scoped_lock_type guard(m_mutex);
          m_validate_handler = h;
      }
      void set_message_handler(message_handler h) {
          m_alog.write(log::alevel::devel,"set_message_handler");
          scoped_lock_type guard(m_mutex);
          m_message_handler = h;
      }
  
      /////////////////////////
      // Connection timeouts //
      /////////////////////////
  
      /// Set open handshake timeout
      /**
       * Sets the length of time the library will wait after an opening handshake
       * has been initiated before cancelling it. This can be used to prevent
       * excessive wait times for outgoing clients or excessive resource usage
       * from broken clients or DoS attacks on servers.
       *
       * Connections that time out will have their fail handlers called with the
       * open_handshake_timeout error code.
       *
       * The default value is specified via the compile time config value
       * 'timeout_open_handshake'. The default value in the core config
       * is 5000ms. A value of 0 will disable the timer entirely.
       *
       * To be effective, the transport you are using must support timers. See
       * the documentation for your transport policy for details about its
       * timer support.
       *
       * @param dur The length of the open handshake timeout in ms
       */
      void set_open_handshake_timeout(long dur) {
          scoped_lock_type guard(m_mutex);
          m_open_handshake_timeout_dur = dur;
      }
  
      /// Set close handshake timeout
      /**
       * Sets the length of time the library will wait after a closing handshake
       * has been initiated before cancelling it. This can be used to prevent
       * excessive wait times for outgoing clients or excessive resource usage
       * from broken clients or DoS attacks on servers.
       *
       * Connections that time out will have their close handlers called with the
       * close_handshake_timeout error code.
       *
       * The default value is specified via the compile time config value
       * 'timeout_close_handshake'. The default value in the core config
       * is 5000ms. A value of 0 will disable the timer entirely.
       *
       * To be effective, the transport you are using must support timers. See
       * the documentation for your transport policy for details about its
       * timer support.
       *
       * @param dur The length of the close handshake timeout in ms
       */
      void set_close_handshake_timeout(long dur) {
          scoped_lock_type guard(m_mutex);
          m_close_handshake_timeout_dur = dur;
      }
  
      /// Set pong timeout
      /**
       * Sets the length of time the library will wait for a pong response to a
       * ping. This can be used as a keepalive or to detect broken  connections.
       *
       * Pong responses that time out will have the pong timeout handler called.
       *
       * The default value is specified via the compile time config value
       * 'timeout_pong'. The default value in the core config
       * is 5000ms. A value of 0 will disable the timer entirely.
       *
       * To be effective, the transport you are using must support timers. See
       * the documentation for your transport policy for details about its
       * timer support.
       *
       * @param dur The length of the pong timeout in ms
       */
      void set_pong_timeout(long dur) {
          scoped_lock_type guard(m_mutex);
          m_pong_timeout_dur = dur;
      }
  
      /*************************************/
      /* Connection pass through functions */
      /*************************************/
  
      /**
       * These functions act as adaptors to their counterparts in connection. They
       * can produce one additional type of error, the bad_connection error, that
       * indicates that the conversion from connection_hdl to connection_ptr
       * failed due to the connection not existing anymore. Each method has a
       * default and an exception free varient.
       */
  
      void interrupt(connection_hdl hdl, lib::error_code & ec);
      void interrupt(connection_hdl hdl);
  
      /// Create a message and add it to the outgoing send queue (exception free)
      /**
       * Convenience method to send a message given a payload string and an opcode
       *
       * @param [in] hdl The handle identifying the connection to send via.
       * @param [in] payload The payload string to generated the message with
       * @param [in] op The opcode to generated the message with.
       * @param [out] ec A code to fill in for errors
       */
      void send(connection_hdl hdl, std::string const & payload,
          frame::opcode::value op, lib::error_code & ec);
      /// Create a message and add it to the outgoing send queue
      /**
       * Convenience method to send a message given a payload string and an opcode
       *
       * @param [in] hdl The handle identifying the connection to send via.
       * @param [in] payload The payload string to generated the message with
       * @param [in] op The opcode to generated the message with.
       * @param [out] ec A code to fill in for errors
       */
      void send(connection_hdl hdl, std::string const & payload,
          frame::opcode::value op);
  
      void send(connection_hdl hdl, void const * payload, size_t len,
          frame::opcode::value op, lib::error_code & ec);
      void send(connection_hdl hdl, void const * payload, size_t len,
          frame::opcode::value op);
  
      void send(connection_hdl hdl, message_ptr msg, lib::error_code & ec);
      void send(connection_hdl hdl, message_ptr msg);
  
      void close(connection_hdl hdl, close::status::value const code,
          std::string const & reason, lib::error_code & ec);
      void close(connection_hdl hdl, close::status::value const code,
          std::string const & reason);
  
      /// Send a ping to a specific connection
      /**
       * @since 0.3.0-alpha3
       *
       * @param [in] hdl The connection_hdl of the connection to send to.
       * @param [in] payload The payload string to send.
       * @param [out] ec A reference to an error code to fill in
       */
      void ping(connection_hdl hdl, std::string const & payload,
          lib::error_code & ec);
      /// Send a ping to a specific connection
      /**
       * Exception variant of `ping`
       *
       * @since 0.3.0-alpha3
       *
       * @param [in] hdl The connection_hdl of the connection to send to.
       * @param [in] payload The payload string to send.
       */
      void ping(connection_hdl hdl, std::string const & payload);
  
      /// Send a pong to a specific connection
      /**
       * @since 0.3.0-alpha3
       *
       * @param [in] hdl The connection_hdl of the connection to send to.
       * @param [in] payload The payload string to send.
       * @param [out] ec A reference to an error code to fill in
       */
      void pong(connection_hdl hdl, std::string const & payload,
          lib::error_code & ec);
      /// Send a pong to a specific connection
      /**
       * Exception variant of `pong`
       *
       * @since 0.3.0-alpha3
       *
       * @param [in] hdl The connection_hdl of the connection to send to.
       * @param [in] payload The payload string to send.
       */
      void pong(connection_hdl hdl, std::string const & payload);
  
      /// Retrieves a connection_ptr from a connection_hdl (exception free)
      /**
       * Converting a weak pointer to shared_ptr is not thread safe because the
       * pointer could be deleted at any time.
       *
       * NOTE: This method may be called by handler to upgrade its handle to a
       * full connection_ptr. That full connection may then be used safely for the
       * remainder of the handler body. get_con_from_hdl and the resulting
       * connection_ptr are NOT safe to use outside the handler loop.
       *
       * @param hdl The connection handle to translate
       *
       * @return the connection_ptr. May be NULL if the handle was invalid.
       */
      connection_ptr get_con_from_hdl(connection_hdl hdl, lib::error_code & ec) {
          scoped_lock_type lock(m_mutex);
          connection_ptr con = lib::static_pointer_cast<connection_type>(
              hdl.lock());
          if (!con) {
              ec = error::make_error_code(error::bad_connection);
          }
          return con;
      }
  
      /// Retrieves a connection_ptr from a connection_hdl (exception version)
      connection_ptr get_con_from_hdl(connection_hdl hdl) {
          lib::error_code ec;
          connection_ptr con = this->get_con_from_hdl(hdl,ec);
          if (ec) {
              throw ec;
          }
          return con;
      }
  protected:
      connection_ptr create_connection();
  
      alog_type m_alog;
      elog_type m_elog;
  private:
      // dynamic settings
      std::string                 m_user_agent;
  
      open_handler                m_open_handler;
      close_handler               m_close_handler;
      fail_handler                m_fail_handler;
      ping_handler                m_ping_handler;
      pong_handler                m_pong_handler;
      pong_timeout_handler        m_pong_timeout_handler;
      interrupt_handler           m_interrupt_handler;
      http_handler                m_http_handler;
      validate_handler            m_validate_handler;
      message_handler             m_message_handler;
  
      long                        m_open_handshake_timeout_dur;
      long                        m_close_handshake_timeout_dur;
      long                        m_pong_timeout_dur;
  
      rng_type m_rng;
  
      // static settings
      bool const                  m_is_server;
  
      // endpoint state
      mutable mutex_type          m_mutex;
  };
  
  } // namespace websocketpp
  
  #include <websocketpp/impl/endpoint_impl.hpp>
  
  #endif // WEBSOCKETPP_ENDPOINT_HPP
  