diff options
Diffstat (limited to 'websocketpp/connection.hpp')
-rw-r--r-- | websocketpp/connection.hpp | 1651 |
1 files changed, 1651 insertions, 0 deletions
diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp new file mode 100644 index 00000000..3bbbbb31 --- /dev/null +++ b/websocketpp/connection.hpp @@ -0,0 +1,1651 @@ +/* + * 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_CONNECTION_HPP +#define WEBSOCKETPP_CONNECTION_HPP + +#include <websocketpp/close.hpp> +#include <websocketpp/error.hpp> +#include <websocketpp/frame.hpp> + +#include <websocketpp/logger/levels.hpp> +#include <websocketpp/processors/processor.hpp> +#include <websocketpp/transport/base/connection.hpp> +#include <websocketpp/http/constants.hpp> + +#include <websocketpp/common/connection_hdl.hpp> +#include <websocketpp/common/cpp11.hpp> +#include <websocketpp/common/functional.hpp> + +#include <queue> +#include <sstream> +#include <string> +#include <vector> + +namespace websocketpp { + +/// The type and function signature of an open handler +/** + * The open handler is called once for every successful WebSocket connection + * attempt. Either the fail handler or the open handler will be called for each + * WebSocket connection attempt. HTTP Connections that did not attempt to + * upgrade the connection to the WebSocket protocol will trigger the http + * handler instead of fail/open. + */ +typedef lib::function<void(connection_hdl)> open_handler; + +/// The type and function signature of a close handler +/** + * The close handler is called once for every successfully established + * connection after it is no longer capable of sending or receiving new messages + * + * The close handler will be called exactly once for every connection for which + * the open handler was called. + */ +typedef lib::function<void(connection_hdl)> close_handler; + +/// The type and function signature of a fail handler +/** + * The fail handler is called once for every unsuccessful WebSocket connection + * attempt. Either the fail handler or the open handler will be called for each + * WebSocket connection attempt. HTTP Connections that did not attempt to + * upgrade the connection to the WebSocket protocol will trigger the http + * handler instead of fail/open. + */ +typedef lib::function<void(connection_hdl)> fail_handler; + +/// The type and function signature of an interrupt handler +/** + * The interrupt handler is called when a connection receives an interrupt + * request from the application. Interrupts allow the application to trigger a + * handler to be run in the absense of a WebSocket level handler trigger (like + * a new message). + * + * This is typically used by another application thread to schedule some tasks + * that can only be run from within the handler chain for thread safety reasons. + */ +typedef lib::function<void(connection_hdl)> interrupt_handler; + +/// The type and function signature of a ping handler +/** + * The ping handler is called when the connection receives a WebSocket ping + * control frame. The string argument contains the ping payload. The payload is + * a binary string up to 126 bytes in length. The ping handler returns a bool, + * true if a pong response should be sent, false if the pong response should be + * suppressed. + */ +typedef lib::function<bool(connection_hdl,std::string)> ping_handler; + +/// The type and function signature of a pong handler +/** + * The pong handler is called when the connection receives a WebSocket pong + * control frame. The string argument contains the pong payload. The payload is + * a binary string up to 126 bytes in length. + */ +typedef lib::function<void(connection_hdl,std::string)> pong_handler; + +/// The type and function signature of a pong timeout handler +/** + * The pong timeout handler is called when a ping goes unanswered by a pong for + * longer than the locally specified timeout period. + */ +typedef lib::function<void(connection_hdl,std::string)> pong_timeout_handler; + +/// The type and function signature of a validate handler +/** + * The validate handler is called after a WebSocket handshake has been received + * and processed but before it has been accepted. This gives the application a + * chance to implement connection details specific policies for accepting + * connections and the ability to negotiate extensions and subprotocols. + * + * The validate handler return value indicates whether or not the connection + * should be accepted. Additional methods may be called during the function to + * set response headers, set HTTP return/error codes, etc. + */ +typedef lib::function<bool(connection_hdl)> validate_handler; + +/// The type and function signature of a http handler +/** + * The http handler is called when an HTTP connection is made that does not + * attempt to upgrade the connection to the WebSocket protocol. This allows + * WebSocket++ servers to respond to these requests with regular HTTP responses. + * + * This can be used to deliver error pages & dashboards and to deliver static + * files such as the base HTML & JavaScript for an otherwise single page + * WebSocket application. + * + * Note: WebSocket++ is designed to be a high performance WebSocket server. It + * is not tuned to provide a full featured, high performance, HTTP web server + * solution. The HTTP handler is appropriate only for low volume HTTP traffic. + * If you expect to serve high volumes of HTTP traffic a dedicated HTTP web + * server is strongly recommended. + * + * The default HTTP handler will return a 426 Upgrade Required error. Custom + * handlers may override the response status code to deliver any type of + * response. + */ +typedef lib::function<void(connection_hdl)> http_handler; + +// +typedef lib::function<void(lib::error_code const & ec, size_t bytes_transferred)> read_handler; +typedef lib::function<void(lib::error_code const & ec)> write_frame_handler; + +// constants related to the default WebSocket protocol versions available +#ifdef _WEBSOCKETPP_INITIALIZER_LISTS_ // simplified C++11 version + /// Container that stores the list of protocol versions supported + /** + * @todo Move this to configs to allow compile/runtime disabling or enabling + * of protocol versions + */ + static std::vector<int> const versions_supported = {0,7,8,13}; +#else + /// Helper array to get around lack of initializer lists pre C++11 + static int const helper[] = {0,7,8,13}; + /// Container that stores the list of protocol versions supported + /** + * @todo Move this to configs to allow compile/runtime disabling or enabling + * of protocol versions + */ + static std::vector<int> const versions_supported(helper,helper+4); +#endif + +namespace session { +namespace state { + // externally visible session state (states based on the RFC) + enum value { + connecting = 0, + open = 1, + closing = 2, + closed = 3 + }; +} // namespace state + + +namespace fail { +namespace status { + enum value { + GOOD = 0, // no failure yet! + SYSTEM = 1, // system call returned error, check that code + WEBSOCKET = 2, // websocket close codes contain error + UNKNOWN = 3, // No failure information is available + TIMEOUT_TLS = 4, // TLS handshake timed out + TIMEOUT_WS = 5 // WS handshake timed out + }; +} // namespace status +} // namespace fail + +namespace internal_state { + // More granular internal states. These are used for multi-threaded + // connection synchronization and preventing values that are not yet or no + // longer available from being used. + + enum value { + USER_INIT = 0, + TRANSPORT_INIT = 1, + READ_HTTP_REQUEST = 2, + WRITE_HTTP_REQUEST = 3, + READ_HTTP_RESPONSE = 4, + WRITE_HTTP_RESPONSE = 5, + PROCESS_HTTP_REQUEST = 6, + PROCESS_CONNECTION = 7 + }; +} // namespace internal_state + + +namespace http_state { + // states to keep track of the progress of http connections + + enum value { + init = 0, + deferred = 1, + headers_written = 2, + body_written = 3, + closed = 4 + }; +} // namespace http_state + +} // namespace session + +/// Represents an individual WebSocket connection +template <typename config> +class connection + : public config::transport_type::transport_con_type + , public config::connection_base +{ +public: + /// Type of this connection + typedef connection<config> type; + /// Type of a shared pointer to this connection + typedef lib::shared_ptr<type> ptr; + /// Type of a weak pointer to this connection + typedef lib::weak_ptr<type> weak_ptr; + + /// Type of the concurrency component of this connection + typedef typename config::concurrency_type concurrency_type; + /// Type of the access logging policy + typedef typename config::alog_type alog_type; + /// Type of the error logging policy + typedef typename config::elog_type elog_type; + + /// Type of the transport component of this connection + typedef typename config::transport_type::transport_con_type + transport_con_type; + /// Type of a shared pointer to the transport component of this connection + typedef typename transport_con_type::ptr transport_con_ptr; + + typedef lib::function<void(ptr)> termination_handler; + + typedef typename concurrency_type::scoped_lock_type scoped_lock_type; + typedef typename concurrency_type::mutex_type mutex_type; + + typedef typename config::request_type request_type; + typedef typename config::response_type response_type; + + typedef typename config::message_type message_type; + typedef typename message_type::ptr message_ptr; + + typedef typename config::con_msg_manager_type con_msg_manager_type; + typedef typename con_msg_manager_type::ptr con_msg_manager_ptr; + + /// Type of RNG + typedef typename config::rng_type rng_type; + + typedef processor::processor<config> processor_type; + typedef lib::shared_ptr<processor_type> processor_ptr; + + // Message handler (needs to know message type) + typedef lib::function<void(connection_hdl,message_ptr)> message_handler; + + /// Type of a pointer to a transport timer handle + typedef typename transport_con_type::timer_ptr timer_ptr; + + // Misc Convenience Types + typedef session::internal_state::value istate_type; + +private: + enum terminate_status { + failed = 1, + closed, + unknown + }; +public: + + explicit connection(bool p_is_server, std::string const & ua, alog_type& alog, + elog_type& elog, rng_type & rng) + : transport_con_type(p_is_server, alog, elog) + , m_handle_read_frame(lib::bind( + &type::handle_read_frame, + this, + lib::placeholders::_1, + lib::placeholders::_2 + )) + , m_write_frame_handler(lib::bind( + &type::handle_write_frame, + this, + lib::placeholders::_1 + )) + , m_user_agent(ua) + , 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_state(session::state::connecting) + , m_internal_state(session::internal_state::USER_INIT) + , m_msg_manager(new con_msg_manager_type()) + , m_send_buffer_size(0) + , m_write_flag(false) + , m_read_flag(true) + , m_is_server(p_is_server) + , m_alog(alog) + , m_elog(elog) + , m_rng(rng) + , m_local_close_code(close::status::abnormal_close) + , m_remote_close_code(close::status::abnormal_close) + , m_is_http(false) + , m_http_state(session::http_state::init) + , m_was_clean(false) + { + m_alog.write(log::alevel::devel,"connection constructor"); + } + + /// Get a shared pointer to this component + ptr get_shared() { + return lib::static_pointer_cast<type>(transport_con_type::get_shared()); + } + + /////////////////////////// + // Set Handler Callbacks // + /////////////////////////// + + /// Set open handler + /** + * The open handler is called after the WebSocket handshake is complete and + * the connection is considered OPEN. + * + * @param h The new open_handler + */ + void set_open_handler(open_handler h) { + m_open_handler = h; + } + + /// Set close handler + /** + * The close handler is called immediately after the connection is closed. + * + * @param h The new close_handler + */ + void set_close_handler(close_handler h) { + m_close_handler = h; + } + + /// Set fail handler + /** + * The fail handler is called whenever the connection fails while the + * handshake is bring processed. + * + * @param h The new fail_handler + */ + void set_fail_handler(fail_handler h) { + m_fail_handler = h; + } + + /// Set ping handler + /** + * The ping handler is called whenever the connection receives a ping + * control frame. The ping payload is included. + * + * The ping handler's return time controls whether or not a pong is + * sent in response to this ping. Returning false will suppress the + * return pong. If no ping handler is set a pong will be sent. + * + * @param h The new ping_handler + */ + void set_ping_handler(ping_handler h) { + m_ping_handler = h; + } + + /// Set pong handler + /** + * The pong handler is called whenever the connection receives a pong + * control frame. The pong payload is included. + * + * @param h The new pong_handler + */ + void set_pong_handler(pong_handler h) { + m_pong_handler = h; + } + + /// Set pong timeout handler + /** + * If the transport component being used supports timers, the pong timeout + * handler is called whenever a pong control frame is not received with the + * configured timeout period after the application sends a ping. + * + * The config setting `timeout_pong` controls the length of the timeout + * period. It is specified in milliseconds. + * + * This can be used to probe the health of the remote endpoint's WebSocket + * implementation. This does not guarantee that the remote application + * itself is still healthy but can be a useful diagnostic. + * + * Note: receipt of this callback doesn't mean the pong will never come. + * This functionality will not suppress delivery of the pong in question + * should it arrive after the timeout. + * + * @param h The new pong_timeout_handler + */ + void set_pong_timeout_handler(pong_timeout_handler h) { + m_pong_timeout_handler = h; + } + + /// Set interrupt handler + /** + * The interrupt handler is called whenever the connection is manually + * interrupted by the application. + * + * @param h The new interrupt_handler + */ + void set_interrupt_handler(interrupt_handler h) { + m_interrupt_handler = h; + } + + /// Set http handler + /** + * The http handler is called after an HTTP request other than a WebSocket + * upgrade request is received. It allows a WebSocket++ server to respond + * to regular HTTP requests on the same port as it processes WebSocket + * connections. This can be useful for hosting error messages, flash + * policy files, status pages, and other simple HTTP responses. It is not + * intended to be used as a primary web server. + * + * @param h The new http_handler + */ + void set_http_handler(http_handler h) { + m_http_handler = h; + } + + /// Set validate handler + /** + * The validate handler is called after a WebSocket handshake has been + * parsed but before a response is returned. It provides the application + * a chance to examine the request and determine whether or not it wants + * to accept the connection. + * + * Returning false from the validate handler will reject the connection. + * If no validate handler is present, all connections will be allowed. + * + * @param h The new validate_handler + */ + void set_validate_handler(validate_handler h) { + m_validate_handler = h; + } + + /// Set message handler + /** + * The message handler is called after a new message has been received. + * + * @param h The new message_handler + */ + void set_message_handler(message_handler h) { + 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) { + 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) { + 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) { + m_pong_timeout_dur = dur; + } + + /// Get maximum message size + /** + * Get maximum message size. Maximum message size determines the point at + * which the connection will fail with the message_too_big protocol error. + * + * The default is set by the endpoint that creates the connection. + * + * @since 0.3.0 + */ + size_t get_max_message_size() const { + return m_max_message_size; + } + + /// Set maximum message size + /** + * Set maximum message size. Maximum message size determines the point at + * which the connection will fail with the message_too_big protocol error. + * This value may be changed during the connection. + * + * The default is set by the endpoint that creates the connection. + * + * @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; + if (m_processor) { + m_processor->set_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 endpoint that creates the connection. + * + * @since 0.5.0 + * + * @return The maximum HTTP message body size + */ + size_t get_max_http_body_size() const { + return m_request.get_max_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 endpoint that creates the connection. + * + * @since 0.5.0 + * + * @param new_value The value to set as the maximum message size. + */ + void set_max_http_body_size(size_t new_value) { + m_request.set_max_body_size(new_value); + } + + ////////////////////////////////// + // Uncategorized public methods // + ////////////////////////////////// + + /// Get the size of the outgoing write buffer (in payload bytes) + /** + * Retrieves the number of bytes in the outgoing write buffer that have not + * already been dispatched to the transport layer. This represents the bytes + * that are presently cancelable without uncleanly ending the websocket + * connection + * + * This method invokes the m_write_lock mutex + * + * @return The current number of bytes in the outgoing send buffer. + */ + size_t get_buffered_amount() const; + + /// Get the size of the outgoing write buffer (in payload bytes) + /** + * @deprecated use `get_buffered_amount` instead + */ + size_t buffered_amount() const { + return get_buffered_amount(); + } + + //////////////////// + // Action Methods // + //////////////////// + + /// Create a message and then add it to the outgoing send queue + /** + * Convenience method to send a message given a payload string and + * optionally an opcode. Default opcode is utf8 text. + * + * This method locks the m_write_lock mutex + * + * @param payload The payload string to generated the message with + * + * @param op The opcode to generated the message with. Default is + * frame::opcode::text + */ + lib::error_code send(std::string const & payload, frame::opcode::value op = + frame::opcode::text); + + /// Send a message (raw array overload) + /** + * Convenience method to send a message given a raw array and optionally an + * opcode. Default opcode is binary. + * + * This method locks the m_write_lock mutex + * + * @param payload A pointer to the array containing the bytes to send. + * + * @param len Length of the array. + * + * @param op The opcode to generated the message with. Default is + * frame::opcode::binary + */ + lib::error_code send(void const * payload, size_t len, frame::opcode::value + op = frame::opcode::binary); + + /// Add a message to the outgoing send queue + /** + * If presented with a prepared message it is added without validation or + * framing. If presented with an unprepared message it is validated, framed, + * and then added + * + * Errors are returned via an exception + * \todo make exception system_error rather than error_code + * + * This method invokes the m_write_lock mutex + * + * @param msg A message_ptr to the message to send. + */ + lib::error_code send(message_ptr msg); + + /// Asyncronously invoke handler::on_inturrupt + /** + * Signals to the connection to asyncronously invoke the on_inturrupt + * callback for this connection's handler once it is safe to do so. + * + * When the on_inturrupt handler callback is called it will be from + * within the transport event loop with all the thread safety features + * guaranteed by the transport to regular handlers + * + * Multiple inturrupt signals can be active at once on the same connection + * + * @return An error code + */ + lib::error_code interrupt(); + + /// Transport inturrupt callback + void handle_interrupt(); + + /// Pause reading of new data + /** + * 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. + */ + lib::error_code pause_reading(); + + /// Pause reading callback + void handle_pause_reading(); + + /// Resume reading of new data + /** + * 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. + */ + lib::error_code resume_reading(); + + /// Resume reading callback + void handle_resume_reading(); + + /// Send a ping + /** + * Initiates a ping with the given payload/ + * + * There is no feedback directly from ping except in cases of immediately + * detectable errors. Feedback will be provided via on_pong or + * on_pong_timeout callbacks. + * + * Ping locks the m_write_lock mutex + * + * @param payload Payload to be used for the ping + */ + void ping(std::string const & payload); + + /// exception free variant of ping + void ping(std::string const & payload, lib::error_code & ec); + + /// Utility method that gets called back when the ping timer expires + void handle_pong_timeout(std::string payload, lib::error_code const & ec); + + /// Send a pong + /** + * Initiates a pong with the given payload. + * + * There is no feedback from a pong once sent. + * + * Pong locks the m_write_lock mutex + * + * @param payload Payload to be used for the pong + */ + void pong(std::string const & payload); + + /// exception free variant of pong + void pong(std::string const & payload, lib::error_code & ec); + + /// Close the connection + /** + * Initiates the close handshake process. + * + * If close returns successfully the connection will be in the closing + * state and no additional messages may be sent. All messages sent prior + * to calling close will be written out before the connection is closed. + * + * If no reason is specified none will be sent. If no code is specified + * then no code will be sent. + * + * The handler's on_close callback will be called once the close handshake + * is complete. + * + * Reasons will be automatically truncated to the maximum length (123 bytes) + * if necessary. + * + * @param code The close code to send + * @param reason The close reason to send + */ + void close(close::status::value const code, std::string const & reason); + + /// exception free variant of close + void close(close::status::value const code, std::string const & reason, + lib::error_code & ec); + + //////////////////////////////////////////////// + // Pass-through access to the uri information // + //////////////////////////////////////////////// + + /// Returns the secure flag from the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return Whether or not the connection URI is flagged secure. + */ + bool get_secure() const; + + /// Returns the host component of the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The host component of the connection URI + */ + std::string const & get_host() const; + + /// Returns the resource component of the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The resource component of the connection URI + */ + std::string const & get_resource() const; + + /// Returns the port component of the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The port component of the connection URI + */ + uint16_t get_port() const; + + /// Gets the connection URI + /** + * This should really only be called by internal library methods unless you + * really know what you are doing. + * + * @return A pointer to the connection's URI + */ + uri_ptr get_uri() const; + + /// Sets the connection URI + /** + * This should really only be called by internal library methods unless you + * really know what you are doing. + * + * @param uri The new URI to set + */ + void set_uri(uri_ptr uri); + + ///////////////////////////// + // Subprotocol negotiation // + ///////////////////////////// + + /// Gets the negotated subprotocol + /** + * Retrieves the subprotocol that was negotiated during the handshake. This + * method is valid in the open handler and later. + * + * @return The negotiated subprotocol + */ + std::string const & get_subprotocol() const; + + /// Gets all of the subprotocols requested by the client + /** + * Retrieves the subprotocols that were requested during the handshake. This + * method is valid in the validate handler and later. + * + * @return A vector of the requested subprotocol + */ + std::vector<std::string> const & get_requested_subprotocols() const; + + /// Adds the given subprotocol string to the request list (exception free) + /** + * Adds a subprotocol to the list to send with the opening handshake. This + * may be called multiple times to request more than one. If the server + * supports one of these, it may choose one. If so, it will return it + * in it's handshake reponse and the value will be available via + * get_subprotocol(). Subprotocol requests should be added in order of + * preference. + * + * @param request The subprotocol to request + * @param ec A reference to an error code that will be filled in the case of + * errors + */ + void add_subprotocol(std::string const & request, lib::error_code & ec); + + /// Adds the given subprotocol string to the request list + /** + * Adds a subprotocol to the list to send with the opening handshake. This + * may be called multiple times to request more than one. If the server + * supports one of these, it may choose one. If so, it will return it + * in it's handshake reponse and the value will be available via + * get_subprotocol(). Subprotocol requests should be added in order of + * preference. + * + * @param request The subprotocol to request + */ + void add_subprotocol(std::string const & request); + + /// Select a subprotocol to use (exception free) + /** + * Indicates which subprotocol should be used for this connection. Valid + * only during the validate handler callback. Subprotocol selected must have + * been requested by the client. Consult get_requested_subprotocols() for a + * list of valid subprotocols. + * + * This member function is valid on server endpoints/connections only + * + * @param value The subprotocol to select + * @param ec A reference to an error code that will be filled in the case of + * errors + */ + void select_subprotocol(std::string const & value, lib::error_code & ec); + + /// Select a subprotocol to use + /** + * Indicates which subprotocol should be used for this connection. Valid + * only during the validate handler callback. Subprotocol selected must have + * been requested by the client. Consult get_requested_subprotocols() for a + * list of valid subprotocols. + * + * This member function is valid on server endpoints/connections only + * + * @param value The subprotocol to select + */ + void select_subprotocol(std::string const & value); + + ///////////////////////////////////////////////////////////// + // Pass-through access to the request and response objects // + ///////////////////////////////////////////////////////////// + + /// Retrieve a request header + /** + * Retrieve the value of a header from the handshake HTTP request. + * + * @param key Name of the header to get + * @return The value of the header + */ + std::string const & get_request_header(std::string const & key) const; + + /// Retrieve a request body + /** + * Retrieve the value of the request body. This value is typically used with + * PUT and POST requests to upload files or other data. Only HTTP + * connections will ever have bodies. WebSocket connection's will always + * have blank bodies. + * + * @return The value of the request body. + */ + std::string const & get_request_body() const; + + /// Retrieve a response header + /** + * Retrieve the value of a header from the handshake HTTP request. + * + * @param key Name of the header to get + * @return The value of the header + */ + std::string const & get_response_header(std::string const & key) const; + + /// Get response HTTP status code + /** + * Gets the response status code + * + * @since 0.7.0 + * + * @return The response status code sent + */ + http::status_code::value get_response_code() const { + return m_response.get_status_code(); + } + + /// Get response HTTP status message + /** + * Gets the response status message + * + * @since 0.7.0 + * + * @return The response status message sent + */ + std::string const & get_response_msg() const { + return m_response.get_status_msg(); + } + + /// Set response status code and message + /** + * Sets the response status code to `code` and looks up the corresponding + * message for standard codes. Non-standard codes will be entered as Unknown + * use set_status(status_code::value,std::string) overload to set both + * values explicitly. + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @param code Code to set + * @param msg Message to set + * @see websocketpp::http::response::set_status + */ + void set_status(http::status_code::value code); + + /// Set response status code and message + /** + * Sets the response status code and message to independent custom values. + * use set_status(status_code::value) to set the code and have the standard + * message be automatically set. + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @param code Code to set + * @param msg Message to set + * @see websocketpp::http::response::set_status + */ + void set_status(http::status_code::value code, std::string const & msg); + + /// Set response body content + /** + * Set the body content of the HTTP response to the parameter string. Note + * set_body will also set the Content-Length HTTP header to the appropriate + * value. If you want the Content-Length header to be something else set it + * to something else after calling set_body + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @param value String data to include as the body content. + * @see websocketpp::http::response::set_body + */ + void set_body(std::string const & value); + + /// Append a header + /** + * If a header with this name already exists the value will be appended to + * the existing header to form a comma separated list of values. Use + * `connection::replace_header` to overwrite existing values. + * + * This member function is valid only from the http() and validate() handler + * callbacks, or to a client connection before connect has been called. + * + * @param key Name of the header to set + * @param val Value to add + * @see replace_header + * @see websocketpp::http::parser::append_header + */ + void append_header(std::string const & key, std::string const & val); + + /// Replace a header + /** + * If a header with this name already exists the old value will be replaced + * Use `connection::append_header` to append to a list of existing values. + * + * This member function is valid only from the http() and validate() handler + * callbacks, or to a client connection before connect has been called. + * + * @param key Name of the header to set + * @param val Value to set + * @see append_header + * @see websocketpp::http::parser::replace_header + */ + void replace_header(std::string const & key, std::string const & val); + + /// Remove a header + /** + * Removes a header from the response. + * + * This member function is valid only from the http() and validate() handler + * callbacks, or to a client connection before connect has been called. + * + * @param key The name of the header to remove + * @see websocketpp::http::parser::remove_header + */ + void remove_header(std::string const & key); + + /// Get request object + /** + * Direct access to request object. This can be used to call methods of the + * request object that are not part of the standard request API that + * connection wraps. + * + * Note use of this method involves using behavior specific to the + * configured HTTP policy. Such behavior may not work with alternate HTTP + * policies. + * + * @since 0.3.0-alpha3 + * + * @return A const reference to the raw request object + */ + request_type const & get_request() const { + return m_request; + } + + /// Get response object + /** + * Direct access to the HTTP response sent or received as a part of the + * opening handshake. This can be used to call methods of the response + * object that are not part of the standard request API that connection + * wraps. + * + * Note use of this method involves using behavior specific to the + * configured HTTP policy. Such behavior may not work with alternate HTTP + * policies. + * + * @since 0.7.0 + * + * @return A const reference to the raw response object + */ + response_type const & get_response() const { + return m_response; + } + + /// Defer HTTP Response until later (Exception free) + /** + * Used in the http handler to defer the HTTP response for this connection + * until later. Handshake timers will be canceled and the connection will be + * left open until `send_http_response` or an equivalent is called. + * + * Warning: deferred connections won't time out and as a result can tie up + * resources. + * + * @since 0.6.0 + * + * @return A status code, zero on success, non-zero otherwise + */ + lib::error_code defer_http_response(); + + /// 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. + * + * @since 0.6.0 + * + * @param ec A status code, zero on success, non-zero otherwise + */ + void send_http_response(lib::error_code & ec); + + /// Send deferred HTTP Response + void send_http_response(); + + // TODO HTTPNBIO: write_headers + // function that processes headers + status so far and writes it to the wire + // beginning the HTTP response body state. This method will ignore anything + // in the response body. + + // TODO HTTPNBIO: write_body_message + // queues the specified message_buffer for async writing + + // TODO HTTPNBIO: finish connection + // + + // TODO HTTPNBIO: write_response + // Writes the whole response, headers + body and closes the connection + + + + ///////////////////////////////////////////////////////////// + // Pass-through access to the other connection information // + ///////////////////////////////////////////////////////////// + + /// Get Connection Handle + /** + * The connection handle is a token that can be shared outside the + * WebSocket++ core for the purposes of identifying a connection and + * sending it messages. + * + * @return A handle to the connection + */ + connection_hdl get_handle() const { + return m_connection_hdl; + } + + /// Get whether or not this connection is part of a server or client + /** + * @return whether or not the connection is attached to a server endpoint + */ + bool is_server() const { + return m_is_server; + } + + /// Return the same origin policy origin value from the opening request. + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The connection's origin value from the opening handshake. + */ + std::string const & get_origin() const; + + /// Return the connection state. + /** + * Values can be connecting, open, closing, and closed + * + * @return The connection's current state. + */ + session::state::value get_state() const; + + + /// Get the WebSocket close code sent by this endpoint. + /** + * @return The WebSocket close code sent by this endpoint. + */ + close::status::value get_local_close_code() const { + return m_local_close_code; + } + + /// Get the WebSocket close reason sent by this endpoint. + /** + * @return The WebSocket close reason sent by this endpoint. + */ + std::string const & get_local_close_reason() const { + return m_local_close_reason; + } + + /// Get the WebSocket close code sent by the remote endpoint. + /** + * @return The WebSocket close code sent by the remote endpoint. + */ + close::status::value get_remote_close_code() const { + return m_remote_close_code; + } + + /// Get the WebSocket close reason sent by the remote endpoint. + /** + * @return The WebSocket close reason sent by the remote endpoint. + */ + std::string const & get_remote_close_reason() const { + return m_remote_close_reason; + } + + /// Get the internal error code for a closed/failed connection + /** + * Retrieves a machine readable detailed error code indicating the reason + * that the connection was closed or failed. Valid only after the close or + * fail handler is called. + * + * @return Error code indicating the reason the connection was closed or + * failed + */ + lib::error_code get_ec() const { + return m_ec; + } + + /// Get a message buffer + /** + * Warning: The API related to directly sending message buffers may change + * before the 1.0 release. If you plan to use it, please keep an eye on any + * breaking changes notifications in future release notes. Also if you have + * any feedback about usage and capabilities now is a great time to provide + * it. + * + * Message buffers are used to store message payloads and other message + * metadata. + * + * The size parameter is a hint only. Your final payload does not need to + * match it. There may be some performance benefits if the initial size + * guess is equal to or slightly higher than the final payload size. + * + * @param op The opcode for the new message + * @param size A hint to optimize the initial allocation of payload space. + * @return A new message buffer + */ + message_ptr get_message(websocketpp::frame::opcode::value op, size_t size) + const + { + return m_msg_manager->get_message(op, size); + } + + //////////////////////////////////////////////////////////////////////// + // The remaining public member functions are for internal/policy use // + // only. Do not call from application code unless you understand what // + // you are doing. // + //////////////////////////////////////////////////////////////////////// + + + + void read_handshake(size_t num_bytes); + + void handle_read_handshake(lib::error_code const & ec, + size_t bytes_transferred); + void handle_read_http_response(lib::error_code const & ec, + size_t bytes_transferred); + + + void handle_write_http_response(lib::error_code const & ec); + void handle_send_http_request(lib::error_code const & ec); + + void handle_open_handshake_timeout(lib::error_code const & ec); + void handle_close_handshake_timeout(lib::error_code const & ec); + + void handle_read_frame(lib::error_code const & ec, size_t bytes_transferred); + void read_frame(); + + /// Get array of WebSocket protocol versions that this connection supports. + std::vector<int> const & get_supported_versions() const; + + /// Sets the handler for a terminating connection. Should only be used + /// internally by the endpoint class. + void set_termination_handler(termination_handler new_handler); + + void terminate(lib::error_code const & ec); + void handle_terminate(terminate_status tstat, lib::error_code const & ec); + + /// Checks if there are frames in the send queue and if there are sends one + /** + * \todo unit tests + * + * This method locks the m_write_lock mutex + */ + void write_frame(); + + /// Process the results of a frame write operation and start the next write + /** + * \todo unit tests + * + * This method locks the m_write_lock mutex + * + * @param terminate Whether or not to terminate the connection upon + * completion of this write. + * + * @param ec A status code from the transport layer, zero on success, + * non-zero otherwise. + */ + void handle_write_frame(lib::error_code const & ec); +// protected: + // This set of methods would really like to be protected, but doing so + // requires that the endpoint be able to friend the connection. This is + // allowed with C++11, but not prior versions + + /// Start the connection state machine + void start(); + + /// Set Connection Handle + /** + * The connection handle is a token that can be shared outside the + * WebSocket++ core for the purposes of identifying a connection and + * sending it messages. + * + * @param hdl A connection_hdl that the connection will use to refer + * to itself. + */ + void set_handle(connection_hdl hdl) { + m_connection_hdl = hdl; + transport_con_type::set_handle(hdl); + } +protected: + void handle_transport_init(lib::error_code const & ec); + + /// Set m_processor based on information in m_request. Set m_response + /// status and return an error code indicating status. + lib::error_code initialize_processor(); + + /// Perform WebSocket handshake validation of m_request using m_processor. + /// set m_response and return an error code indicating status. + lib::error_code process_handshake_request(); +private: + + + /// Completes m_response, serializes it, and sends it out on the wire. + void write_http_response(lib::error_code const & ec); + + /// Sends an opening WebSocket connect request + void send_http_request(); + + /// Alternate path for write_http_response in error conditions + void write_http_response_error(lib::error_code const & ec); + + /// Process control message + /** + * + */ + void process_control_frame(message_ptr msg); + + /// Send close acknowledgement + /** + * If no arguments are present no close code/reason will be specified. + * + * Note: the close code/reason values provided here may be overrided by + * other settings (such as silent close). + * + * @param code The close code to send + * @param reason The close reason to send + * @return A status code, zero on success, non-zero otherwise + */ + lib::error_code send_close_ack(close::status::value code = + close::status::blank, std::string const & reason = std::string()); + + /// Send close frame + /** + * If no arguments are present no close code/reason will be specified. + * + * Note: the close code/reason values provided here may be overrided by + * other settings (such as silent close). + * + * The ack flag determines what to do in the case of a blank status and + * whether or not to terminate the TCP connection after sending it. + * + * @param code The close code to send + * @param reason The close reason to send + * @param ack Whether or not this is an acknowledgement close frame + * @return A status code, zero on success, non-zero otherwise + */ + lib::error_code send_close_frame(close::status::value code = + close::status::blank, std::string const & reason = std::string(), bool ack = false, + bool terminal = false); + + /// Get a pointer to a new WebSocket protocol processor for a given version + /** + * @param version Version number of the WebSocket protocol to get a + * processor for. Negative values indicate invalid/unknown versions and will + * always return a null ptr + * + * @return A shared_ptr to a new instance of the appropriate processor or a + * null ptr if there is no installed processor that matches the version + * number. + */ + processor_ptr get_processor(int version) const; + + /// Add a message to the write queue + /** + * Adds a message to the write queue and updates any associated shared state + * + * Must be called while holding m_write_lock + * + * @todo unit tests + * + * @param msg The message to push + */ + void write_push(message_ptr msg); + + /// Pop a message from the write queue + /** + * Removes and returns a message from the write queue and updates any + * associated shared state. + * + * Must be called while holding m_write_lock + * + * @todo unit tests + * + * @return the message_ptr at the front of the queue + */ + message_ptr write_pop(); + + /// Prints information about the incoming connection to the access log + /** + * Prints information about the incoming connection to the access log. + * Includes: connection type, websocket version, remote endpoint, user agent + * path, status code. + */ + void log_open_result(); + + /// Prints information about a connection being closed to the access log + /** + * Includes: local and remote close codes and reasons + */ + void log_close_result(); + + /// Prints information about a connection being failed to the access log + /** + * Includes: error code and message for why it was failed + */ + void log_fail_result(); + + /// Prints information about HTTP connections + /** + * Includes: TODO + */ + void log_http_result(); + + /// Prints information about an arbitrary error code on the specified channel + template <typename error_type> + void log_err(log::level l, char const * msg, error_type const & ec) { + std::stringstream s; + s << msg << " error: " << ec << " (" << ec.message() << ")"; + m_elog.write(l, s.str()); + } + + // internal handler functions + read_handler m_handle_read_frame; + write_frame_handler m_write_frame_handler; + + // static settings + std::string const m_user_agent; + + /// Pointer to the connection handle + connection_hdl m_connection_hdl; + + /// Handler objects + 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; + + /// constant values + long m_open_handshake_timeout_dur; + long m_close_handshake_timeout_dur; + long m_pong_timeout_dur; + size_t m_max_message_size; + + /// External connection state + /** + * Lock: m_connection_state_lock + */ + session::state::value m_state; + + /// Internal connection state + /** + * Lock: m_connection_state_lock + */ + istate_type m_internal_state; + + mutable mutex_type m_connection_state_lock; + + /// The lock used to protect the message queue + /** + * Serializes access to the write queue as well as shared state within the + * processor. + */ + mutex_type m_write_lock; + + // connection resources + char m_buf[config::connection_read_buffer_size]; + size_t m_buf_cursor; + termination_handler m_termination_handler; + con_msg_manager_ptr m_msg_manager; + timer_ptr m_handshake_timer; + timer_ptr m_ping_timer; + + /// @todo this is not memory efficient. this value is not used after the + /// handshake. + std::string m_handshake_buffer; + + /// Pointer to the processor object for this connection + /** + * The processor provides functionality that is specific to the WebSocket + * protocol version that the client has negotiated. It also contains all of + * the state necessary to encode and decode the incoming and outgoing + * WebSocket byte streams + * + * Use of the prepare_data_frame method requires lock: m_write_lock + */ + processor_ptr m_processor; + + /// Queue of unsent outgoing messages + /** + * Lock: m_write_lock + */ + std::queue<message_ptr> m_send_queue; + + /// Size in bytes of the outstanding payloads in the write queue + /** + * Lock: m_write_lock + */ + size_t m_send_buffer_size; + + /// buffer holding the various parts of the current message being writen + /** + * Lock m_write_lock + */ + std::vector<transport::buffer> m_send_buffer; + + /// a list of pointers to hold on to the messages being written to keep them + /// from going out of scope before the write is complete. + std::vector<message_ptr> m_current_msgs; + + /// True if there is currently an outstanding transport write + /** + * Lock m_write_lock + */ + bool m_write_flag; + + /// True if this connection is presently reading new data + bool m_read_flag; + + // connection data + request_type m_request; + response_type m_response; + uri_ptr m_uri; + std::string m_subprotocol; + + // connection data that might not be necessary to keep around for the life + // of the whole connection. + std::vector<std::string> m_requested_subprotocols; + + bool const m_is_server; + alog_type& m_alog; + elog_type& m_elog; + + rng_type & m_rng; + + // Close state + /// Close code that was sent on the wire by this endpoint + close::status::value m_local_close_code; + + /// Close reason that was sent on the wire by this endpoint + std::string m_local_close_reason; + + /// Close code that was received on the wire from the remote endpoint + close::status::value m_remote_close_code; + + /// Close reason that was received on the wire from the remote endpoint + std::string m_remote_close_reason; + + /// Detailed internal error code + lib::error_code m_ec; + + /// A flag that gets set once it is determined that the connection is an + /// HTTP connection and not a WebSocket one. + bool m_is_http; + + /// A flag that gets set when the completion of an http connection is + /// deferred until later. + session::http_state::value m_http_state; + + bool m_was_clean; + + /// Whether or not this endpoint initiated the closing handshake. + bool m_closed_by_me; + + /// ??? + bool m_failed_by_me; + + /// Whether or not this endpoint initiated the drop of the TCP connection + bool m_dropped_by_me; +}; + +} // namespace websocketpp + +#include <websocketpp/impl/connection_impl.hpp> + +#endif // WEBSOCKETPP_CONNECTION_HPP |