/* * Copyright (c) 2014, 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 #include #include #include namespace websocketpp { /// Creates and manages connections associated with a WebSocket endpoint template 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 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; // This would be ideal. Requires C++11 though //friend connection; explicit endpoint(bool p_is_server) : m_alog(config::alog_level, log::channel_type_hint::access) , m_elog(config::elog_level, log::channel_type_hint::error) , 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_max_message_size(config::max_message_size) , m_max_http_body_size(config::max_http_body_size) , m_is_server(p_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); } /// Destructor ~endpoint() {} #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ // no copy constructor because endpoints are not copyable endpoint(endpoint &) = delete; // no copy assignment operator because endpoints are not copyable endpoint & operator=(endpoint const &) = delete; #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ /// Move constructor endpoint(endpoint && o) : config::transport_type(std::move(o)) , config::endpoint_base(std::move(o)) , m_alog(std::move(o.m_alog)) , m_elog(std::move(o.m_elog)) , m_user_agent(std::move(o.m_user_agent)) , m_open_handler(std::move(o.m_open_handler)) , m_close_handler(std::move(o.m_close_handler)) , m_fail_handler(std::move(o.m_fail_handler)) , m_ping_handler(std::move(o.m_ping_handler)) , m_pong_handler(std::move(o.m_pong_handler)) , m_pong_timeout_handler(std::move(o.m_pong_timeout_handler)) , m_interrupt_handler(std::move(o.m_interrupt_handler)) , m_http_handler(std::move(o.m_http_handler)) , m_validate_handler(std::move(o.m_validate_handler)) , m_message_handler(std::move(o.m_message_handler)) , m_open_handshake_timeout_dur(o.m_open_handshake_timeout_dur) , m_close_handshake_timeout_dur(o.m_close_handshake_timeout_dur) , m_pong_timeout_dur(o.m_pong_timeout_dur) , m_max_message_size(o.m_max_message_size) , m_max_http_body_size(o.m_max_http_body_size) , m_rng(std::move(o.m_rng)) , m_is_server(o.m_is_server) {} #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ // no move assignment operator because of const member variables endpoint & operator=(endpoint &&) = delete; #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ #endif // _WEBSOCKETPP_MOVE_SEMANTICS_ /// 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 and other limits // ////////////////////////////////////////// /// 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; } /// Get default maximum message size /** * Get the default maximum message size that will be used for new * connections created by this endpoint. The maximum message size determines * the point at which the connection will fail a connection with the * message_too_big protocol error. * * The default is set by the max_message_size value from the template config * * @since 0.3.0 */ size_t get_max_message_size() const { return m_max_message_size; } /// Set default maximum message size /** * Set the default maximum message size that will be used for new * connections created by this endpoint. Maximum message size determines the * point at which the connection will fail a connection with the * message_too_big protocol error. * * The default is set by the max_message_size value from the template config * * @since 0.3.0 * * @param new_value The value to set as the maximum message size. */ void set_max_message_size(size_t new_value) { m_max_message_size = new_value; } /// Get maximum HTTP message body size /** * Get maximum HTTP message body size. Maximum message body size determines * the point at which the connection will stop reading an HTTP request whose * body is too large. * * The default is set by the max_http_body_size value from the template * config * * @since 0.5.0 * * @return The maximum HTTP message body size */ size_t get_max_http_body_size() const { return m_max_http_body_size; } /// Set maximum HTTP message body size /** * Set maximum HTTP message body size. Maximum message body size determines * the point at which the connection will stop reading an HTTP request whose * body is too large. * * The default is set by the max_http_body_size value from the template * config * * @since 0.5.1 * * @param new_value The value to set as the maximum message size. */ void set_max_http_body_size(size_t new_value) { m_max_http_body_size = new_value; } /*************************************/ /* 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); /// Pause reading of new data (exception free) /** * Signals to the connection to halt reading of new data. While reading is * paused, the connection will stop reading from its associated socket. In * turn this will result in TCP based flow control kicking in and slowing * data flow from the remote endpoint. * * This is useful for applications that push new requests to a queue to be * processed by another thread and need a way to signal when their request * queue is full without blocking the network processing thread. * * Use `resume_reading()` to resume. * * If supported by the transport this is done asynchronously. As such * reading may not stop until the current read operation completes. * Typically you can expect to receive no more bytes after initiating a read * pause than the size of the read buffer. * * If reading is paused for this connection already nothing is changed. */ void pause_reading(connection_hdl hdl, lib::error_code & ec); /// Pause reading of new data void pause_reading(connection_hdl hdl); /// Resume reading of new data (exception free) /** * Signals to the connection to resume reading of new data after it was * paused by `pause_reading()`. * * If reading is not paused for this connection already nothing is changed. */ void resume_reading(connection_hdl hdl, lib::error_code & ec); /// Resume reading of new data void resume_reading(connection_hdl hdl); /// Send deferred HTTP Response /** * Sends an http response to an HTTP connection that was deferred. This will * send a complete response including all headers, status line, and body * text. The connection will be closed afterwards. * * Exception free variant * * @since 0.6.0 * * @param hdl The connection to send the response on * @param ec A status code, zero on success, non-zero otherwise */ void send_http_response(connection_hdl hdl, lib::error_code & ec); /// Send deferred HTTP Response (exception free) /** * Sends an http response to an HTTP connection that was deferred. This will * send a complete response including all headers, status line, and body * text. The connection will be closed afterwards. * * Exception variant * * @since 0.6.0 * * @param hdl The connection to send the response on */ void send_http_response(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) { connection_ptr con = lib::static_pointer_cast( 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 exception(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; size_t m_max_message_size; size_t m_max_http_body_size; rng_type m_rng; // static settings bool const m_is_server; // endpoint state mutable mutex_type m_mutex; }; } // namespace websocketpp #include #endif // WEBSOCKETPP_ENDPOINT_HPP