aboutsummaryrefslogtreecommitdiffstats
path: root/websocketpp/transport
diff options
context:
space:
mode:
Diffstat (limited to 'websocketpp/transport')
-rw-r--r--websocketpp/transport/asio/base.hpp232
-rw-r--r--websocketpp/transport/asio/connection.hpp1204
-rw-r--r--websocketpp/transport/asio/endpoint.hpp1147
-rw-r--r--websocketpp/transport/asio/security/base.hpp159
-rw-r--r--websocketpp/transport/asio/security/none.hpp370
-rw-r--r--websocketpp/transport/asio/security/tls.hpp484
-rw-r--r--websocketpp/transport/base/connection.hpp238
-rw-r--r--websocketpp/transport/base/endpoint.hpp77
-rw-r--r--websocketpp/transport/debug/base.hpp104
-rw-r--r--websocketpp/transport/debug/connection.hpp412
-rw-r--r--websocketpp/transport/debug/endpoint.hpp140
-rw-r--r--websocketpp/transport/iostream/base.hpp133
-rw-r--r--websocketpp/transport/iostream/connection.hpp714
-rw-r--r--websocketpp/transport/iostream/endpoint.hpp222
-rw-r--r--websocketpp/transport/stub/base.hpp95
-rw-r--r--websocketpp/transport/stub/connection.hpp286
-rw-r--r--websocketpp/transport/stub/endpoint.hpp140
17 files changed, 6157 insertions, 0 deletions
diff --git a/websocketpp/transport/asio/base.hpp b/websocketpp/transport/asio/base.hpp
new file mode 100644
index 00000000..b945fe11
--- /dev/null
+++ b/websocketpp/transport/asio/base.hpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2015, 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_TRANSPORT_ASIO_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP
+
+#include <websocketpp/common/asio.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/common/type_traits.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+/// Transport policy that uses asio
+/**
+ * This policy uses a single asio io_service to provide transport
+ * services to a WebSocket++ endpoint.
+ */
+namespace asio {
+
+// Class to manage the memory to be used for handler-based custom allocation.
+// It contains a single block of memory which may be returned for allocation
+// requests. If the memory is in use when an allocation request is made, the
+// allocator delegates allocation to the global heap.
+class handler_allocator {
+public:
+ static const size_t size = 1024;
+
+ handler_allocator() : m_in_use(false) {}
+
+#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ handler_allocator(handler_allocator const & cpy) = delete;
+ handler_allocator & operator =(handler_allocator const &) = delete;
+#endif
+
+ void * allocate(std::size_t memsize) {
+ if (!m_in_use && memsize < size) {
+ m_in_use = true;
+ return static_cast<void*>(&m_storage);
+ } else {
+ return ::operator new(memsize);
+ }
+ }
+
+ void deallocate(void * pointer) {
+ if (pointer == &m_storage) {
+ m_in_use = false;
+ } else {
+ ::operator delete(pointer);
+ }
+ }
+
+private:
+ // Storage space used for handler-based custom memory allocation.
+ lib::aligned_storage<size>::type m_storage;
+
+ // Whether the handler-based custom allocation storage has been used.
+ bool m_in_use;
+};
+
+// Wrapper class template for handler objects to allow handler memory
+// allocation to be customised. Calls to operator() are forwarded to the
+// encapsulated handler.
+template <typename Handler>
+class custom_alloc_handler {
+public:
+ custom_alloc_handler(handler_allocator& a, Handler h)
+ : allocator_(a),
+ handler_(h)
+ {}
+
+ template <typename Arg1>
+ void operator()(Arg1 arg1) {
+ handler_(arg1);
+ }
+
+ template <typename Arg1, typename Arg2>
+ void operator()(Arg1 arg1, Arg2 arg2) {
+ handler_(arg1, arg2);
+ }
+
+ friend void* asio_handler_allocate(std::size_t size,
+ custom_alloc_handler<Handler> * this_handler)
+ {
+ return this_handler->allocator_.allocate(size);
+ }
+
+ friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/,
+ custom_alloc_handler<Handler> * this_handler)
+ {
+ this_handler->allocator_.deallocate(pointer);
+ }
+
+private:
+ handler_allocator & allocator_;
+ Handler handler_;
+};
+
+// Helper function to wrap a handler object to add custom allocation.
+template <typename Handler>
+inline custom_alloc_handler<Handler> make_custom_alloc_handler(
+ handler_allocator & a, Handler h)
+{
+ return custom_alloc_handler<Handler>(a, h);
+}
+
+
+
+
+
+
+
+// Forward declaration of class endpoint so that it can be friended/referenced
+// before being included.
+template <typename config>
+class endpoint;
+
+typedef lib::function<void (lib::asio::error_code const & ec,
+ size_t bytes_transferred)> async_read_handler;
+
+typedef lib::function<void (lib::asio::error_code const & ec,
+ size_t bytes_transferred)> async_write_handler;
+
+typedef lib::function<void (lib::error_code const & ec)> pre_init_handler;
+
+// handle_timer: dynamic parameters, multiple copies
+// handle_proxy_write
+// handle_proxy_read
+// handle_async_write
+// handle_pre_init
+
+
+/// Asio transport errors
+namespace error {
+enum value {
+ /// Catch-all error for transport policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// async_read_at_least call requested more bytes than buffer can store
+ invalid_num_bytes,
+
+ /// there was an error in the underlying transport library
+ pass_through,
+
+ /// The connection to the requested proxy server failed
+ proxy_failed,
+
+ /// Invalid Proxy URI
+ proxy_invalid,
+
+ /// Invalid host or service
+ invalid_host_service
+};
+
+/// Asio transport error category
+class category : public lib::error_category {
+public:
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport.asio";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case error::general:
+ return "Generic asio transport policy error";
+ case error::invalid_num_bytes:
+ return "async_read_at_least call requested more bytes than buffer can store";
+ case error::pass_through:
+ return "Underlying Transport Error";
+ case error::proxy_failed:
+ return "Proxy connection failed";
+ case error::proxy_invalid:
+ return "Invalid proxy URI";
+ case error::invalid_host_service:
+ return "Invalid host or service";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+/// Get a reference to a static copy of the asio transport error category
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+/// Create an error code with the given value and the asio transport category
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::transport::asio::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+#endif // WEBSOCKETPP_TRANSPORT_ASIO_HPP
diff --git a/websocketpp/transport/asio/connection.hpp b/websocketpp/transport/asio/connection.hpp
new file mode 100644
index 00000000..8eb8c759
--- /dev/null
+++ b/websocketpp/transport/asio/connection.hpp
@@ -0,0 +1,1204 @@
+/*
+ * Copyright (c) 2015, 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_TRANSPORT_ASIO_CON_HPP
+#define WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
+
+#include <websocketpp/transport/asio/base.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+
+#include <websocketpp/logger/levels.hpp>
+#include <websocketpp/http/constants.hpp>
+
+#include <websocketpp/base64/base64.hpp>
+#include <websocketpp/error.hpp>
+#include <websocketpp/uri.hpp>
+
+#include <websocketpp/common/asio.hpp>
+#include <websocketpp/common/chrono.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/connection_hdl.hpp>
+
+#include <istream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace transport {
+namespace asio {
+
+typedef lib::function<void(connection_hdl)> tcp_init_handler;
+
+/// Asio based connection transport component
+/**
+ * transport::asio::connection implements a connection transport component using
+ * Asio that works with the transport::asio::endpoint endpoint transport
+ * component.
+ */
+template <typename config>
+class connection : public config::socket_type::socket_con_type {
+public:
+ /// Type of this connection transport component
+ typedef connection<config> type;
+ /// Type of a shared pointer to this connection transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of the socket connection component
+ typedef typename config::socket_type::socket_con_type socket_con_type;
+ /// Type of a shared pointer to the socket connection component
+ typedef typename socket_con_type::ptr socket_con_ptr;
+ /// Type of this transport's access logging policy
+ typedef typename config::alog_type alog_type;
+ /// Type of this transport's error logging policy
+ typedef typename config::elog_type elog_type;
+
+ typedef typename config::request_type request_type;
+ typedef typename request_type::ptr request_ptr;
+ typedef typename config::response_type response_type;
+ typedef typename response_type::ptr response_ptr;
+
+ /// Type of a pointer to the Asio io_service being used
+ typedef lib::asio::io_service * io_service_ptr;
+ /// Type of a pointer to the Asio io_service::strand being used
+ typedef lib::shared_ptr<lib::asio::io_service::strand> strand_ptr;
+ /// Type of a pointer to the Asio timer class
+ typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
+
+ // connection is friends with its associated endpoint to allow the endpoint
+ // to call private/protected utility methods that we don't want to expose
+ // to the public api.
+ friend class endpoint<config>;
+
+ // generate and manage our own io_service
+ explicit connection(bool is_server, alog_type & alog, elog_type & elog)
+ : m_is_server(is_server)
+ , m_alog(alog)
+ , m_elog(elog)
+ {
+ m_alog.write(log::alevel::devel,"asio con transport constructor");
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return lib::static_pointer_cast<type>(socket_con_type::get_shared());
+ }
+
+ bool is_secure() const {
+ return socket_con_type::is_secure();
+ }
+
+ /// Set uri hook
+ /**
+ * Called by the endpoint as a connection is being established to provide
+ * the uri being connected to to the transport layer.
+ *
+ * This transport policy doesn't use the uri except to forward it to the
+ * socket layer.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr u) {
+ socket_con_type::set_uri(u);
+ }
+
+ /// Sets the tcp pre init handler
+ /**
+ * The tcp pre init handler is called after the raw tcp connection has been
+ * established but before any additional wrappers (proxy connects, TLS
+ * handshakes, etc) have been performed.
+ *
+ * @since 0.3.0
+ *
+ * @param h The handler to call on tcp pre init.
+ */
+ void set_tcp_pre_init_handler(tcp_init_handler h) {
+ m_tcp_pre_init_handler = h;
+ }
+
+ /// Sets the tcp pre init handler (deprecated)
+ /**
+ * The tcp pre init handler is called after the raw tcp connection has been
+ * established but before any additional wrappers (proxy connects, TLS
+ * handshakes, etc) have been performed.
+ *
+ * @deprecated Use set_tcp_pre_init_handler instead
+ *
+ * @param h The handler to call on tcp pre init.
+ */
+ void set_tcp_init_handler(tcp_init_handler h) {
+ set_tcp_pre_init_handler(h);
+ }
+
+ /// Sets the tcp post init handler
+ /**
+ * The tcp post init handler is called after the tcp connection has been
+ * established and all additional wrappers (proxy connects, TLS handshakes,
+ * etc have been performed. This is fired before any bytes are read or any
+ * WebSocket specific handshake logic has been performed.
+ *
+ * @since 0.3.0
+ *
+ * @param h The handler to call on tcp post init.
+ */
+ void set_tcp_post_init_handler(tcp_init_handler h) {
+ m_tcp_post_init_handler = h;
+ }
+
+ /// Set the proxy to connect through (exception free)
+ /**
+ * The URI passed should be a complete URI including scheme. For example:
+ * http://proxy.example.com:8080/
+ *
+ * The proxy must be set up as an explicit (CONNECT) proxy allowed to
+ * connect to the port you specify. Traffic to the proxy is not encrypted.
+ *
+ * @param uri The full URI of the proxy to connect to.
+ *
+ * @param ec A status value
+ */
+ void set_proxy(std::string const & uri, lib::error_code & ec) {
+ // TODO: return errors for illegal URIs here?
+ // TODO: should https urls be illegal for the moment?
+ m_proxy = uri;
+ m_proxy_data = lib::make_shared<proxy_data>();
+ ec = lib::error_code();
+ }
+
+ /// Set the proxy to connect through (exception)
+ void set_proxy(std::string const & uri) {
+ lib::error_code ec;
+ set_proxy(uri,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Set the basic auth credentials to use (exception free)
+ /**
+ * The URI passed should be a complete URI including scheme. For example:
+ * http://proxy.example.com:8080/
+ *
+ * The proxy must be set up as an explicit proxy
+ *
+ * @param username The username to send
+ *
+ * @param password The password to send
+ *
+ * @param ec A status value
+ */
+ void set_proxy_basic_auth(std::string const & username, std::string const &
+ password, lib::error_code & ec)
+ {
+ if (!m_proxy_data) {
+ ec = make_error_code(websocketpp::error::invalid_state);
+ return;
+ }
+
+ // TODO: username can't contain ':'
+ std::string val = "Basic "+base64_encode(username + ":" + password);
+ m_proxy_data->req.replace_header("Proxy-Authorization",val);
+ ec = lib::error_code();
+ }
+
+ /// Set the basic auth credentials to use (exception)
+ void set_proxy_basic_auth(std::string const & username, std::string const &
+ password)
+ {
+ lib::error_code ec;
+ set_proxy_basic_auth(username,password,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Set the proxy timeout duration (exception free)
+ /**
+ * Duration is in milliseconds. Default value is based on the transport
+ * config
+ *
+ * @param duration The number of milliseconds to wait before aborting the
+ * proxy connection.
+ *
+ * @param ec A status value
+ */
+ void set_proxy_timeout(long duration, lib::error_code & ec) {
+ if (!m_proxy_data) {
+ ec = make_error_code(websocketpp::error::invalid_state);
+ return;
+ }
+
+ m_proxy_data->timeout_proxy = duration;
+ ec = lib::error_code();
+ }
+
+ /// Set the proxy timeout duration (exception)
+ void set_proxy_timeout(long duration) {
+ lib::error_code ec;
+ set_proxy_timeout(duration,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ std::string const & get_proxy() const {
+ return m_proxy;
+ }
+
+ /// Get the remote endpoint address
+ /**
+ * The iostream transport has no information about the ultimate remote
+ * endpoint. It will return the string "iostream transport". To indicate
+ * this.
+ *
+ * TODO: allow user settable remote endpoint addresses if this seems useful
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint() const {
+ lib::error_code ec;
+
+ std::string ret = socket_con_type::get_remote_endpoint(ec);
+
+ if (ec) {
+ m_elog.write(log::elevel::info,ret);
+ return "Unknown";
+ } else {
+ return ret;
+ }
+ }
+
+ /// Get the connection handle
+ connection_hdl get_handle() const {
+ return m_connection_hdl;
+ }
+
+ /// Call back a function after a period of time.
+ /**
+ * Sets a timer that calls back a function after the specified period of
+ * milliseconds. Returns a handle that can be used to cancel the timer.
+ * A cancelled timer will return the error code error::operation_aborted
+ * A timer that expired will return no error.
+ *
+ * @param duration Length of time to wait in milliseconds
+ *
+ * @param callback The function to call back when the timer has expired
+ *
+ * @return A handle that can be used to cancel the timer if it is no longer
+ * needed.
+ */
+ timer_ptr set_timer(long duration, timer_handler callback) {
+ timer_ptr new_timer = lib::make_shared<lib::asio::steady_timer>(
+ lib::ref(*m_io_service),
+ lib::asio::milliseconds(duration)
+ );
+
+ if (config::enable_multithreading) {
+ new_timer->async_wait(m_strand->wrap(lib::bind(
+ &type::handle_timer, get_shared(),
+ new_timer,
+ callback,
+ lib::placeholders::_1
+ )));
+ } else {
+ new_timer->async_wait(lib::bind(
+ &type::handle_timer, get_shared(),
+ new_timer,
+ callback,
+ lib::placeholders::_1
+ ));
+ }
+
+ return new_timer;
+ }
+
+ /// Timer callback
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * TODO: candidate for protected status
+ *
+ * @param post_timer Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec The status code
+ */
+ void handle_timer(timer_ptr, timer_handler callback,
+ lib::asio::error_code const & ec)
+ {
+ if (ec) {
+ if (ec == lib::asio::error::operation_aborted) {
+ callback(make_error_code(transport::error::operation_aborted));
+ } else {
+ log_err(log::elevel::info,"asio handle_timer",ec);
+ callback(make_error_code(error::pass_through));
+ }
+ } else {
+ callback(lib::error_code());
+ }
+ }
+
+ /// Get a pointer to this connection's strand
+ strand_ptr get_strand() {
+ return m_strand;
+ }
+
+ /// Get the internal transport 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.
+ *
+ * Primarily used if you are using mismatched asio / system_error
+ * implementations such as `boost::asio` with `std::system_error`. In these
+ * cases the transport error type is different than the library error type
+ * and some WebSocket++ functions that return transport errors via the
+ * library error code type will be coerced into a catch all `pass_through`
+ * or `tls_error` error. This method will return the original machine
+ * readable transport error in the native type.
+ *
+ * @since 0.7.0
+ *
+ * @return Error code indicating the reason the connection was closed or
+ * failed
+ */
+ lib::asio::error_code get_transport_ec() const {
+ return m_tec;
+ }
+
+ /// Initialize transport for reading
+ /**
+ * init_asio is called once immediately after construction to initialize
+ * Asio components to the io_service
+ *
+ * The transport initialization sequence consists of the following steps:
+ * - Pre-init: the underlying socket is initialized to the point where
+ * bytes may be written. No bytes are actually written in this stage
+ * - Proxy negotiation: if a proxy is set, a request is made to it to start
+ * a tunnel to the final destination. This stage ends when the proxy is
+ * ready to forward the
+ * next byte to the remote endpoint.
+ * - Post-init: Perform any i/o with the remote endpoint, such as setting up
+ * tunnels for encryption. This stage ends when the connection is ready to
+ * read or write the WebSocket handshakes. At this point the original
+ * callback function is called.
+ */
+protected:
+ void init(init_handler callback) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection init");
+ }
+
+ // TODO: pre-init timeout. Right now no implemented socket policies
+ // actually have an asyncronous pre-init
+
+ socket_con_type::pre_init(
+ lib::bind(
+ &type::handle_pre_init,
+ get_shared(),
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ /// initialize the proxy buffers and http parsers
+ /**
+ *
+ * @param authority The address of the server we want the proxy to tunnel to
+ * in the format of a URI authority (host:port)
+ *
+ * @return Status code indicating what errors occurred, if any
+ */
+ lib::error_code proxy_init(std::string const & authority) {
+ if (!m_proxy_data) {
+ return websocketpp::error::make_error_code(
+ websocketpp::error::invalid_state);
+ }
+ m_proxy_data->req.set_version("HTTP/1.1");
+ m_proxy_data->req.set_method("CONNECT");
+
+ m_proxy_data->req.set_uri(authority);
+ m_proxy_data->req.replace_header("Host",authority);
+
+ return lib::error_code();
+ }
+
+ /// Finish constructing the transport
+ /**
+ * init_asio is called once immediately after construction to initialize
+ * Asio components to the io_service.
+ *
+ * @param io_service A pointer to the io_service to register with this
+ * connection
+ *
+ * @return Status code for the success or failure of the initialization
+ */
+ lib::error_code init_asio (io_service_ptr io_service) {
+ m_io_service = io_service;
+
+ if (config::enable_multithreading) {
+ m_strand = lib::make_shared<lib::asio::io_service::strand>(
+ lib::ref(*io_service));
+ }
+
+ lib::error_code ec = socket_con_type::init_asio(io_service, m_strand,
+ m_is_server);
+
+ return ec;
+ }
+
+ void handle_pre_init(init_handler callback, lib::error_code const & ec) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection handle pre_init");
+ }
+
+ if (m_tcp_pre_init_handler) {
+ m_tcp_pre_init_handler(m_connection_hdl);
+ }
+
+ if (ec) {
+ callback(ec);
+ }
+
+ // If we have a proxy set issue a proxy connect, otherwise skip to
+ // post_init
+ if (!m_proxy.empty()) {
+ proxy_write(callback);
+ } else {
+ post_init(callback);
+ }
+ }
+
+ void post_init(init_handler callback) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection post_init");
+ }
+
+ timer_ptr post_timer;
+
+ if (config::timeout_socket_post_init > 0) {
+ post_timer = set_timer(
+ config::timeout_socket_post_init,
+ lib::bind(
+ &type::handle_post_init_timeout,
+ get_shared(),
+ post_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ socket_con_type::post_init(
+ lib::bind(
+ &type::handle_post_init,
+ get_shared(),
+ post_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ /// Post init timeout callback
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * @param post_timer Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec The status code
+ */
+ void handle_post_init_timeout(timer_ptr, init_handler callback,
+ lib::error_code const & ec)
+ {
+ lib::error_code ret_ec;
+
+ if (ec) {
+ if (ec == transport::error::operation_aborted) {
+ m_alog.write(log::alevel::devel,
+ "asio post init timer cancelled");
+ return;
+ }
+
+ log_err(log::elevel::devel,"asio handle_post_init_timeout",ec);
+ ret_ec = ec;
+ } else {
+ if (socket_con_type::get_ec()) {
+ ret_ec = socket_con_type::get_ec();
+ } else {
+ ret_ec = make_error_code(transport::error::timeout);
+ }
+ }
+
+ m_alog.write(log::alevel::devel, "Asio transport post-init timed out");
+ cancel_socket_checked();
+ callback(ret_ec);
+ }
+
+ /// Post init timeout callback
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * @param post_timer Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec The status code
+ */
+ void handle_post_init(timer_ptr post_timer, init_handler callback,
+ lib::error_code const & ec)
+ {
+ if (ec == transport::error::operation_aborted ||
+ (post_timer && lib::asio::is_neg(post_timer->expires_from_now())))
+ {
+ m_alog.write(log::alevel::devel,"post_init cancelled");
+ return;
+ }
+
+ if (post_timer) {
+ post_timer->cancel();
+ }
+
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection handle_post_init");
+ }
+
+ if (m_tcp_post_init_handler) {
+ m_tcp_post_init_handler(m_connection_hdl);
+ }
+
+ callback(ec);
+ }
+
+ void proxy_write(init_handler callback) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection proxy_write");
+ }
+
+ if (!m_proxy_data) {
+ m_elog.write(log::elevel::library,
+ "assertion failed: !m_proxy_data in asio::connection::proxy_write");
+ callback(make_error_code(error::general));
+ return;
+ }
+
+ m_proxy_data->write_buf = m_proxy_data->req.raw();
+
+ m_bufs.push_back(lib::asio::buffer(m_proxy_data->write_buf.data(),
+ m_proxy_data->write_buf.size()));
+
+ m_alog.write(log::alevel::devel,m_proxy_data->write_buf);
+
+ // Set a timer so we don't wait forever for the proxy to respond
+ m_proxy_data->timer = this->set_timer(
+ m_proxy_data->timeout_proxy,
+ lib::bind(
+ &type::handle_proxy_timeout,
+ get_shared(),
+ callback,
+ lib::placeholders::_1
+ )
+ );
+
+ // Send proxy request
+ if (config::enable_multithreading) {
+ lib::asio::async_write(
+ socket_con_type::get_next_layer(),
+ m_bufs,
+ m_strand->wrap(lib::bind(
+ &type::handle_proxy_write, get_shared(),
+ callback,
+ lib::placeholders::_1
+ ))
+ );
+ } else {
+ lib::asio::async_write(
+ socket_con_type::get_next_layer(),
+ m_bufs,
+ lib::bind(
+ &type::handle_proxy_write, get_shared(),
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+ }
+
+ void handle_proxy_timeout(init_handler callback, lib::error_code const & ec)
+ {
+ if (ec == transport::error::operation_aborted) {
+ m_alog.write(log::alevel::devel,
+ "asio handle_proxy_write timer cancelled");
+ return;
+ } else if (ec) {
+ log_err(log::elevel::devel,"asio handle_proxy_write",ec);
+ callback(ec);
+ } else {
+ m_alog.write(log::alevel::devel,
+ "asio handle_proxy_write timer expired");
+ cancel_socket_checked();
+ callback(make_error_code(transport::error::timeout));
+ }
+ }
+
+ void handle_proxy_write(init_handler callback,
+ lib::asio::error_code const & ec)
+ {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,
+ "asio connection handle_proxy_write");
+ }
+
+ m_bufs.clear();
+
+ // Timer expired or the operation was aborted for some reason.
+ // Whatever aborted it will be issuing the callback so we are safe to
+ // return
+ if (ec == lib::asio::error::operation_aborted ||
+ lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
+ {
+ m_elog.write(log::elevel::devel,"write operation aborted");
+ return;
+ }
+
+ if (ec) {
+ log_err(log::elevel::info,"asio handle_proxy_write",ec);
+ m_proxy_data->timer->cancel();
+ callback(make_error_code(error::pass_through));
+ return;
+ }
+
+ proxy_read(callback);
+ }
+
+ void proxy_read(init_handler callback) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection proxy_read");
+ }
+
+ if (!m_proxy_data) {
+ m_elog.write(log::elevel::library,
+ "assertion failed: !m_proxy_data in asio::connection::proxy_read");
+ m_proxy_data->timer->cancel();
+ callback(make_error_code(error::general));
+ return;
+ }
+
+ if (config::enable_multithreading) {
+ lib::asio::async_read_until(
+ socket_con_type::get_next_layer(),
+ m_proxy_data->read_buf,
+ "\r\n\r\n",
+ m_strand->wrap(lib::bind(
+ &type::handle_proxy_read, get_shared(),
+ callback,
+ lib::placeholders::_1, lib::placeholders::_2
+ ))
+ );
+ } else {
+ lib::asio::async_read_until(
+ socket_con_type::get_next_layer(),
+ m_proxy_data->read_buf,
+ "\r\n\r\n",
+ lib::bind(
+ &type::handle_proxy_read, get_shared(),
+ callback,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ );
+ }
+ }
+
+ /// Proxy read callback
+ /**
+ * @param init_handler The function to call back
+ * @param ec The status code
+ * @param bytes_transferred The number of bytes read
+ */
+ void handle_proxy_read(init_handler callback,
+ lib::asio::error_code const & ec, size_t)
+ {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,
+ "asio connection handle_proxy_read");
+ }
+
+ // Timer expired or the operation was aborted for some reason.
+ // Whatever aborted it will be issuing the callback so we are safe to
+ // return
+ if (ec == lib::asio::error::operation_aborted ||
+ lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
+ {
+ m_elog.write(log::elevel::devel,"read operation aborted");
+ return;
+ }
+
+ // At this point there is no need to wait for the timer anymore
+ m_proxy_data->timer->cancel();
+
+ if (ec) {
+ m_elog.write(log::elevel::info,
+ "asio handle_proxy_read error: "+ec.message());
+ callback(make_error_code(error::pass_through));
+ } else {
+ if (!m_proxy_data) {
+ m_elog.write(log::elevel::library,
+ "assertion failed: !m_proxy_data in asio::connection::handle_proxy_read");
+ callback(make_error_code(error::general));
+ return;
+ }
+
+ std::istream input(&m_proxy_data->read_buf);
+
+ m_proxy_data->res.consume(input);
+
+ if (!m_proxy_data->res.headers_ready()) {
+ // we read until the headers were done in theory but apparently
+ // they aren't. Internal endpoint error.
+ callback(make_error_code(error::general));
+ return;
+ }
+
+ m_alog.write(log::alevel::devel,m_proxy_data->res.raw());
+
+ if (m_proxy_data->res.get_status_code() != http::status_code::ok) {
+ // got an error response back
+ // TODO: expose this error in a programmatically accessible way?
+ // if so, see below for an option on how to do this.
+ std::stringstream s;
+ s << "Proxy connection error: "
+ << m_proxy_data->res.get_status_code()
+ << " ("
+ << m_proxy_data->res.get_status_msg()
+ << ")";
+ m_elog.write(log::elevel::info,s.str());
+ callback(make_error_code(error::proxy_failed));
+ return;
+ }
+
+ // we have successfully established a connection to the proxy, now
+ // we can continue and the proxy will transparently forward the
+ // WebSocket connection.
+
+ // TODO: decide if we want an on_proxy callback that would allow
+ // access to the proxy response.
+
+ // free the proxy buffers and req/res objects as they aren't needed
+ // anymore
+ m_proxy_data.reset();
+
+ // Continue with post proxy initialization
+ post_init(callback);
+ }
+ }
+
+ /// read at least num_bytes bytes into buf and then call handler.
+ void async_read_at_least(size_t num_bytes, char *buf, size_t len,
+ read_handler handler)
+ {
+ if (m_alog.static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "asio async_read_at_least: " << num_bytes;
+ m_alog.write(log::alevel::devel,s.str());
+ }
+
+ // TODO: safety vs speed ?
+ // maybe move into an if devel block
+ /*if (num_bytes > len) {
+ m_elog.write(log::elevel::devel,
+ "asio async_read_at_least error::invalid_num_bytes");
+ handler(make_error_code(transport::error::invalid_num_bytes),
+ size_t(0));
+ return;
+ }*/
+
+ if (config::enable_multithreading) {
+ lib::asio::async_read(
+ socket_con_type::get_socket(),
+ lib::asio::buffer(buf,len),
+ lib::asio::transfer_at_least(num_bytes),
+ m_strand->wrap(make_custom_alloc_handler(
+ m_read_handler_allocator,
+ lib::bind(
+ &type::handle_async_read, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ ))
+ );
+ } else {
+ lib::asio::async_read(
+ socket_con_type::get_socket(),
+ lib::asio::buffer(buf,len),
+ lib::asio::transfer_at_least(num_bytes),
+ make_custom_alloc_handler(
+ m_read_handler_allocator,
+ lib::bind(
+ &type::handle_async_read, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ )
+ );
+ }
+
+ }
+
+ void handle_async_read(read_handler handler, lib::asio::error_code const & ec,
+ size_t bytes_transferred)
+ {
+ m_alog.write(log::alevel::devel, "asio con handle_async_read");
+
+ // translate asio error codes into more lib::error_codes
+ lib::error_code tec;
+ if (ec == lib::asio::error::eof) {
+ tec = make_error_code(transport::error::eof);
+ } else if (ec) {
+ // We don't know much more about the error at this point. As our
+ // socket/security policy if it knows more:
+ tec = socket_con_type::translate_ec(ec);
+ m_tec = ec;
+
+ if (tec == transport::error::tls_error ||
+ tec == transport::error::pass_through)
+ {
+ // These are aggregate/catch all errors. Log some human readable
+ // information to the info channel to give library users some
+ // more details about why the upstream method may have failed.
+ log_err(log::elevel::info,"asio async_read_at_least",ec);
+ }
+ }
+ if (handler) {
+ handler(tec,bytes_transferred);
+ } else {
+ // This can happen in cases where the connection is terminated while
+ // the transport is waiting on a read.
+ m_alog.write(log::alevel::devel,
+ "handle_async_read called with null read handler");
+ }
+ }
+
+ /// Initiate a potentially asyncronous write of the given buffer
+ void async_write(const char* buf, size_t len, write_handler handler) {
+ m_bufs.push_back(lib::asio::buffer(buf,len));
+
+ if (config::enable_multithreading) {
+ lib::asio::async_write(
+ socket_con_type::get_socket(),
+ m_bufs,
+ m_strand->wrap(make_custom_alloc_handler(
+ m_write_handler_allocator,
+ lib::bind(
+ &type::handle_async_write, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ ))
+ );
+ } else {
+ lib::asio::async_write(
+ socket_con_type::get_socket(),
+ m_bufs,
+ make_custom_alloc_handler(
+ m_write_handler_allocator,
+ lib::bind(
+ &type::handle_async_write, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ )
+ );
+ }
+ }
+
+ /// Initiate a potentially asyncronous write of the given buffers
+ void async_write(std::vector<buffer> const & bufs, write_handler handler) {
+ std::vector<buffer>::const_iterator it;
+
+ for (it = bufs.begin(); it != bufs.end(); ++it) {
+ m_bufs.push_back(lib::asio::buffer((*it).buf,(*it).len));
+ }
+
+ if (config::enable_multithreading) {
+ lib::asio::async_write(
+ socket_con_type::get_socket(),
+ m_bufs,
+ m_strand->wrap(make_custom_alloc_handler(
+ m_write_handler_allocator,
+ lib::bind(
+ &type::handle_async_write, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ ))
+ );
+ } else {
+ lib::asio::async_write(
+ socket_con_type::get_socket(),
+ m_bufs,
+ make_custom_alloc_handler(
+ m_write_handler_allocator,
+ lib::bind(
+ &type::handle_async_write, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ )
+ );
+ }
+ }
+
+ /// Async write callback
+ /**
+ * @param ec The status code
+ * @param bytes_transferred The number of bytes read
+ */
+ void handle_async_write(write_handler handler, lib::asio::error_code const & ec, size_t) {
+ m_bufs.clear();
+ lib::error_code tec;
+ if (ec) {
+ log_err(log::elevel::info,"asio async_write",ec);
+ tec = make_error_code(transport::error::pass_through);
+ }
+ if (handler) {
+ handler(tec);
+ } else {
+ // This can happen in cases where the connection is terminated while
+ // the transport is waiting on a read.
+ m_alog.write(log::alevel::devel,
+ "handle_async_write called with null write handler");
+ }
+ }
+
+ /// Set Connection Handle
+ /**
+ * See common/connection_hdl.hpp for information
+ *
+ * @param hdl A connection_hdl that the transport will use to refer
+ * to itself
+ */
+ void set_handle(connection_hdl hdl) {
+ m_connection_hdl = hdl;
+ socket_con_type::set_handle(hdl);
+ }
+
+ /// Trigger the on_interrupt handler
+ /**
+ * This needs to be thread safe
+ */
+ lib::error_code interrupt(interrupt_handler handler) {
+ if (config::enable_multithreading) {
+ m_io_service->post(m_strand->wrap(handler));
+ } else {
+ m_io_service->post(handler);
+ }
+ return lib::error_code();
+ }
+
+ lib::error_code dispatch(dispatch_handler handler) {
+ if (config::enable_multithreading) {
+ m_io_service->post(m_strand->wrap(handler));
+ } else {
+ m_io_service->post(handler);
+ }
+ return lib::error_code();
+ }
+
+ /*void handle_interrupt(interrupt_handler handler) {
+ handler();
+ }*/
+
+ /// close and clean up the underlying socket
+ void async_shutdown(shutdown_handler callback) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection async_shutdown");
+ }
+
+ timer_ptr shutdown_timer;
+ shutdown_timer = set_timer(
+ config::timeout_socket_shutdown,
+ lib::bind(
+ &type::handle_async_shutdown_timeout,
+ get_shared(),
+ shutdown_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+
+ socket_con_type::async_shutdown(
+ lib::bind(
+ &type::handle_async_shutdown,
+ get_shared(),
+ shutdown_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ /// Async shutdown timeout handler
+ /**
+ * @param shutdown_timer A pointer to the timer to keep it in scope
+ * @param callback The function to call back
+ * @param ec The status code
+ */
+ void handle_async_shutdown_timeout(timer_ptr, init_handler callback,
+ lib::error_code const & ec)
+ {
+ lib::error_code ret_ec;
+
+ if (ec) {
+ if (ec == transport::error::operation_aborted) {
+ m_alog.write(log::alevel::devel,
+ "asio socket shutdown timer cancelled");
+ return;
+ }
+
+ log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec);
+ ret_ec = ec;
+ } else {
+ ret_ec = make_error_code(transport::error::timeout);
+ }
+
+ m_alog.write(log::alevel::devel,
+ "Asio transport socket shutdown timed out");
+ cancel_socket_checked();
+ callback(ret_ec);
+ }
+
+ void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler
+ callback, lib::asio::error_code const & ec)
+ {
+ if (ec == lib::asio::error::operation_aborted ||
+ lib::asio::is_neg(shutdown_timer->expires_from_now()))
+ {
+ m_alog.write(log::alevel::devel,"async_shutdown cancelled");
+ return;
+ }
+
+ shutdown_timer->cancel();
+
+ lib::error_code tec;
+ if (ec) {
+ if (ec == lib::asio::error::not_connected) {
+ // The socket was already closed when we tried to close it. This
+ // happens periodically (usually if a read or write fails
+ // earlier and if it is a real error will be caught at another
+ // level of the stack.
+ } else {
+ // We don't know anything more about this error, give our
+ // socket/security policy a crack at it.
+ tec = socket_con_type::translate_ec(ec);
+ m_tec = ec;
+
+ if (tec == transport::error::tls_short_read) {
+ // TLS short read at this point is somewhat expected if both
+ // sides try and end the connection at the same time or if
+ // SSLv2 is being used. In general there is nothing that can
+ // be done here other than a low level development log.
+ } else {
+ // all other errors are effectively pass through errors of
+ // some sort so print some detail on the info channel for
+ // library users to look up if needed.
+ log_err(log::elevel::info,"asio async_shutdown",ec);
+ }
+ }
+ } else {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,
+ "asio con handle_async_shutdown");
+ }
+ }
+ callback(tec);
+ }
+
+ /// Cancel the underlying socket and log any errors
+ void cancel_socket_checked() {
+ lib::asio::error_code cec = socket_con_type::cancel_socket();
+ if (cec) {
+ if (cec == lib::asio::error::operation_not_supported) {
+ // cancel not supported on this OS, ignore and log at dev level
+ m_alog.write(log::alevel::devel, "socket cancel not supported");
+ } else {
+ log_err(log::elevel::warn, "socket cancel failed", cec);
+ }
+ }
+ }
+
+private:
+ /// Convenience method for logging the code and message for an error_code
+ template <typename error_type>
+ void log_err(log::level l, const char * msg, const error_type & ec) {
+ std::stringstream s;
+ s << msg << " error: " << ec << " (" << ec.message() << ")";
+ m_elog.write(l,s.str());
+ }
+
+ // static settings
+ const bool m_is_server;
+ alog_type& m_alog;
+ elog_type& m_elog;
+
+ struct proxy_data {
+ proxy_data() : timeout_proxy(config::timeout_proxy) {}
+
+ request_type req;
+ response_type res;
+ std::string write_buf;
+ lib::asio::streambuf read_buf;
+ long timeout_proxy;
+ timer_ptr timer;
+ };
+
+ std::string m_proxy;
+ lib::shared_ptr<proxy_data> m_proxy_data;
+
+ // transport resources
+ io_service_ptr m_io_service;
+ strand_ptr m_strand;
+ connection_hdl m_connection_hdl;
+
+ std::vector<lib::asio::const_buffer> m_bufs;
+
+ /// Detailed internal error code
+ lib::asio::error_code m_tec;
+
+ // Handlers
+ tcp_init_handler m_tcp_pre_init_handler;
+ tcp_init_handler m_tcp_post_init_handler;
+
+ handler_allocator m_read_handler_allocator;
+ handler_allocator m_write_handler_allocator;
+};
+
+
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
diff --git a/websocketpp/transport/asio/endpoint.hpp b/websocketpp/transport/asio/endpoint.hpp
new file mode 100644
index 00000000..46ff24c0
--- /dev/null
+++ b/websocketpp/transport/asio/endpoint.hpp
@@ -0,0 +1,1147 @@
+/*
+ * Copyright (c) 2015, 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_TRANSPORT_ASIO_HPP
+#define WEBSOCKETPP_TRANSPORT_ASIO_HPP
+
+#include <websocketpp/transport/base/endpoint.hpp>
+#include <websocketpp/transport/asio/connection.hpp>
+#include <websocketpp/transport/asio/security/none.hpp>
+
+#include <websocketpp/uri.hpp>
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/functional.hpp>
+
+#include <sstream>
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+namespace asio {
+
+/// Asio based endpoint transport component
+/**
+ * transport::asio::endpoint implements an endpoint transport component using
+ * Asio.
+ */
+template <typename config>
+class endpoint : public config::socket_type {
+public:
+ /// Type of this endpoint transport component
+ typedef endpoint<config> type;
+
+ /// Type of the concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of the socket policy
+ typedef typename config::socket_type socket_type;
+ /// Type of the error logging policy
+ typedef typename config::elog_type elog_type;
+ /// Type of the access logging policy
+ typedef typename config::alog_type alog_type;
+
+ /// Type of the socket connection component
+ typedef typename socket_type::socket_con_type socket_con_type;
+ /// Type of a shared pointer to the socket connection component
+ typedef typename socket_con_type::ptr socket_con_ptr;
+
+ /// Type of the connection transport component associated with this
+ /// endpoint transport component
+ typedef asio::connection<config> transport_con_type;
+ /// Type of a shared pointer to the connection transport component
+ /// associated with this endpoint transport component
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ /// Type of a pointer to the ASIO io_service being used
+ typedef lib::asio::io_service * io_service_ptr;
+ /// Type of a shared pointer to the acceptor being used
+ typedef lib::shared_ptr<lib::asio::ip::tcp::acceptor> acceptor_ptr;
+ /// Type of a shared pointer to the resolver being used
+ typedef lib::shared_ptr<lib::asio::ip::tcp::resolver> resolver_ptr;
+ /// Type of timer handle
+ typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
+ /// Type of a shared pointer to an io_service work object
+ typedef lib::shared_ptr<lib::asio::io_service::work> work_ptr;
+
+ // generate and manage our own io_service
+ explicit endpoint()
+ : m_io_service(NULL)
+ , m_external_io_service(false)
+ , m_listen_backlog(0)
+ , m_reuse_addr(false)
+ , m_state(UNINITIALIZED)
+ {
+ //std::cout << "transport::asio::endpoint constructor" << std::endl;
+ }
+
+ ~endpoint() {
+ // clean up our io_service if we were initialized with an internal one.
+
+ // Explicitly destroy local objects
+ m_acceptor.reset();
+ m_resolver.reset();
+ m_work.reset();
+ if (m_state != UNINITIALIZED && !m_external_io_service) {
+ delete m_io_service;
+ }
+ }
+
+ /// transport::asio objects are moveable but not copyable or assignable.
+ /// The following code sets this situation up based on whether or not we
+ /// have C++11 support or not
+#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ endpoint(const endpoint & src) = delete;
+ endpoint& operator= (const endpoint & rhs) = delete;
+#else
+private:
+ endpoint(const endpoint & src);
+ endpoint & operator= (const endpoint & rhs);
+public:
+#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+
+#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_
+ endpoint (endpoint && src)
+ : config::socket_type(std::move(src))
+ , m_tcp_pre_init_handler(src.m_tcp_pre_init_handler)
+ , m_tcp_post_init_handler(src.m_tcp_post_init_handler)
+ , m_io_service(src.m_io_service)
+ , m_external_io_service(src.m_external_io_service)
+ , m_acceptor(src.m_acceptor)
+ , m_listen_backlog(lib::asio::socket_base::max_connections)
+ , m_reuse_addr(src.m_reuse_addr)
+ , m_elog(src.m_elog)
+ , m_alog(src.m_alog)
+ , m_state(src.m_state)
+ {
+ src.m_io_service = NULL;
+ src.m_external_io_service = false;
+ src.m_acceptor = NULL;
+ src.m_state = UNINITIALIZED;
+ }
+
+ /*endpoint & operator= (const endpoint && rhs) {
+ if (this != &rhs) {
+ m_io_service = rhs.m_io_service;
+ m_external_io_service = rhs.m_external_io_service;
+ m_acceptor = rhs.m_acceptor;
+ m_listen_backlog = rhs.m_listen_backlog;
+ m_reuse_addr = rhs.m_reuse_addr;
+ m_state = rhs.m_state;
+
+ rhs.m_io_service = NULL;
+ rhs.m_external_io_service = false;
+ rhs.m_acceptor = NULL;
+ rhs.m_listen_backlog = lib::asio::socket_base::max_connections;
+ rhs.m_state = UNINITIALIZED;
+
+ // TODO: this needs to be updated
+ }
+ return *this;
+ }*/
+#endif // _WEBSOCKETPP_MOVE_SEMANTICS_
+
+ /// Return whether or not the endpoint produces secure connections.
+ bool is_secure() const {
+ return socket_type::is_secure();
+ }
+
+ /// initialize asio transport with external io_service (exception free)
+ /**
+ * Initialize the ASIO transport policy for this endpoint using the provided
+ * io_service object. asio_init must be called exactly once on any endpoint
+ * that uses transport::asio before it can be used.
+ *
+ * @param ptr A pointer to the io_service to use for asio events
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void init_asio(io_service_ptr ptr, lib::error_code & ec) {
+ if (m_state != UNINITIALIZED) {
+ m_elog->write(log::elevel::library,
+ "asio::init_asio called from the wrong state");
+ using websocketpp::error::make_error_code;
+ ec = make_error_code(websocketpp::error::invalid_state);
+ return;
+ }
+
+ m_alog->write(log::alevel::devel,"asio::init_asio");
+
+ m_io_service = ptr;
+ m_external_io_service = true;
+ m_acceptor = lib::make_shared<lib::asio::ip::tcp::acceptor>(
+ lib::ref(*m_io_service));
+
+ m_state = READY;
+ ec = lib::error_code();
+ }
+
+ /// initialize asio transport with external io_service
+ /**
+ * Initialize the ASIO transport policy for this endpoint using the provided
+ * io_service object. asio_init must be called exactly once on any endpoint
+ * that uses transport::asio before it can be used.
+ *
+ * @param ptr A pointer to the io_service to use for asio events
+ */
+ void init_asio(io_service_ptr ptr) {
+ lib::error_code ec;
+ init_asio(ptr,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Initialize asio transport with internal io_service (exception free)
+ /**
+ * This method of initialization will allocate and use an internally managed
+ * io_service.
+ *
+ * @see init_asio(io_service_ptr ptr)
+ *
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void init_asio(lib::error_code & ec) {
+ // Use a smart pointer until the call is successful and ownership has
+ // successfully been taken. Use unique_ptr when available.
+ // TODO: remove the use of auto_ptr when C++98/03 support is no longer
+ // necessary.
+#ifdef _WEBSOCKETPP_CPP11_MEMORY_
+ lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
+#else
+ lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
+#endif
+ init_asio(service.get(), ec);
+ if( !ec ) service.release(); // Call was successful, transfer ownership
+ m_external_io_service = false;
+ }
+
+ /// Initialize asio transport with internal io_service
+ /**
+ * This method of initialization will allocate and use an internally managed
+ * io_service.
+ *
+ * @see init_asio(io_service_ptr ptr)
+ */
+ void init_asio() {
+ // Use a smart pointer until the call is successful and ownership has
+ // successfully been taken. Use unique_ptr when available.
+ // TODO: remove the use of auto_ptr when C++98/03 support is no longer
+ // necessary.
+#ifdef _WEBSOCKETPP_CPP11_MEMORY_
+ lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
+#else
+ lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
+#endif
+ init_asio( service.get() );
+ // If control got this far without an exception, then ownership has successfully been taken
+ service.release();
+ m_external_io_service = false;
+ }
+
+ /// Sets the tcp pre init handler
+ /**
+ * The tcp pre init handler is called after the raw tcp connection has been
+ * established but before any additional wrappers (proxy connects, TLS
+ * handshakes, etc) have been performed.
+ *
+ * @since 0.3.0
+ *
+ * @param h The handler to call on tcp pre init.
+ */
+ void set_tcp_pre_init_handler(tcp_init_handler h) {
+ m_tcp_pre_init_handler = h;
+ }
+
+ /// Sets the tcp pre init handler (deprecated)
+ /**
+ * The tcp pre init handler is called after the raw tcp connection has been
+ * established but before any additional wrappers (proxy connects, TLS
+ * handshakes, etc) have been performed.
+ *
+ * @deprecated Use set_tcp_pre_init_handler instead
+ *
+ * @param h The handler to call on tcp pre init.
+ */
+ void set_tcp_init_handler(tcp_init_handler h) {
+ set_tcp_pre_init_handler(h);
+ }
+
+ /// Sets the tcp post init handler
+ /**
+ * The tcp post init handler is called after the tcp connection has been
+ * established and all additional wrappers (proxy connects, TLS handshakes,
+ * etc have been performed. This is fired before any bytes are read or any
+ * WebSocket specific handshake logic has been performed.
+ *
+ * @since 0.3.0
+ *
+ * @param h The handler to call on tcp post init.
+ */
+ void set_tcp_post_init_handler(tcp_init_handler h) {
+ m_tcp_post_init_handler = h;
+ }
+
+ /// Sets the maximum length of the queue of pending connections.
+ /**
+ * Sets the maximum length of the queue of pending connections. Increasing
+ * this will allow WebSocket++ to queue additional incoming connections.
+ * Setting it higher may prevent failed connections at high connection rates
+ * but may cause additional latency.
+ *
+ * For this value to take effect you may need to adjust operating system
+ * settings.
+ *
+ * New values affect future calls to listen only.
+ *
+ * A value of zero will use the operating system default. This is the
+ * default value.
+ *
+ * @since 0.3.0
+ *
+ * @param backlog The maximum length of the queue of pending connections
+ */
+ void set_listen_backlog(int backlog) {
+ m_listen_backlog = backlog;
+ }
+
+ /// Sets whether to use the SO_REUSEADDR flag when opening listening sockets
+ /**
+ * Specifies whether or not to use the SO_REUSEADDR TCP socket option. What
+ * this flag does depends on your operating system. Please consult operating
+ * system documentation for more details.
+ *
+ * New values affect future calls to listen only.
+ *
+ * The default is false.
+ *
+ * @since 0.3.0
+ *
+ * @param value Whether or not to use the SO_REUSEADDR option
+ */
+ void set_reuse_addr(bool value) {
+ m_reuse_addr = value;
+ }
+
+ /// Retrieve a reference to the endpoint's io_service
+ /**
+ * The io_service may be an internal or external one. This may be used to
+ * call methods of the io_service that are not explicitly wrapped by the
+ * endpoint.
+ *
+ * This method is only valid after the endpoint has been initialized with
+ * `init_asio`. No error will be returned if it isn't.
+ *
+ * @return A reference to the endpoint's io_service
+ */
+ lib::asio::io_service & get_io_service() {
+ return *m_io_service;
+ }
+
+ /// Get local TCP endpoint
+ /**
+ * Extracts the local endpoint from the acceptor. This represents the
+ * address that WebSocket++ is listening on.
+ *
+ * Sets a bad_descriptor error if the acceptor is not currently listening
+ * or otherwise unavailable.
+ *
+ * @since 0.7.0
+ *
+ * @param ec Set to indicate what error occurred, if any.
+ * @return The local endpoint
+ */
+ lib::asio::ip::tcp::endpoint get_local_endpoint(lib::asio::error_code & ec) {
+ if (m_acceptor) {
+ return m_acceptor->local_endpoint(ec);
+ } else {
+ ec = lib::asio::error::make_error_code(lib::asio::error::bad_descriptor);
+ return lib::asio::ip::tcp::endpoint();
+ }
+ }
+
+ /// Set up endpoint for listening manually (exception free)
+ /**
+ * Bind the internal acceptor using the specified settings. The endpoint
+ * must have been initialized by calling init_asio before listening.
+ *
+ * @param ep An endpoint to read settings from
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void listen(lib::asio::ip::tcp::endpoint const & ep, lib::error_code & ec)
+ {
+ if (m_state != READY) {
+ m_elog->write(log::elevel::library,
+ "asio::listen called from the wrong state");
+ using websocketpp::error::make_error_code;
+ ec = make_error_code(websocketpp::error::invalid_state);
+ return;
+ }
+
+ m_alog->write(log::alevel::devel,"asio::listen");
+
+ lib::asio::error_code bec;
+
+ m_acceptor->open(ep.protocol(),bec);
+ if (!bec) {
+ m_acceptor->set_option(lib::asio::socket_base::reuse_address(m_reuse_addr),bec);
+ }
+ if (!bec) {
+ m_acceptor->bind(ep,bec);
+ }
+ if (!bec) {
+ m_acceptor->listen(m_listen_backlog,bec);
+ }
+ if (bec) {
+ if (m_acceptor->is_open()) {
+ m_acceptor->close();
+ }
+ log_err(log::elevel::info,"asio listen",bec);
+ ec = make_error_code(error::pass_through);
+ } else {
+ m_state = LISTENING;
+ ec = lib::error_code();
+ }
+ }
+
+ /// Set up endpoint for listening manually
+ /**
+ * Bind the internal acceptor using the settings specified by the endpoint e
+ *
+ * @param ep An endpoint to read settings from
+ */
+ void listen(lib::asio::ip::tcp::endpoint const & ep) {
+ lib::error_code ec;
+ listen(ep,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Set up endpoint for listening with protocol and port (exception free)
+ /**
+ * Bind the internal acceptor using the given internet protocol and port.
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * Common options include:
+ * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6()
+ * - IPv4 only: lib::asio::ip::tcp::v4()
+ *
+ * @param internet_protocol The internet protocol to use.
+ * @param port The port to listen on.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ template <typename InternetProtocol>
+ void listen(InternetProtocol const & internet_protocol, uint16_t port,
+ lib::error_code & ec)
+ {
+ lib::asio::ip::tcp::endpoint ep(internet_protocol, port);
+ listen(ep,ec);
+ }
+
+ /// Set up endpoint for listening with protocol and port
+ /**
+ * Bind the internal acceptor using the given internet protocol and port.
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * Common options include:
+ * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6()
+ * - IPv4 only: lib::asio::ip::tcp::v4()
+ *
+ * @param internet_protocol The internet protocol to use.
+ * @param port The port to listen on.
+ */
+ template <typename InternetProtocol>
+ void listen(InternetProtocol const & internet_protocol, uint16_t port)
+ {
+ lib::asio::ip::tcp::endpoint ep(internet_protocol, port);
+ listen(ep);
+ }
+
+ /// Set up endpoint for listening on a port (exception free)
+ /**
+ * Bind the internal acceptor using the given port. The IPv6 protocol with
+ * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use
+ * the overload that allows specifying the protocol explicitly.
+ *
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * @param port The port to listen on.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void listen(uint16_t port, lib::error_code & ec) {
+ listen(lib::asio::ip::tcp::v6(), port, ec);
+ }
+
+ /// Set up endpoint for listening on a port
+ /**
+ * Bind the internal acceptor using the given port. The IPv6 protocol with
+ * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use
+ * the overload that allows specifying the protocol explicitly.
+ *
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * @param port The port to listen on.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void listen(uint16_t port) {
+ listen(lib::asio::ip::tcp::v6(), port);
+ }
+
+ /// Set up endpoint for listening on a host and service (exception free)
+ /**
+ * Bind the internal acceptor using the given host and service. More details
+ * about what host and service can be are available in the Asio
+ * documentation for ip::basic_resolver_query::basic_resolver_query's
+ * constructors.
+ *
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * @param host A string identifying a location. May be a descriptive name or
+ * a numeric address string.
+ * @param service A string identifying the requested service. This may be a
+ * descriptive name or a numeric string corresponding to a port number.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void listen(std::string const & host, std::string const & service,
+ lib::error_code & ec)
+ {
+ using lib::asio::ip::tcp;
+ tcp::resolver r(*m_io_service);
+ tcp::resolver::query query(host, service);
+ tcp::resolver::iterator endpoint_iterator = r.resolve(query);
+ tcp::resolver::iterator end;
+ if (endpoint_iterator == end) {
+ m_elog->write(log::elevel::library,
+ "asio::listen could not resolve the supplied host or service");
+ ec = make_error_code(error::invalid_host_service);
+ return;
+ }
+ listen(*endpoint_iterator,ec);
+ }
+
+ /// Set up endpoint for listening on a host and service
+ /**
+ * Bind the internal acceptor using the given host and service. More details
+ * about what host and service can be are available in the Asio
+ * documentation for ip::basic_resolver_query::basic_resolver_query's
+ * constructors.
+ *
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * @param host A string identifying a location. May be a descriptive name or
+ * a numeric address string.
+ * @param service A string identifying the requested service. This may be a
+ * descriptive name or a numeric string corresponding to a port number.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void listen(std::string const & host, std::string const & service)
+ {
+ lib::error_code ec;
+ listen(host,service,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Stop listening (exception free)
+ /**
+ * Stop listening and accepting new connections. This will not end any
+ * existing connections.
+ *
+ * @since 0.3.0-alpha4
+ * @param ec A status code indicating an error, if any.
+ */
+ void stop_listening(lib::error_code & ec) {
+ if (m_state != LISTENING) {
+ m_elog->write(log::elevel::library,
+ "asio::listen called from the wrong state");
+ using websocketpp::error::make_error_code;
+ ec = make_error_code(websocketpp::error::invalid_state);
+ return;
+ }
+
+ m_acceptor->close();
+ m_state = READY;
+ ec = lib::error_code();
+ }
+
+ /// Stop listening
+ /**
+ * Stop listening and accepting new connections. This will not end any
+ * existing connections.
+ *
+ * @since 0.3.0-alpha4
+ */
+ void stop_listening() {
+ lib::error_code ec;
+ stop_listening(ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Check if the endpoint is listening
+ /**
+ * @return Whether or not the endpoint is listening.
+ */
+ bool is_listening() const {
+ return (m_state == LISTENING);
+ }
+
+ /// wraps the run method of the internal io_service object
+ std::size_t run() {
+ return m_io_service->run();
+ }
+
+ /// wraps the run_one method of the internal io_service object
+ /**
+ * @since 0.3.0-alpha4
+ */
+ std::size_t run_one() {
+ return m_io_service->run_one();
+ }
+
+ /// wraps the stop method of the internal io_service object
+ void stop() {
+ m_io_service->stop();
+ }
+
+ /// wraps the poll method of the internal io_service object
+ std::size_t poll() {
+ return m_io_service->poll();
+ }
+
+ /// wraps the poll_one method of the internal io_service object
+ std::size_t poll_one() {
+ return m_io_service->poll_one();
+ }
+
+ /// wraps the reset method of the internal io_service object
+ void reset() {
+ m_io_service->reset();
+ }
+
+ /// wraps the stopped method of the internal io_service object
+ bool stopped() const {
+ return m_io_service->stopped();
+ }
+
+ /// Marks the endpoint as perpetual, stopping it from exiting when empty
+ /**
+ * Marks the endpoint as perpetual. Perpetual endpoints will not
+ * automatically exit when they run out of connections to process. To stop
+ * a perpetual endpoint call `end_perpetual`.
+ *
+ * An endpoint may be marked perpetual at any time by any thread. It must be
+ * called either before the endpoint has run out of work or before it was
+ * started
+ *
+ * @since 0.3.0
+ */
+ void start_perpetual() {
+ m_work = lib::make_shared<lib::asio::io_service::work>(
+ lib::ref(*m_io_service)
+ );
+ }
+
+ /// Clears the endpoint's perpetual flag, allowing it to exit when empty
+ /**
+ * Clears the endpoint's perpetual flag. This will cause the endpoint's run
+ * method to exit normally when it runs out of connections. If there are
+ * currently active connections it will not end until they are complete.
+ *
+ * @since 0.3.0
+ */
+ void stop_perpetual() {
+ m_work.reset();
+ }
+
+ /// Call back a function after a period of time.
+ /**
+ * Sets a timer that calls back a function after the specified period of
+ * milliseconds. Returns a handle that can be used to cancel the timer.
+ * A cancelled timer will return the error code error::operation_aborted
+ * A timer that expired will return no error.
+ *
+ * @param duration Length of time to wait in milliseconds
+ * @param callback The function to call back when the timer has expired
+ * @return A handle that can be used to cancel the timer if it is no longer
+ * needed.
+ */
+ timer_ptr set_timer(long duration, timer_handler callback) {
+ timer_ptr new_timer = lib::make_shared<lib::asio::steady_timer>(
+ *m_io_service,
+ lib::asio::milliseconds(duration)
+ );
+
+ new_timer->async_wait(
+ lib::bind(
+ &type::handle_timer,
+ this,
+ new_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+
+ return new_timer;
+ }
+
+ /// Timer handler
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * @param t Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec A status code indicating an error, if any.
+ */
+ void handle_timer(timer_ptr, timer_handler callback,
+ lib::asio::error_code const & ec)
+ {
+ if (ec) {
+ if (ec == lib::asio::error::operation_aborted) {
+ callback(make_error_code(transport::error::operation_aborted));
+ } else {
+ m_elog->write(log::elevel::info,
+ "asio handle_timer error: "+ec.message());
+ log_err(log::elevel::info,"asio handle_timer",ec);
+ callback(make_error_code(error::pass_through));
+ }
+ } else {
+ callback(lib::error_code());
+ }
+ }
+
+ /// Accept the next connection attempt and assign it to con (exception free)
+ /**
+ * @param tcon The connection to accept into.
+ * @param callback The function to call when the operation is complete.
+ * @param ec A status code indicating an error, if any.
+ */
+ void async_accept(transport_con_ptr tcon, accept_handler callback,
+ lib::error_code & ec)
+ {
+ if (m_state != LISTENING) {
+ using websocketpp::error::make_error_code;
+ ec = make_error_code(websocketpp::error::async_accept_not_listening);
+ return;
+ }
+
+ m_alog->write(log::alevel::devel, "asio::async_accept");
+
+ if (config::enable_multithreading) {
+ m_acceptor->async_accept(
+ tcon->get_raw_socket(),
+ tcon->get_strand()->wrap(lib::bind(
+ &type::handle_accept,
+ this,
+ callback,
+ lib::placeholders::_1
+ ))
+ );
+ } else {
+ m_acceptor->async_accept(
+ tcon->get_raw_socket(),
+ lib::bind(
+ &type::handle_accept,
+ this,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+ }
+
+ /// Accept the next connection attempt and assign it to con.
+ /**
+ * @param tcon The connection to accept into.
+ * @param callback The function to call when the operation is complete.
+ */
+ void async_accept(transport_con_ptr tcon, accept_handler callback) {
+ lib::error_code ec;
+ async_accept(tcon,callback,ec);
+ if (ec) { throw exception(ec); }
+ }
+protected:
+ /// Initialize logging
+ /**
+ * The loggers are located in the main endpoint class. As such, the
+ * transport doesn't have direct access to them. This method is called
+ * by the endpoint constructor to allow shared logging from the transport
+ * component. These are raw pointers to member variables of the endpoint.
+ * In particular, they cannot be used in the transport constructor as they
+ * haven't been constructed yet, and cannot be used in the transport
+ * destructor as they will have been destroyed by then.
+ */
+ void init_logging(alog_type* a, elog_type* e) {
+ m_alog = a;
+ m_elog = e;
+ }
+
+ void handle_accept(accept_handler callback, lib::asio::error_code const &
+ asio_ec)
+ {
+ lib::error_code ret_ec;
+
+ m_alog->write(log::alevel::devel, "asio::handle_accept");
+
+ if (asio_ec) {
+ if (asio_ec == lib::asio::errc::operation_canceled) {
+ ret_ec = make_error_code(websocketpp::error::operation_canceled);
+ } else {
+ log_err(log::elevel::info,"asio handle_accept",asio_ec);
+ ret_ec = make_error_code(error::pass_through);
+ }
+ }
+
+ callback(ret_ec);
+ }
+
+ /// Initiate a new connection
+ // TODO: there have to be some more failure conditions here
+ void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) {
+ using namespace lib::asio::ip;
+
+ // Create a resolver
+ if (!m_resolver) {
+ m_resolver = lib::make_shared<lib::asio::ip::tcp::resolver>(
+ lib::ref(*m_io_service));
+ }
+
+ tcon->set_uri(u);
+
+ std::string proxy = tcon->get_proxy();
+ std::string host;
+ std::string port;
+
+ if (proxy.empty()) {
+ host = u->get_host();
+ port = u->get_port_str();
+ } else {
+ lib::error_code ec;
+
+ uri_ptr pu = lib::make_shared<uri>(proxy);
+
+ if (!pu->get_valid()) {
+ cb(make_error_code(error::proxy_invalid));
+ return;
+ }
+
+ ec = tcon->proxy_init(u->get_authority());
+ if (ec) {
+ cb(ec);
+ return;
+ }
+
+ host = pu->get_host();
+ port = pu->get_port_str();
+ }
+
+ tcp::resolver::query query(host,port);
+
+ if (m_alog->static_test(log::alevel::devel)) {
+ m_alog->write(log::alevel::devel,
+ "starting async DNS resolve for "+host+":"+port);
+ }
+
+ timer_ptr dns_timer;
+
+ dns_timer = tcon->set_timer(
+ config::timeout_dns_resolve,
+ lib::bind(
+ &type::handle_resolve_timeout,
+ this,
+ dns_timer,
+ cb,
+ lib::placeholders::_1
+ )
+ );
+
+ if (config::enable_multithreading) {
+ m_resolver->async_resolve(
+ query,
+ tcon->get_strand()->wrap(lib::bind(
+ &type::handle_resolve,
+ this,
+ tcon,
+ dns_timer,
+ cb,
+ lib::placeholders::_1,
+ lib::placeholders::_2
+ ))
+ );
+ } else {
+ m_resolver->async_resolve(
+ query,
+ lib::bind(
+ &type::handle_resolve,
+ this,
+ tcon,
+ dns_timer,
+ cb,
+ lib::placeholders::_1,
+ lib::placeholders::_2
+ )
+ );
+ }
+ }
+
+ /// DNS resolution timeout handler
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * @param dns_timer Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec A status code indicating an error, if any.
+ */
+ void handle_resolve_timeout(timer_ptr, connect_handler callback,
+ lib::error_code const & ec)
+ {
+ lib::error_code ret_ec;
+
+ if (ec) {
+ if (ec == transport::error::operation_aborted) {
+ m_alog->write(log::alevel::devel,
+ "asio handle_resolve_timeout timer cancelled");
+ return;
+ }
+
+ log_err(log::elevel::devel,"asio handle_resolve_timeout",ec);
+ ret_ec = ec;
+ } else {
+ ret_ec = make_error_code(transport::error::timeout);
+ }
+
+ m_alog->write(log::alevel::devel,"DNS resolution timed out");
+ m_resolver->cancel();
+ callback(ret_ec);
+ }
+
+ void handle_resolve(transport_con_ptr tcon, timer_ptr dns_timer,
+ connect_handler callback, lib::asio::error_code const & ec,
+ lib::asio::ip::tcp::resolver::iterator iterator)
+ {
+ if (ec == lib::asio::error::operation_aborted ||
+ lib::asio::is_neg(dns_timer->expires_from_now()))
+ {
+ m_alog->write(log::alevel::devel,"async_resolve cancelled");
+ return;
+ }
+
+ dns_timer->cancel();
+
+ if (ec) {
+ log_err(log::elevel::info,"asio async_resolve",ec);
+ callback(make_error_code(error::pass_through));
+ return;
+ }
+
+ if (m_alog->static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "Async DNS resolve successful. Results: ";
+
+ lib::asio::ip::tcp::resolver::iterator it, end;
+ for (it = iterator; it != end; ++it) {
+ s << (*it).endpoint() << " ";
+ }
+
+ m_alog->write(log::alevel::devel,s.str());
+ }
+
+ m_alog->write(log::alevel::devel,"Starting async connect");
+
+ timer_ptr con_timer;
+
+ con_timer = tcon->set_timer(
+ config::timeout_connect,
+ lib::bind(
+ &type::handle_connect_timeout,
+ this,
+ tcon,
+ con_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+
+ if (config::enable_multithreading) {
+ lib::asio::async_connect(
+ tcon->get_raw_socket(),
+ iterator,
+ tcon->get_strand()->wrap(lib::bind(
+ &type::handle_connect,
+ this,
+ tcon,
+ con_timer,
+ callback,
+ lib::placeholders::_1
+ ))
+ );
+ } else {
+ lib::asio::async_connect(
+ tcon->get_raw_socket(),
+ iterator,
+ lib::bind(
+ &type::handle_connect,
+ this,
+ tcon,
+ con_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+ }
+
+ /// Asio connect timeout handler
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * @param tcon Pointer to the transport connection that is being connected
+ * @param con_timer Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec A status code indicating an error, if any.
+ */
+ void handle_connect_timeout(transport_con_ptr tcon, timer_ptr,
+ connect_handler callback, lib::error_code const & ec)
+ {
+ lib::error_code ret_ec;
+
+ if (ec) {
+ if (ec == transport::error::operation_aborted) {
+ m_alog->write(log::alevel::devel,
+ "asio handle_connect_timeout timer cancelled");
+ return;
+ }
+
+ log_err(log::elevel::devel,"asio handle_connect_timeout",ec);
+ ret_ec = ec;
+ } else {
+ ret_ec = make_error_code(transport::error::timeout);
+ }
+
+ m_alog->write(log::alevel::devel,"TCP connect timed out");
+ tcon->cancel_socket_checked();
+ callback(ret_ec);
+ }
+
+ void handle_connect(transport_con_ptr tcon, timer_ptr con_timer,
+ connect_handler callback, lib::asio::error_code const & ec)
+ {
+ if (ec == lib::asio::error::operation_aborted ||
+ lib::asio::is_neg(con_timer->expires_from_now()))
+ {
+ m_alog->write(log::alevel::devel,"async_connect cancelled");
+ return;
+ }
+
+ con_timer->cancel();
+
+ if (ec) {
+ log_err(log::elevel::info,"asio async_connect",ec);
+ callback(make_error_code(error::pass_through));
+ return;
+ }
+
+ if (m_alog->static_test(log::alevel::devel)) {
+ m_alog->write(log::alevel::devel,
+ "Async connect to "+tcon->get_remote_endpoint()+" successful.");
+ }
+
+ callback(lib::error_code());
+ }
+
+ /// Initialize a connection
+ /**
+ * init is called by an endpoint once for each newly created connection.
+ * It's purpose is to give the transport policy the chance to perform any
+ * transport specific initialization that couldn't be done via the default
+ * constructor.
+ *
+ * @param tcon A pointer to the transport portion of the connection.
+ *
+ * @return A status code indicating the success or failure of the operation
+ */
+ lib::error_code init(transport_con_ptr tcon) {
+ m_alog->write(log::alevel::devel, "transport::asio::init");
+
+ // Initialize the connection socket component
+ socket_type::init(lib::static_pointer_cast<socket_con_type,
+ transport_con_type>(tcon));
+
+ lib::error_code ec;
+
+ ec = tcon->init_asio(m_io_service);
+ if (ec) {return ec;}
+
+ tcon->set_tcp_pre_init_handler(m_tcp_pre_init_handler);
+ tcon->set_tcp_post_init_handler(m_tcp_post_init_handler);
+
+ return lib::error_code();
+ }
+private:
+ /// Convenience method for logging the code and message for an error_code
+ 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());
+ }
+
+ enum state {
+ UNINITIALIZED = 0,
+ READY = 1,
+ LISTENING = 2
+ };
+
+ // Handlers
+ tcp_init_handler m_tcp_pre_init_handler;
+ tcp_init_handler m_tcp_post_init_handler;
+
+ // Network Resources
+ io_service_ptr m_io_service;
+ bool m_external_io_service;
+ acceptor_ptr m_acceptor;
+ resolver_ptr m_resolver;
+ work_ptr m_work;
+
+ // Network constants
+ int m_listen_backlog;
+ bool m_reuse_addr;
+
+ elog_type* m_elog;
+ alog_type* m_alog;
+
+ // Transport state
+ state m_state;
+};
+
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_ASIO_HPP
diff --git a/websocketpp/transport/asio/security/base.hpp b/websocketpp/transport/asio/security/base.hpp
new file mode 100644
index 00000000..0f08f404
--- /dev/null
+++ b/websocketpp/transport/asio/security/base.hpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2015, 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_TRANSPORT_ASIO_SOCKET_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP
+
+#include <websocketpp/common/asio.hpp>
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/connection_hdl.hpp>
+
+#include <string>
+
+// Interface that sockets/security policies must implement
+
+/*
+ * Endpoint Interface
+ *
+ * bool is_secure() const;
+ * @return Whether or not the endpoint creates secure connections
+ *
+ * lib::error_code init(socket_con_ptr scon);
+ * Called by the transport after a new connection is created to initialize
+ * the socket component of the connection.
+ * @param scon Pointer to the socket component of the connection
+ * @return Error code (empty on success)
+ */
+
+
+// Connection
+// TODO
+// set_hostname(std::string hostname)
+// pre_init(init_handler);
+// post_init(init_handler);
+
+namespace websocketpp {
+namespace transport {
+namespace asio {
+namespace socket {
+
+typedef lib::function<void(lib::asio::error_code const &)> shutdown_handler;
+
+/**
+ * The transport::asio::socket::* classes are a set of security/socket related
+ * policies and support code for the ASIO transport types.
+ */
+
+/// Errors related to asio transport sockets
+namespace error {
+ enum value {
+ /// Catch-all error for security policy errors that don't fit in other
+ /// categories
+ security = 1,
+
+ /// Catch-all error for socket component errors that don't fit in other
+ /// categories
+ socket,
+
+ /// A function was called in a state that it was illegal to do so.
+ invalid_state,
+
+ /// The application was prompted to provide a TLS context and it was
+ /// empty or otherwise invalid
+ invalid_tls_context,
+
+ /// TLS Handshake Timeout
+ tls_handshake_timeout,
+
+ /// pass_through from underlying library
+ pass_through,
+
+ /// Required tls_init handler not present
+ missing_tls_init_handler,
+
+ /// TLS Handshake Failed
+ tls_handshake_failed,
+
+ /// Failed to set TLS SNI hostname
+ tls_failed_sni_hostname
+ };
+} // namespace error
+
+/// Error category related to asio transport socket policies
+class socket_category : public lib::error_category {
+public:
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport.asio.socket";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case error::security:
+ return "Security policy error";
+ case error::socket:
+ return "Socket component error";
+ case error::invalid_state:
+ return "Invalid state";
+ case error::invalid_tls_context:
+ return "Invalid or empty TLS context supplied";
+ case error::tls_handshake_timeout:
+ return "TLS handshake timed out";
+ case error::pass_through:
+ return "Pass through from socket policy";
+ case error::missing_tls_init_handler:
+ return "Required tls_init handler not present.";
+ case error::tls_handshake_failed:
+ return "TLS handshake failed";
+ case error::tls_failed_sni_hostname:
+ return "Failed to set TLS SNI hostname";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+inline lib::error_category const & get_socket_category() {
+ static socket_category instance;
+ return instance;
+}
+
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_socket_category());
+}
+
+/// Type of asio transport socket policy initialization handlers
+typedef lib::function<void(const lib::error_code&)> init_handler;
+
+} // namespace socket
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP
diff --git a/websocketpp/transport/asio/security/none.hpp b/websocketpp/transport/asio/security/none.hpp
new file mode 100644
index 00000000..0e68a65c
--- /dev/null
+++ b/websocketpp/transport/asio/security/none.hpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2015, 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_TRANSPORT_SECURITY_NONE_HPP
+#define WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP
+
+#include <websocketpp/uri.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+#include <websocketpp/transport/asio/security/base.hpp>
+
+#include <websocketpp/common/asio.hpp>
+#include <websocketpp/common/memory.hpp>
+
+#include <sstream>
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+namespace asio {
+/// A socket policy for the asio transport that implements a plain, unencrypted
+/// socket
+namespace basic_socket {
+
+/// The signature of the socket init handler for this socket policy
+typedef lib::function<void(connection_hdl,lib::asio::ip::tcp::socket&)>
+ socket_init_handler;
+
+/// Basic Asio connection socket component
+/**
+ * transport::asio::basic_socket::connection implements a connection socket
+ * component using Asio ip::tcp::socket.
+ */
+class connection : public lib::enable_shared_from_this<connection> {
+public:
+ /// Type of this connection socket component
+ typedef connection type;
+ /// Type of a shared pointer to this connection socket component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of a pointer to the Asio io_service being used
+ typedef lib::asio::io_service* io_service_ptr;
+ /// Type of a pointer to the Asio io_service strand being used
+ typedef lib::shared_ptr<lib::asio::io_service::strand> strand_ptr;
+ /// Type of the ASIO socket being used
+ typedef lib::asio::ip::tcp::socket socket_type;
+ /// Type of a shared pointer to the socket being used.
+ typedef lib::shared_ptr<socket_type> socket_ptr;
+
+ explicit connection() : m_state(UNINITIALIZED) {
+ //std::cout << "transport::asio::basic_socket::connection constructor"
+ // << std::endl;
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return shared_from_this();
+ }
+
+ /// Check whether or not this connection is secure
+ /**
+ * @return Whether or not this connection is secure
+ */
+ bool is_secure() const {
+ return false;
+ }
+
+ /// Set the socket initialization handler
+ /**
+ * The socket initialization handler is called after the socket object is
+ * created but before it is used. This gives the application a chance to
+ * set any Asio socket options it needs.
+ *
+ * @param h The new socket_init_handler
+ */
+ void set_socket_init_handler(socket_init_handler h) {
+ m_socket_init_handler = h;
+ }
+
+ /// Retrieve a pointer to the underlying socket
+ /**
+ * This is used internally. It can also be used to set socket options, etc
+ */
+ lib::asio::ip::tcp::socket & get_socket() {
+ return *m_socket;
+ }
+
+ /// Retrieve a pointer to the underlying socket
+ /**
+ * This is used internally.
+ */
+ lib::asio::ip::tcp::socket & get_next_layer() {
+ return *m_socket;
+ }
+
+ /// Retrieve a pointer to the underlying socket
+ /**
+ * This is used internally. It can also be used to set socket options, etc
+ */
+ lib::asio::ip::tcp::socket & get_raw_socket() {
+ return *m_socket;
+ }
+
+ /// Get the remote endpoint address
+ /**
+ * The iostream transport has no information about the ultimate remote
+ * endpoint. It will return the string "iostream transport". To indicate
+ * this.
+ *
+ * TODO: allow user settable remote endpoint addresses if this seems useful
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint(lib::error_code & ec) const {
+ std::stringstream s;
+
+ lib::asio::error_code aec;
+ lib::asio::ip::tcp::endpoint ep = m_socket->remote_endpoint(aec);
+
+ if (aec) {
+ ec = error::make_error_code(error::pass_through);
+ s << "Error getting remote endpoint: " << aec
+ << " (" << aec.message() << ")";
+ return s.str();
+ } else {
+ ec = lib::error_code();
+ s << ep;
+ return s.str();
+ }
+ }
+protected:
+ /// Perform one time initializations
+ /**
+ * init_asio is called once immediately after construction to initialize
+ * Asio components to the io_service
+ *
+ * @param service A pointer to the endpoint's io_service
+ * @param strand A shared pointer to the connection's asio strand
+ * @param is_server Whether or not the endpoint is a server or not.
+ */
+ lib::error_code init_asio (io_service_ptr service, strand_ptr, bool)
+ {
+ if (m_state != UNINITIALIZED) {
+ return socket::make_error_code(socket::error::invalid_state);
+ }
+
+ m_socket = lib::make_shared<lib::asio::ip::tcp::socket>(
+ lib::ref(*service));
+
+ m_state = READY;
+
+ return lib::error_code();
+ }
+
+ /// Set uri hook
+ /**
+ * Called by the transport as a connection is being established to provide
+ * the uri being connected to to the security/socket layer.
+ *
+ * This socket policy doesn't use the uri so it is ignored.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr) {}
+
+ /// Pre-initialize security policy
+ /**
+ * Called by the transport after a new connection is created to initialize
+ * the socket component of the connection. This method is not allowed to
+ * write any bytes to the wire. This initialization happens before any
+ * proxies or other intermediate wrappers are negotiated.
+ *
+ * @param callback Handler to call back with completion information
+ */
+ void pre_init(init_handler callback) {
+ if (m_state != READY) {
+ callback(socket::make_error_code(socket::error::invalid_state));
+ return;
+ }
+
+ if (m_socket_init_handler) {
+ m_socket_init_handler(m_hdl,*m_socket);
+ }
+
+ m_state = READING;
+
+ callback(lib::error_code());
+ }
+
+ /// Post-initialize security policy
+ /**
+ * Called by the transport after all intermediate proxies have been
+ * negotiated. This gives the security policy the chance to talk with the
+ * real remote endpoint for a bit before the websocket handshake.
+ *
+ * @param callback Handler to call back with completion information
+ */
+ void post_init(init_handler callback) {
+ callback(lib::error_code());
+ }
+
+ /// Sets the connection handle
+ /**
+ * The connection handle is passed to any handlers to identify the
+ * connection
+ *
+ * @param hdl The new handle
+ */
+ void set_handle(connection_hdl hdl) {
+ m_hdl = hdl;
+ }
+
+ /// Cancel all async operations on this socket
+ /**
+ * Attempts to cancel all async operations on this socket and reports any
+ * failures.
+ *
+ * NOTE: Windows XP and earlier do not support socket cancellation.
+ *
+ * @return The error that occurred, if any.
+ */
+ lib::asio::error_code cancel_socket() {
+ lib::asio::error_code ec;
+ m_socket->cancel(ec);
+ return ec;
+ }
+
+ void async_shutdown(socket::shutdown_handler h) {
+ lib::asio::error_code ec;
+ m_socket->shutdown(lib::asio::ip::tcp::socket::shutdown_both, ec);
+ h(ec);
+ }
+
+ lib::error_code get_ec() const {
+ return lib::error_code();
+ }
+
+ /// Translate any security policy specific information about an error code
+ /**
+ * Translate_ec takes an Asio error code and attempts to convert its value
+ * to an appropriate websocketpp error code. In the case that the Asio and
+ * Websocketpp error types are the same (such as using boost::asio and
+ * boost::system_error or using standalone asio and std::system_error the
+ * code will be passed through natively.
+ *
+ * In the case of a mismatch (boost::asio with std::system_error) a
+ * translated code will be returned. The plain socket policy does not have
+ * any additional information so all such errors will be reported as the
+ * generic transport pass_through error.
+ *
+ * @since 0.3.0
+ *
+ * @param ec The error code to translate_ec
+ * @return The translated error code
+ */
+ template <typename ErrorCodeType>
+ lib::error_code translate_ec(ErrorCodeType) {
+ // We don't know any more information about this error so pass through
+ return make_error_code(transport::error::pass_through);
+ }
+
+ /// Overload of translate_ec to catch cases where lib::error_code is the
+ /// same type as lib::asio::error_code
+ lib::error_code translate_ec(lib::error_code ec) {
+ // We don't know any more information about this error, but the error is
+ // the same type as the one we are translating to, so pass through
+ // untranslated.
+ return ec;
+ }
+private:
+ enum state {
+ UNINITIALIZED = 0,
+ READY = 1,
+ READING = 2
+ };
+
+ socket_ptr m_socket;
+ state m_state;
+
+ connection_hdl m_hdl;
+ socket_init_handler m_socket_init_handler;
+};
+
+/// Basic ASIO endpoint socket component
+/**
+ * transport::asio::basic_socket::endpoint implements an endpoint socket
+ * component that uses Boost ASIO's ip::tcp::socket.
+ */
+class endpoint {
+public:
+ /// The type of this endpoint socket component
+ typedef endpoint type;
+
+ /// The type of the corresponding connection socket component
+ typedef connection socket_con_type;
+ /// The type of a shared pointer to the corresponding connection socket
+ /// component.
+ typedef socket_con_type::ptr socket_con_ptr;
+
+ explicit endpoint() {}
+
+ /// Checks whether the endpoint creates secure connections
+ /**
+ * @return Whether or not the endpoint creates secure connections
+ */
+ bool is_secure() const {
+ return false;
+ }
+
+ /// Set socket init handler
+ /**
+ * The socket init handler is called after a connection's socket is created
+ * but before it is used. This gives the end application an opportunity to
+ * set asio socket specific parameters.
+ *
+ * @param h The new socket_init_handler
+ */
+ void set_socket_init_handler(socket_init_handler h) {
+ m_socket_init_handler = h;
+ }
+protected:
+ /// Initialize a connection
+ /**
+ * Called by the transport after a new connection is created to initialize
+ * the socket component of the connection.
+ *
+ * @param scon Pointer to the socket component of the connection
+ *
+ * @return Error code (empty on success)
+ */
+ lib::error_code init(socket_con_ptr scon) {
+ scon->set_socket_init_handler(m_socket_init_handler);
+ return lib::error_code();
+ }
+private:
+ socket_init_handler m_socket_init_handler;
+};
+
+} // namespace basic_socket
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP
diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp
new file mode 100644
index 00000000..7b32db81
--- /dev/null
+++ b/websocketpp/transport/asio/security/tls.hpp
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 2015, 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_TRANSPORT_SECURITY_TLS_HPP
+#define WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP
+
+#include <websocketpp/transport/asio/security/base.hpp>
+
+#include <websocketpp/uri.hpp>
+
+#include <websocketpp/common/asio_ssl.hpp>
+#include <websocketpp/common/asio.hpp>
+#include <websocketpp/common/connection_hdl.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/memory.hpp>
+
+#include <sstream>
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+namespace asio {
+/// A socket policy for the asio transport that implements a TLS encrypted
+/// socket by wrapping with an asio::ssl::stream
+namespace tls_socket {
+
+/// The signature of the socket_init_handler for this socket policy
+typedef lib::function<void(connection_hdl,lib::asio::ssl::stream<
+ lib::asio::ip::tcp::socket>&)> socket_init_handler;
+/// The signature of the tls_init_handler for this socket policy
+typedef lib::function<lib::shared_ptr<lib::asio::ssl::context>(connection_hdl)>
+ tls_init_handler;
+
+/// TLS enabled Asio connection socket component
+/**
+ * transport::asio::tls_socket::connection implements a secure connection socket
+ * component that uses Asio's ssl::stream to wrap an ip::tcp::socket.
+ */
+class connection : public lib::enable_shared_from_this<connection> {
+public:
+ /// Type of this connection socket component
+ typedef connection type;
+ /// Type of a shared pointer to this connection socket component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of the ASIO socket being used
+ typedef lib::asio::ssl::stream<lib::asio::ip::tcp::socket> socket_type;
+ /// Type of a shared pointer to the ASIO socket being used
+ typedef lib::shared_ptr<socket_type> socket_ptr;
+ /// Type of a pointer to the ASIO io_service being used
+ typedef lib::asio::io_service * io_service_ptr;
+ /// Type of a pointer to the ASIO io_service strand being used
+ typedef lib::shared_ptr<lib::asio::io_service::strand> strand_ptr;
+ /// Type of a shared pointer to the ASIO TLS context being used
+ typedef lib::shared_ptr<lib::asio::ssl::context> context_ptr;
+
+ explicit connection() {
+ //std::cout << "transport::asio::tls_socket::connection constructor"
+ // << std::endl;
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return shared_from_this();
+ }
+
+ /// Check whether or not this connection is secure
+ /**
+ * @return Whether or not this connection is secure
+ */
+ bool is_secure() const {
+ return true;
+ }
+
+ /// Retrieve a pointer to the underlying socket
+ /**
+ * This is used internally. It can also be used to set socket options, etc
+ */
+ socket_type::lowest_layer_type & get_raw_socket() {
+ return m_socket->lowest_layer();
+ }
+
+ /// Retrieve a pointer to the layer below the ssl stream
+ /**
+ * This is used internally.
+ */
+ socket_type::next_layer_type & get_next_layer() {
+ return m_socket->next_layer();
+ }
+
+ /// Retrieve a pointer to the wrapped socket
+ /**
+ * This is used internally.
+ */
+ socket_type & get_socket() {
+ return *m_socket;
+ }
+
+ /// Set the socket initialization handler
+ /**
+ * The socket initialization handler is called after the socket object is
+ * created but before it is used. This gives the application a chance to
+ * set any ASIO socket options it needs.
+ *
+ * @param h The new socket_init_handler
+ */
+ void set_socket_init_handler(socket_init_handler h) {
+ m_socket_init_handler = h;
+ }
+
+ /// Set TLS init handler
+ /**
+ * The tls init handler is called when needed to request a TLS context for
+ * the library to use. A TLS init handler must be set and it must return a
+ * valid TLS context in order for this endpoint to be able to initialize
+ * TLS connections
+ *
+ * @param h The new tls_init_handler
+ */
+ void set_tls_init_handler(tls_init_handler h) {
+ m_tls_init_handler = h;
+ }
+
+ /// Get the remote endpoint address
+ /**
+ * The iostream transport has no information about the ultimate remote
+ * endpoint. It will return the string "iostream transport". To indicate
+ * this.
+ *
+ * TODO: allow user settable remote endpoint addresses if this seems useful
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint(lib::error_code & ec) const {
+ std::stringstream s;
+
+ lib::asio::error_code aec;
+ lib::asio::ip::tcp::endpoint ep = m_socket->lowest_layer().remote_endpoint(aec);
+
+ if (aec) {
+ ec = error::make_error_code(error::pass_through);
+ s << "Error getting remote endpoint: " << aec
+ << " (" << aec.message() << ")";
+ return s.str();
+ } else {
+ ec = lib::error_code();
+ s << ep;
+ return s.str();
+ }
+ }
+protected:
+ /// Perform one time initializations
+ /**
+ * init_asio is called once immediately after construction to initialize
+ * Asio components to the io_service
+ *
+ * @param service A pointer to the endpoint's io_service
+ * @param strand A pointer to the connection's strand
+ * @param is_server Whether or not the endpoint is a server or not.
+ */
+ lib::error_code init_asio (io_service_ptr service, strand_ptr strand,
+ bool is_server)
+ {
+ if (!m_tls_init_handler) {
+ return socket::make_error_code(socket::error::missing_tls_init_handler);
+ }
+ m_context = m_tls_init_handler(m_hdl);
+
+ if (!m_context) {
+ return socket::make_error_code(socket::error::invalid_tls_context);
+ }
+ m_socket = lib::make_shared<socket_type>(
+ _WEBSOCKETPP_REF(*service),lib::ref(*m_context));
+
+ m_io_service = service;
+ m_strand = strand;
+ m_is_server = is_server;
+
+ return lib::error_code();
+ }
+
+ /// Set hostname hook
+ /**
+ * Called by the transport as a connection is being established to provide
+ * the hostname being connected to to the security/socket layer.
+ *
+ * This socket policy uses the hostname to set the appropriate TLS SNI
+ * header.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr u) {
+ m_uri = u;
+ }
+
+ /// Pre-initialize security policy
+ /**
+ * Called by the transport after a new connection is created to initialize
+ * the socket component of the connection. This method is not allowed to
+ * write any bytes to the wire. This initialization happens before any
+ * proxies or other intermediate wrappers are negotiated.
+ *
+ * @param callback Handler to call back with completion information
+ */
+ void pre_init(init_handler callback) {
+ // TODO: is this the best way to check whether this function is
+ // available in the version of OpenSSL being used?
+ // TODO: consider case where host is an IP address
+#if OPENSSL_VERSION_NUMBER >= 0x90812f
+ if (!m_is_server) {
+ // For clients on systems with a suitable OpenSSL version, set the
+ // TLS SNI hostname header so connecting to TLS servers using SNI
+ // will work.
+ long res = SSL_set_tlsext_host_name(
+ get_socket().native_handle(), m_uri->get_host().c_str());
+ if (!(1 == res)) {
+ callback(socket::make_error_code(socket::error::tls_failed_sni_hostname));
+ }
+ }
+#endif
+
+ if (m_socket_init_handler) {
+ m_socket_init_handler(m_hdl,get_socket());
+ }
+
+ callback(lib::error_code());
+ }
+
+ /// Post-initialize security policy
+ /**
+ * Called by the transport after all intermediate proxies have been
+ * negotiated. This gives the security policy the chance to talk with the
+ * real remote endpoint for a bit before the websocket handshake.
+ *
+ * @param callback Handler to call back with completion information
+ */
+ void post_init(init_handler callback) {
+ m_ec = socket::make_error_code(socket::error::tls_handshake_timeout);
+
+ // TLS handshake
+ if (m_strand) {
+ m_socket->async_handshake(
+ get_handshake_type(),
+ m_strand->wrap(lib::bind(
+ &type::handle_init, get_shared(),
+ callback,
+ lib::placeholders::_1
+ ))
+ );
+ } else {
+ m_socket->async_handshake(
+ get_handshake_type(),
+ lib::bind(
+ &type::handle_init, get_shared(),
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+ }
+
+ /// Sets the connection handle
+ /**
+ * The connection handle is passed to any handlers to identify the
+ * connection
+ *
+ * @param hdl The new handle
+ */
+ void set_handle(connection_hdl hdl) {
+ m_hdl = hdl;
+ }
+
+ void handle_init(init_handler callback,lib::asio::error_code const & ec) {
+ if (ec) {
+ m_ec = socket::make_error_code(socket::error::tls_handshake_failed);
+ } else {
+ m_ec = lib::error_code();
+ }
+
+ callback(m_ec);
+ }
+
+ lib::error_code get_ec() const {
+ return m_ec;
+ }
+
+ /// Cancel all async operations on this socket
+ /**
+ * Attempts to cancel all async operations on this socket and reports any
+ * failures.
+ *
+ * NOTE: Windows XP and earlier do not support socket cancellation.
+ *
+ * @return The error that occurred, if any.
+ */
+ lib::asio::error_code cancel_socket() {
+ lib::asio::error_code ec;
+ get_raw_socket().cancel(ec);
+ return ec;
+ }
+
+ void async_shutdown(socket::shutdown_handler callback) {
+ if (m_strand) {
+ m_socket->async_shutdown(m_strand->wrap(callback));
+ } else {
+ m_socket->async_shutdown(callback);
+ }
+ }
+
+ /// Translate any security policy specific information about an error code
+ /**
+ * Translate_ec takes an Asio error code and attempts to convert its value
+ * to an appropriate websocketpp error code. In the case that the Asio and
+ * Websocketpp error types are the same (such as using boost::asio and
+ * boost::system_error or using standalone asio and std::system_error the
+ * code will be passed through natively.
+ *
+ * In the case of a mismatch (boost::asio with std::system_error) a
+ * translated code will be returned. Any error that is determined to be
+ * related to TLS but does not have a more specific websocketpp error code
+ * is returned under the catch all error `tls_error`. Non-TLS related errors
+ * are returned as the transport generic error `pass_through`
+ *
+ * @since 0.3.0
+ *
+ * @param ec The error code to translate_ec
+ * @return The translated error code
+ */
+ template <typename ErrorCodeType>
+ lib::error_code translate_ec(ErrorCodeType ec) {
+ if (ec.category() == lib::asio::error::get_ssl_category()) {
+ if (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ) {
+ return make_error_code(transport::error::tls_short_read);
+ } else {
+ // We know it is a TLS related error, but otherwise don't know
+ // more. Pass through as TLS generic.
+ return make_error_code(transport::error::tls_error);
+ }
+ } else {
+ // We don't know any more information about this error so pass
+ // through
+ return make_error_code(transport::error::pass_through);
+ }
+ }
+
+ /// Overload of translate_ec to catch cases where lib::error_code is the
+ /// same type as lib::asio::error_code
+ lib::error_code translate_ec(lib::error_code ec) {
+ // Normalize the tls_short_read error as it is used by the library and
+ // needs a consistent value. All other errors pass through natively.
+ // TODO: how to get the SSL category from std::error?
+ /*if (ec.category() == lib::asio::error::get_ssl_category()) {
+ if (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ) {
+ return make_error_code(transport::error::tls_short_read);
+ }
+ }*/
+ return ec;
+ }
+private:
+ socket_type::handshake_type get_handshake_type() {
+ if (m_is_server) {
+ return lib::asio::ssl::stream_base::server;
+ } else {
+ return lib::asio::ssl::stream_base::client;
+ }
+ }
+
+ io_service_ptr m_io_service;
+ strand_ptr m_strand;
+ context_ptr m_context;
+ socket_ptr m_socket;
+ uri_ptr m_uri;
+ bool m_is_server;
+
+ lib::error_code m_ec;
+
+ connection_hdl m_hdl;
+ socket_init_handler m_socket_init_handler;
+ tls_init_handler m_tls_init_handler;
+};
+
+/// TLS enabled Asio endpoint socket component
+/**
+ * transport::asio::tls_socket::endpoint implements a secure endpoint socket
+ * component that uses Asio's ssl::stream to wrap an ip::tcp::socket.
+ */
+class endpoint {
+public:
+ /// The type of this endpoint socket component
+ typedef endpoint type;
+
+ /// The type of the corresponding connection socket component
+ typedef connection socket_con_type;
+ /// The type of a shared pointer to the corresponding connection socket
+ /// component.
+ typedef socket_con_type::ptr socket_con_ptr;
+
+ explicit endpoint() {}
+
+ /// Checks whether the endpoint creates secure connections
+ /**
+ * @return Whether or not the endpoint creates secure connections
+ */
+ bool is_secure() const {
+ return true;
+ }
+
+ /// Set socket init handler
+ /**
+ * The socket init handler is called after a connection's socket is created
+ * but before it is used. This gives the end application an opportunity to
+ * set asio socket specific parameters.
+ *
+ * @param h The new socket_init_handler
+ */
+ void set_socket_init_handler(socket_init_handler h) {
+ m_socket_init_handler = h;
+ }
+
+ /// Set TLS init handler
+ /**
+ * The tls init handler is called when needed to request a TLS context for
+ * the library to use. A TLS init handler must be set and it must return a
+ * valid TLS context in order for this endpoint to be able to initialize
+ * TLS connections
+ *
+ * @param h The new tls_init_handler
+ */
+ void set_tls_init_handler(tls_init_handler h) {
+ m_tls_init_handler = h;
+ }
+protected:
+ /// Initialize a connection
+ /**
+ * Called by the transport after a new connection is created to initialize
+ * the socket component of the connection.
+ *
+ * @param scon Pointer to the socket component of the connection
+ *
+ * @return Error code (empty on success)
+ */
+ lib::error_code init(socket_con_ptr scon) {
+ scon->set_socket_init_handler(m_socket_init_handler);
+ scon->set_tls_init_handler(m_tls_init_handler);
+ return lib::error_code();
+ }
+
+private:
+ socket_init_handler m_socket_init_handler;
+ tls_init_handler m_tls_init_handler;
+};
+
+} // namespace tls_socket
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP
diff --git a/websocketpp/transport/base/connection.hpp b/websocketpp/transport/base/connection.hpp
new file mode 100644
index 00000000..f76d4091
--- /dev/null
+++ b/websocketpp/transport/base/connection.hpp
@@ -0,0 +1,238 @@
+/*
+ * 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_TRANSPORT_BASE_CON_HPP
+#define WEBSOCKETPP_TRANSPORT_BASE_CON_HPP
+
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/connection_hdl.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+#include <string>
+
+namespace websocketpp {
+/// Transport policies provide network connectivity and timers
+/**
+ * ### Connection Interface
+ *
+ * Transport connection components needs to provide:
+ *
+ * **init**\n
+ * `void init(init_handler handler)`\n
+ * Called once shortly after construction to give the policy the chance to
+ * perform one time initialization. When complete, the policy must call the
+ * supplied `init_handler` to continue setup. The handler takes one argument
+ * with the error code if any. If an error is returned here setup will fail and
+ * the connection will be aborted or terminated.
+ *
+ * WebSocket++ will call init only once. The transport must call `handler`
+ * exactly once.
+ *
+ * **async_read_at_least**\n
+ * `void async_read_at_least(size_t num_bytes, char *buf, size_t len,
+ * read_handler handler)`\n
+ * start an async read for at least num_bytes and at most len
+ * bytes into buf. Call handler when done with number of bytes read.
+ *
+ * WebSocket++ promises to have only one async_read_at_least in flight at a
+ * time. The transport must promise to only call read_handler once per async
+ * read.
+ *
+ * **async_write**\n
+ * `void async_write(const char* buf, size_t len, write_handler handler)`\n
+ * `void async_write(std::vector<buffer> & bufs, write_handler handler)`\n
+ * Start a write of all of the data in buf or bufs. In second case data is
+ * written sequentially and in place without copying anything to a temporary
+ * location.
+ *
+ * Websocket++ promises to have only one async_write in flight at a time.
+ * The transport must promise to only call the write_handler once per async
+ * write
+ *
+ * **set_handle**\n
+ * `void set_handle(connection_hdl hdl)`\n
+ * Called by WebSocket++ to let this policy know the hdl to the connection. It
+ * may be stored for later use or ignored/discarded. This handle should be used
+ * if the policy adds any connection handlers. Connection handlers must be
+ * called with the handle as the first argument so that the handler code knows
+ * which connection generated the callback.
+ *
+ * **set_timer**\n
+ * `timer_ptr set_timer(long duration, timer_handler handler)`\n
+ * WebSocket++ uses the timers provided by the transport policy as the
+ * implementation of timers is often highly coupled with the implementation of
+ * the networking event loops.
+ *
+ * Transport timer support is an optional feature. A transport method may elect
+ * to implement a dummy timer object and have this method return an empty
+ * pointer. If so, all timer related features of WebSocket++ core will be
+ * disabled. This includes many security features designed to prevent denial of
+ * service attacks. Use timer-free transport policies with caution.
+ *
+ * **get_remote_endpoint**\n
+ * `std::string get_remote_endpoint()`\n
+ * retrieve address of remote endpoint
+ *
+ * **is_secure**\n
+ * `void is_secure()`\n
+ * whether or not the connection to the remote endpoint is secure
+ *
+ * **dispatch**\n
+ * `lib::error_code dispatch(dispatch_handler handler)`: invoke handler within
+ * the transport's event system if it uses one. Otherwise, this method should
+ * simply call `handler` immediately.
+ *
+ * **async_shutdown**\n
+ * `void async_shutdown(shutdown_handler handler)`\n
+ * Perform any cleanup necessary (if any). Call `handler` when complete.
+ */
+namespace transport {
+
+/// The type and signature of the callback passed to the init hook
+typedef lib::function<void(lib::error_code const &)> init_handler;
+
+/// The type and signature of the callback passed to the read method
+typedef lib::function<void(lib::error_code const &,size_t)> read_handler;
+
+/// The type and signature of the callback passed to the write method
+typedef lib::function<void(lib::error_code const &)> write_handler;
+
+/// The type and signature of the callback passed to the read method
+typedef lib::function<void(lib::error_code const &)> timer_handler;
+
+/// The type and signature of the callback passed to the shutdown method
+typedef lib::function<void(lib::error_code const &)> shutdown_handler;
+
+/// The type and signature of the callback passed to the interrupt method
+typedef lib::function<void()> interrupt_handler;
+
+/// The type and signature of the callback passed to the dispatch method
+typedef lib::function<void()> dispatch_handler;
+
+/// A simple utility buffer class
+struct buffer {
+ buffer(char const * b, size_t l) : buf(b),len(l) {}
+
+ char const * buf;
+ size_t len;
+};
+
+/// Generic transport related errors
+namespace error {
+enum value {
+ /// Catch-all error for transport policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// underlying transport pass through
+ pass_through,
+
+ /// async_read_at_least call requested more bytes than buffer can store
+ invalid_num_bytes,
+
+ /// async_read called while another async_read was in progress
+ double_read,
+
+ /// Operation aborted
+ operation_aborted,
+
+ /// Operation not supported
+ operation_not_supported,
+
+ /// End of file
+ eof,
+
+ /// TLS short read
+ tls_short_read,
+
+ /// Timer expired
+ timeout,
+
+ /// read or write after shutdown
+ action_after_shutdown,
+
+ /// Other TLS error
+ tls_error
+};
+
+class category : public lib::error_category {
+ public:
+ category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case general:
+ return "Generic transport policy error";
+ case pass_through:
+ return "Underlying Transport Error";
+ case invalid_num_bytes:
+ return "async_read_at_least call requested more bytes than buffer can store";
+ case operation_aborted:
+ return "The operation was aborted";
+ case operation_not_supported:
+ return "The operation is not supported by this transport";
+ case eof:
+ return "End of File";
+ case tls_short_read:
+ return "TLS Short Read";
+ case timeout:
+ return "Timer Expired";
+ case action_after_shutdown:
+ return "A transport action was requested after shutdown";
+ case tls_error:
+ return "Generic TLS related error";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace transport
+} // namespace websocketpp
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::transport::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+#endif // WEBSOCKETPP_TRANSPORT_BASE_CON_HPP
diff --git a/websocketpp/transport/base/endpoint.hpp b/websocketpp/transport/base/endpoint.hpp
new file mode 100644
index 00000000..3b4b0d6d
--- /dev/null
+++ b/websocketpp/transport/base/endpoint.hpp
@@ -0,0 +1,77 @@
+/*
+ * 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_TRANSPORT_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_BASE_HPP
+
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+namespace websocketpp {
+/// Transport policies provide network connectivity and timers
+/**
+ * ### Endpoint Interface
+ *
+ * Transport endpoint components needs to provide:
+ *
+ * **init**\n
+ * `lib::error_code init(transport_con_ptr tcon)`\n
+ * init is called by an endpoint once for each newly created connection.
+ * It's purpose is to give the transport policy the chance to perform any
+ * transport specific initialization that couldn't be done via the default
+ * constructor.
+ *
+ * **is_secure**\n
+ * `bool is_secure() const`\n
+ * Test whether the transport component of this endpoint is capable of secure
+ * connections.
+ *
+ * **async_connect**\n
+ * `void async_connect(transport_con_ptr tcon, uri_ptr location,
+ * connect_handler handler)`\n
+ * Initiate a connection to `location` using the given connection `tcon`. `tcon`
+ * is a pointer to the transport connection component of the connection. When
+ * complete, `handler` should be called with the the connection's
+ * `connection_hdl` and any error that occurred.
+ *
+ * **init_logging**
+ * `void init_logging(alog_type * a, elog_type * e)`\n
+ * Called once after construction to provide pointers to the endpoint's access
+ * and error loggers. These may be stored and used to log messages or ignored.
+ */
+namespace transport {
+
+/// The type and signature of the callback passed to the accept method
+typedef lib::function<void(lib::error_code const &)> accept_handler;
+
+/// The type and signature of the callback passed to the connect method
+typedef lib::function<void(lib::error_code const &)> connect_handler;
+
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_BASE_HPP
diff --git a/websocketpp/transport/debug/base.hpp b/websocketpp/transport/debug/base.hpp
new file mode 100644
index 00000000..2e477b50
--- /dev/null
+++ b/websocketpp/transport/debug/base.hpp
@@ -0,0 +1,104 @@
+/*
+ * 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_TRANSPORT_DEBUG_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_DEBUG_BASE_HPP
+
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/common/cpp11.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+/// Debug transport policy that is used for various mocking and stubbing duties
+/// in unit tests.
+namespace debug {
+
+/// debug transport errors
+namespace error {
+enum value {
+ /// Catch-all error for transport policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// not implemented
+ not_implemented,
+
+ invalid_num_bytes,
+
+ double_read
+};
+
+/// debug transport error category
+class category : public lib::error_category {
+ public:
+ category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport.debug";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case general:
+ return "Generic stub transport policy error";
+ case not_implemented:
+ return "feature not implemented";
+ case invalid_num_bytes:
+ return "Invalid number of bytes";
+ case double_read:
+ return "Read while another read was outstanding";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+/// Get a reference to a static copy of the debug transport error category
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+/// Get an error code with the given value and the debug transport category
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace debug
+} // namespace transport
+} // namespace websocketpp
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::transport::debug::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+#endif // WEBSOCKETPP_TRANSPORT_DEBUG_BASE_HPP
diff --git a/websocketpp/transport/debug/connection.hpp b/websocketpp/transport/debug/connection.hpp
new file mode 100644
index 00000000..36b282a2
--- /dev/null
+++ b/websocketpp/transport/debug/connection.hpp
@@ -0,0 +1,412 @@
+/*
+ * 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_TRANSPORT_DEBUG_CON_HPP
+#define WEBSOCKETPP_TRANSPORT_DEBUG_CON_HPP
+
+#include <websocketpp/transport/debug/base.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+
+#include <websocketpp/uri.hpp>
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/connection_hdl.hpp>
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/common/platforms.hpp>
+
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace transport {
+namespace debug {
+
+/// Empty timer class to stub out for timer functionality that stub
+/// transport doesn't support
+struct timer {
+ void cancel() {}
+};
+
+template <typename config>
+class connection : public lib::enable_shared_from_this< connection<config> > {
+public:
+ /// Type of this connection transport component
+ typedef connection<config> type;
+ /// Type of a shared pointer to this connection transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// transport concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this transport's access logging policy
+ typedef typename config::alog_type alog_type;
+ /// Type of this transport's error logging policy
+ typedef typename config::elog_type elog_type;
+
+ // Concurrency policy types
+ typedef typename concurrency_type::scoped_lock_type scoped_lock_type;
+ typedef typename concurrency_type::mutex_type mutex_type;
+
+ typedef lib::shared_ptr<timer> timer_ptr;
+
+ explicit connection(bool is_server, alog_type & alog, elog_type & elog)
+ : m_reading(false), m_is_server(is_server), m_alog(alog), m_elog(elog)
+ {
+ m_alog.write(log::alevel::devel,"debug con transport constructor");
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return type::shared_from_this();
+ }
+
+ /// Set whether or not this connection is secure
+ /**
+ * Todo: docs
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not this connection is secure.
+ */
+ void set_secure(bool) {}
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * TODO: docs
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return false;
+ }
+
+ /// Set uri hook
+ /**
+ * Called by the endpoint as a connection is being established to provide
+ * the uri being connected to to the transport layer.
+ *
+ * Implementation is optional and can be ignored if the transport has no
+ * need for this information.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr) {}
+
+ /// Set human readable remote endpoint address
+ /**
+ * Sets the remote endpoint address returned by `get_remote_endpoint`. This
+ * value should be a human readable string that describes the remote
+ * endpoint. Typically an IP address or hostname, perhaps with a port. But
+ * may be something else depending on the nature of the underlying
+ * transport.
+ *
+ * If none is set a default is returned.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value The remote endpoint address to set.
+ */
+ void set_remote_endpoint(std::string) {}
+
+ /// Get human readable remote endpoint address
+ /**
+ * TODO: docs
+ *
+ * This value is used in access and error logs and is available to the end
+ * application for including in user facing interfaces and messages.
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint() const {
+ return "unknown (debug transport)";
+ }
+
+ /// Get the connection handle
+ /**
+ * @return The handle for this connection.
+ */
+ connection_hdl get_handle() const {
+ return connection_hdl();
+ }
+
+ /// Call back a function after a period of time.
+ /**
+ * Timers are not implemented in this transport. The timer pointer will
+ * always be empty. The handler will never be called.
+ *
+ * @param duration Length of time to wait in milliseconds
+ * @param callback The function to call back when the timer has expired
+ * @return A handle that can be used to cancel the timer if it is no longer
+ * needed.
+ */
+ timer_ptr set_timer(long, timer_handler handler) {
+ m_alog.write(log::alevel::devel,"debug connection set timer");
+ m_timer_handler = handler;
+ return timer_ptr();
+ }
+
+ /// Manual input supply (read all)
+ /**
+ * Similar to read_some, but continues to read until all bytes in the
+ * supplied buffer have been read or the connection runs out of read
+ * requests.
+ *
+ * This method still may not read all of the bytes in the input buffer. if
+ * it doesn't it indicates that the connection was most likely closed or
+ * is in an error state where it is no longer accepting new input.
+ *
+ * @since 0.3.0
+ *
+ * @param buf Char buffer to read into the websocket
+ * @param len Length of buf
+ * @return The number of characters from buf actually read.
+ */
+ size_t read_all(char const * buf, size_t len) {
+ size_t total_read = 0;
+ size_t temp_read = 0;
+
+ do {
+ temp_read = this->read_some_impl(buf+total_read,len-total_read);
+ total_read += temp_read;
+ } while (temp_read != 0 && total_read < len);
+
+ return total_read;
+ }
+
+ // debug stuff to invoke the async handlers
+ void expire_timer(lib::error_code const & ec) {
+ m_timer_handler(ec);
+ }
+
+ void fullfil_write() {
+ m_write_handler(lib::error_code());
+ }
+protected:
+ /// Initialize the connection transport
+ /**
+ * Initialize the connection's transport component.
+ *
+ * @param handler The `init_handler` to call when initialization is done
+ */
+ void init(init_handler handler) {
+ m_alog.write(log::alevel::devel,"debug connection init");
+ handler(lib::error_code());
+ }
+
+ /// Initiate an async_read for at least num_bytes bytes into buf
+ /**
+ * Initiates an async_read request for at least num_bytes bytes. The input
+ * will be read into buf. A maximum of len bytes will be input. When the
+ * operation is complete, handler will be called with the status and number
+ * of bytes read.
+ *
+ * This method may or may not call handler from within the initial call. The
+ * application should be prepared to accept either.
+ *
+ * The application should never call this method a second time before it has
+ * been called back for the first read. If this is done, the second read
+ * will be called back immediately with a double_read error.
+ *
+ * If num_bytes or len are zero handler will be called back immediately
+ * indicating success.
+ *
+ * @param num_bytes Don't call handler until at least this many bytes have
+ * been read.
+ * @param buf The buffer to read bytes into
+ * @param len The size of buf. At maximum, this many bytes will be read.
+ * @param handler The callback to invoke when the operation is complete or
+ * ends in an error
+ */
+ void async_read_at_least(size_t num_bytes, char * buf, size_t len,
+ read_handler handler)
+ {
+ std::stringstream s;
+ s << "debug_con async_read_at_least: " << num_bytes;
+ m_alog.write(log::alevel::devel,s.str());
+
+ if (num_bytes > len) {
+ handler(make_error_code(error::invalid_num_bytes),size_t(0));
+ return;
+ }
+
+ if (m_reading == true) {
+ handler(make_error_code(error::double_read),size_t(0));
+ return;
+ }
+
+ if (num_bytes == 0 || len == 0) {
+ handler(lib::error_code(),size_t(0));
+ return;
+ }
+
+ m_buf = buf;
+ m_len = len;
+ m_bytes_needed = num_bytes;
+ m_read_handler = handler;
+ m_cursor = 0;
+ m_reading = true;
+ }
+
+ /// Asyncronous Transport Write
+ /**
+ * Write len bytes in buf to the output stream. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success.
+ *
+ * @param buf buffer to read bytes from
+ * @param len number of bytes to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(char const *, size_t, write_handler handler) {
+ m_alog.write(log::alevel::devel,"debug_con async_write");
+ m_write_handler = handler;
+ }
+
+ /// Asyncronous Transport Write (scatter-gather)
+ /**
+ * Write a sequence of buffers to the output stream. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success.
+ *
+ * @param bufs vector of buffers to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(std::vector<buffer> const &, write_handler handler) {
+ m_alog.write(log::alevel::devel,"debug_con async_write buffer list");
+ m_write_handler = handler;
+ }
+
+ /// Set Connection Handle
+ /**
+ * @param hdl The new handle
+ */
+ void set_handle(connection_hdl) {}
+
+ /// Call given handler back within the transport's event system (if present)
+ /**
+ * Invoke a callback within the transport's event system if it has one. If
+ * it doesn't, the handler will be invoked immediately before this function
+ * returns.
+ *
+ * @param handler The callback to invoke
+ *
+ * @return Whether or not the transport was able to register the handler for
+ * callback.
+ */
+ lib::error_code dispatch(dispatch_handler handler) {
+ handler();
+ return lib::error_code();
+ }
+
+ /// Perform cleanup on socket shutdown_handler
+ /**
+ * @param h The `shutdown_handler` to call back when complete
+ */
+ void async_shutdown(shutdown_handler handler) {
+ handler(lib::error_code());
+ }
+
+ size_t read_some_impl(char const * buf, size_t len) {
+ m_alog.write(log::alevel::devel,"debug_con read_some");
+
+ if (!m_reading) {
+ m_elog.write(log::elevel::devel,"write while not reading");
+ return 0;
+ }
+
+ size_t bytes_to_copy = (std::min)(len,m_len-m_cursor);
+
+ std::copy(buf,buf+bytes_to_copy,m_buf+m_cursor);
+
+ m_cursor += bytes_to_copy;
+
+ if (m_cursor >= m_bytes_needed) {
+ complete_read(lib::error_code());
+ }
+
+ return bytes_to_copy;
+ }
+
+ /// Signal that a requested read is complete
+ /**
+ * Sets the reading flag to false and returns the handler that should be
+ * called back with the result of the read. The cursor position that is sent
+ * is whatever the value of m_cursor is.
+ *
+ * It MUST NOT be called when m_reading is false.
+ * it MUST be called while holding the read lock
+ *
+ * It is important to use this method rather than directly setting/calling
+ * m_read_handler back because this function makes sure to delete the
+ * locally stored handler which contains shared pointers that will otherwise
+ * cause circular reference based memory leaks.
+ *
+ * @param ec The error code to forward to the read handler
+ */
+ void complete_read(lib::error_code const & ec) {
+ m_reading = false;
+
+ read_handler handler = m_read_handler;
+ m_read_handler = read_handler();
+
+ handler(ec,m_cursor);
+ }
+private:
+ timer_handler m_timer_handler;
+
+ // Read space (Protected by m_read_mutex)
+ char * m_buf;
+ size_t m_len;
+ size_t m_bytes_needed;
+ read_handler m_read_handler;
+ size_t m_cursor;
+
+ // transport resources
+ connection_hdl m_connection_hdl;
+ write_handler m_write_handler;
+ shutdown_handler m_shutdown_handler;
+
+ bool m_reading;
+ bool const m_is_server;
+ bool m_is_secure;
+ alog_type & m_alog;
+ elog_type & m_elog;
+ std::string m_remote_endpoint;
+};
+
+
+} // namespace debug
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_DEBUG_CON_HPP
diff --git a/websocketpp/transport/debug/endpoint.hpp b/websocketpp/transport/debug/endpoint.hpp
new file mode 100644
index 00000000..1cca70c5
--- /dev/null
+++ b/websocketpp/transport/debug/endpoint.hpp
@@ -0,0 +1,140 @@
+/*
+ * 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_TRANSPORT_DEBUG_HPP
+#define WEBSOCKETPP_TRANSPORT_DEBUG_HPP
+
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/transport/base/endpoint.hpp>
+#include <websocketpp/transport/debug/connection.hpp>
+
+namespace websocketpp {
+namespace transport {
+namespace debug {
+
+template <typename config>
+class endpoint {
+public:
+ /// Type of this endpoint transport component
+ typedef endpoint type;
+ /// Type of a pointer to this endpoint transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of this endpoint's concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this endpoint's error logging policy
+ typedef typename config::elog_type elog_type;
+ /// Type of this endpoint's access logging policy
+ typedef typename config::alog_type alog_type;
+
+ /// Type of this endpoint transport component's associated connection
+ /// transport component.
+ typedef debug::connection<config> transport_con_type;
+ /// Type of a shared pointer to this endpoint transport component's
+ /// associated connection transport component
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ // generate and manage our own io_service
+ explicit endpoint()
+ {
+ //std::cout << "transport::iostream::endpoint constructor" << std::endl;
+ }
+
+ /// Set whether or not endpoint can create secure connections
+ /**
+ * TODO: docs
+ *
+ * Setting this value only indicates whether or not the endpoint is capable
+ * of producing and managing secure connections. Connections produced by
+ * this endpoint must also be individually flagged as secure if they are.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not the endpoint can create secure connections.
+ */
+ void set_secure(bool) {}
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * TODO: docs
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return false;
+ }
+protected:
+ /// Initialize logging
+ /**
+ * The loggers are located in the main endpoint class. As such, the
+ * transport doesn't have direct access to them. This method is called
+ * by the endpoint constructor to allow shared logging from the transport
+ * component. These are raw pointers to member variables of the endpoint.
+ * In particular, they cannot be used in the transport constructor as they
+ * haven't been constructed yet, and cannot be used in the transport
+ * destructor as they will have been destroyed by then.
+ *
+ * @param a A pointer to the access logger to use.
+ * @param e A pointer to the error logger to use.
+ */
+ void init_logging(alog_type *, elog_type *) {}
+
+ /// Initiate a new connection
+ /**
+ * @param tcon A pointer to the transport connection component of the
+ * connection to connect.
+ * @param u A URI pointer to the URI to connect to.
+ * @param cb The function to call back with the results when complete.
+ */
+ void async_connect(transport_con_ptr, uri_ptr, connect_handler cb) {
+ cb(lib::error_code());
+ }
+
+ /// Initialize a connection
+ /**
+ * Init is called by an endpoint once for each newly created connection.
+ * It's purpose is to give the transport policy the chance to perform any
+ * transport specific initialization that couldn't be done via the default
+ * constructor.
+ *
+ * @param tcon A pointer to the transport portion of the connection.
+ * @return A status code indicating the success or failure of the operation
+ */
+ lib::error_code init(transport_con_ptr) {
+ return lib::error_code();
+ }
+private:
+
+};
+
+} // namespace debug
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_DEBUG_HPP
diff --git a/websocketpp/transport/iostream/base.hpp b/websocketpp/transport/iostream/base.hpp
new file mode 100644
index 00000000..f8783987
--- /dev/null
+++ b/websocketpp/transport/iostream/base.hpp
@@ -0,0 +1,133 @@
+/*
+ * 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_TRANSPORT_IOSTREAM_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_IOSTREAM_BASE_HPP
+
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/connection_hdl.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace transport {
+/// Transport policy that uses STL iostream for I/O and does not support timers
+namespace iostream {
+
+/// The type and signature of the callback used by iostream transport to write
+typedef lib::function<lib::error_code(connection_hdl, char const *, size_t)>
+ write_handler;
+
+/// The type and signature of the callback used by iostream transport to perform
+/// vectored writes.
+/**
+ * If a vectored write handler is not set the standard write handler will be
+ * called multiple times.
+ */
+typedef lib::function<lib::error_code(connection_hdl, std::vector<transport::buffer> const
+ & bufs)> vector_write_handler;
+
+/// The type and signature of the callback used by iostream transport to signal
+/// a transport shutdown.
+typedef lib::function<lib::error_code(connection_hdl)> shutdown_handler;
+
+/// iostream transport errors
+namespace error {
+enum value {
+ /// Catch-all error for transport policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// async_read_at_least call requested more bytes than buffer can store
+ invalid_num_bytes,
+
+ /// async_read called while another async_read was in progress
+ double_read,
+
+ /// An operation that requires an output stream was attempted before
+ /// setting one.
+ output_stream_required,
+
+ /// stream error
+ bad_stream
+};
+
+/// iostream transport error category
+class category : public lib::error_category {
+ public:
+ category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport.iostream";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case general:
+ return "Generic iostream transport policy error";
+ case invalid_num_bytes:
+ return "async_read_at_least call requested more bytes than buffer can store";
+ case double_read:
+ return "Async read already in progress";
+ case output_stream_required:
+ return "An output stream to be set before async_write can be used";
+ case bad_stream:
+ return "A stream operation returned ios::bad";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+/// Get a reference to a static copy of the iostream transport error category
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+/// Get an error code with the given value and the iostream transport category
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace iostream
+} // namespace transport
+} // namespace websocketpp
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::transport::iostream::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_BASE_HPP
diff --git a/websocketpp/transport/iostream/connection.hpp b/websocketpp/transport/iostream/connection.hpp
new file mode 100644
index 00000000..81c4f411
--- /dev/null
+++ b/websocketpp/transport/iostream/connection.hpp
@@ -0,0 +1,714 @@
+/*
+ * 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_TRANSPORT_IOSTREAM_CON_HPP
+#define WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
+
+#include <websocketpp/transport/iostream/base.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+
+#include <websocketpp/uri.hpp>
+
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/connection_hdl.hpp>
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/common/platforms.hpp>
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace transport {
+namespace iostream {
+
+/// Empty timer class to stub out for timer functionality that iostream
+/// transport doesn't support
+struct timer {
+ void cancel() {}
+};
+
+template <typename config>
+class connection : public lib::enable_shared_from_this< connection<config> > {
+public:
+ /// Type of this connection transport component
+ typedef connection<config> type;
+ /// Type of a shared pointer to this connection transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// transport concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this transport's access logging policy
+ typedef typename config::alog_type alog_type;
+ /// Type of this transport's error logging policy
+ typedef typename config::elog_type elog_type;
+
+ // Concurrency policy types
+ typedef typename concurrency_type::scoped_lock_type scoped_lock_type;
+ typedef typename concurrency_type::mutex_type mutex_type;
+
+ typedef lib::shared_ptr<timer> timer_ptr;
+
+ explicit connection(bool is_server, alog_type & alog, elog_type & elog)
+ : m_output_stream(NULL)
+ , m_reading(false)
+ , m_is_server(is_server)
+ , m_is_secure(false)
+ , m_alog(alog)
+ , m_elog(elog)
+ , m_remote_endpoint("iostream transport")
+ {
+ m_alog.write(log::alevel::devel,"iostream con transport constructor");
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return type::shared_from_this();
+ }
+
+ /// Register a std::ostream with the transport for writing output
+ /**
+ * Register a std::ostream with the transport. All future writes will be
+ * done to this output stream.
+ *
+ * @param o A pointer to the ostream to use for output.
+ */
+ void register_ostream(std::ostream * o) {
+ // TODO: lock transport state?
+ scoped_lock_type lock(m_read_mutex);
+ m_output_stream = o;
+ }
+
+ /// Set uri hook
+ /**
+ * Called by the endpoint as a connection is being established to provide
+ * the uri being connected to to the transport layer.
+ *
+ * This transport policy doesn't use the uri so it is ignored.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr) {}
+
+ /// Overloaded stream input operator
+ /**
+ * Attempts to read input from the given stream into the transport. Bytes
+ * will be extracted from the input stream to fulfill any pending reads.
+ * Input in this manner will only read until the current read buffer has
+ * been filled. Then it will signal the library to process the input. If the
+ * library's input handler adds a new async_read, additional bytes will be
+ * read, otherwise the input operation will end.
+ *
+ * When this function returns one of the following conditions is true:
+ * - There is no outstanding read operation
+ * - There are no more bytes available in the input stream
+ *
+ * You can use tellg() on the input stream to determine if all of the input
+ * bytes were read or not.
+ *
+ * If there is no pending read operation when the input method is called, it
+ * will return immediately and tellg() will not have changed.
+ */
+ friend std::istream & operator>> (std::istream & in, type & t) {
+ // this serializes calls to external read.
+ scoped_lock_type lock(t.m_read_mutex);
+
+ t.read(in);
+
+ return in;
+ }
+
+ /// Manual input supply (read some)
+ /**
+ * Copies bytes from buf into WebSocket++'s input buffers. Bytes will be
+ * copied from the supplied buffer to fulfill any pending library reads. It
+ * will return the number of bytes successfully processed. If there are no
+ * pending reads read_some will return immediately. Not all of the bytes may
+ * be able to be read in one call.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param buf Char buffer to read into the websocket
+ * @param len Length of buf
+ * @return The number of characters from buf actually read.
+ */
+ size_t read_some(char const * buf, size_t len) {
+ // this serializes calls to external read.
+ scoped_lock_type lock(m_read_mutex);
+
+ return this->read_some_impl(buf,len);
+ }
+
+ /// Manual input supply (read all)
+ /**
+ * Similar to read_some, but continues to read until all bytes in the
+ * supplied buffer have been read or the connection runs out of read
+ * requests.
+ *
+ * This method still may not read all of the bytes in the input buffer. if
+ * it doesn't it indicates that the connection was most likely closed or
+ * is in an error state where it is no longer accepting new input.
+ *
+ * @since 0.3.0
+ *
+ * @param buf Char buffer to read into the websocket
+ * @param len Length of buf
+ * @return The number of characters from buf actually read.
+ */
+ size_t read_all(char const * buf, size_t len) {
+ // this serializes calls to external read.
+ scoped_lock_type lock(m_read_mutex);
+
+ size_t total_read = 0;
+ size_t temp_read = 0;
+
+ do {
+ temp_read = this->read_some_impl(buf+total_read,len-total_read);
+ total_read += temp_read;
+ } while (temp_read != 0 && total_read < len);
+
+ return total_read;
+ }
+
+ /// Manual input supply (DEPRECATED)
+ /**
+ * @deprecated DEPRECATED in favor of read_some()
+ * @see read_some()
+ */
+ size_t readsome(char const * buf, size_t len) {
+ return this->read_some(buf,len);
+ }
+
+ /// Signal EOF
+ /**
+ * Signals to the transport that data stream being read has reached EOF and
+ * that no more bytes may be read or written to/from the transport.
+ *
+ * @since 0.3.0-alpha4
+ */
+ void eof() {
+ // this serializes calls to external read.
+ scoped_lock_type lock(m_read_mutex);
+
+ if (m_reading) {
+ complete_read(make_error_code(transport::error::eof));
+ }
+ }
+
+ /// Signal transport error
+ /**
+ * Signals to the transport that a fatal data stream error has occurred and
+ * that no more bytes may be read or written to/from the transport.
+ *
+ * @since 0.3.0-alpha4
+ */
+ void fatal_error() {
+ // this serializes calls to external read.
+ scoped_lock_type lock(m_read_mutex);
+
+ if (m_reading) {
+ complete_read(make_error_code(transport::error::pass_through));
+ }
+ }
+
+ /// Set whether or not this connection is secure
+ /**
+ * The iostream transport does not provide any security features. As such
+ * it defaults to returning false when `is_secure` is called. However, the
+ * iostream transport may be used to wrap an external socket API that may
+ * provide secure transport. This method allows that external API to flag
+ * whether or not this connection is secure so that users of the WebSocket++
+ * API will get more accurate information.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not this connection is secure.
+ */
+ void set_secure(bool value) {
+ m_is_secure = value;
+ }
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * iostream transport will return false always because it has no information
+ * about the ultimate remote endpoint. This may or may not be accurate
+ * depending on the real source of bytes being input. The `set_secure`
+ * method may be used to flag connections that are secured by an external
+ * API
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return m_is_secure;
+ }
+
+ /// Set human readable remote endpoint address
+ /**
+ * Sets the remote endpoint address returned by `get_remote_endpoint`. This
+ * value should be a human readable string that describes the remote
+ * endpoint. Typically an IP address or hostname, perhaps with a port. But
+ * may be something else depending on the nature of the underlying
+ * transport.
+ *
+ * If none is set the default is "iostream transport".
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value The remote endpoint address to set.
+ */
+ void set_remote_endpoint(std::string value) {
+ m_remote_endpoint = value;
+ }
+
+ /// Get human readable remote endpoint address
+ /**
+ * The iostream transport has no information about the ultimate remote
+ * endpoint. It will return the string "iostream transport". The
+ * `set_remote_endpoint` method may be used by external network code to set
+ * a more accurate value.
+ *
+ * This value is used in access and error logs and is available to the end
+ * application for including in user facing interfaces and messages.
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint() const {
+ return m_remote_endpoint;
+ }
+
+ /// Get the connection handle
+ /**
+ * @return The handle for this connection.
+ */
+ connection_hdl get_handle() const {
+ return m_connection_hdl;
+ }
+
+ /// Call back a function after a period of time.
+ /**
+ * Timers are not implemented in this transport. The timer pointer will
+ * always be empty. The handler will never be called.
+ *
+ * @param duration Length of time to wait in milliseconds
+ * @param callback The function to call back when the timer has expired
+ * @return A handle that can be used to cancel the timer if it is no longer
+ * needed.
+ */
+ timer_ptr set_timer(long, timer_handler) {
+ return timer_ptr();
+ }
+
+ /// Sets the write handler
+ /**
+ * The write handler is called when the iostream transport receives data
+ * that needs to be written to the appropriate output location. This handler
+ * can be used in place of registering an ostream for output.
+ *
+ * The signature of the handler is
+ * `lib::error_code (connection_hdl, char const *, size_t)` The
+ * code returned will be reported and logged by the core library.
+ *
+ * See also, set_vector_write_handler, for an optional write handler that
+ * allows more efficient handling of multiple writes at once.
+ *
+ * @see set_vector_write_handler
+ *
+ * @since 0.5.0
+ *
+ * @param h The handler to call when data is to be written.
+ */
+ void set_write_handler(write_handler h) {
+ m_write_handler = h;
+ }
+
+ /// Sets the vectored write handler
+ /**
+ * The vectored write handler is called when the iostream transport receives
+ * multiple chunks of data that need to be written to the appropriate output
+ * location. This handler can be used in conjunction with the write_handler
+ * in place of registering an ostream for output.
+ *
+ * The sequence of buffers represents bytes that should be written
+ * consecutively and it is suggested to group the buffers into as few next
+ * layer packets as possible. Vector write is used to allow implementations
+ * that support it to coalesce writes into a single TCP packet or TLS
+ * segment for improved efficiency.
+ *
+ * This is an optional handler. If it is not defined then multiple calls
+ * will be made to the standard write handler.
+ *
+ * The signature of the handler is
+ * `lib::error_code (connection_hdl, std::vector<websocketpp::transport::buffer>
+ * const & bufs)`. The code returned will be reported and logged by the core
+ * library. The `websocketpp::transport::buffer` type is a struct with two
+ * data members. buf (char const *) and len (size_t).
+ *
+ * @since 0.6.0
+ *
+ * @param h The handler to call when vectored data is to be written.
+ */
+ void set_vector_write_handler(vector_write_handler h) {
+ m_vector_write_handler = h;
+ }
+
+ /// Sets the shutdown handler
+ /**
+ * The shutdown handler is called when the iostream transport receives a
+ * notification from the core library that it is finished with all read and
+ * write operations and that the underlying transport can be cleaned up.
+ *
+ * If you are using iostream transport with another socket library, this is
+ * a good time to close/shutdown the socket for this connection.
+ *
+ * The signature of the handler is `lib::error_code (connection_hdl)`. The
+ * code returned will be reported and logged by the core library.
+ *
+ * @since 0.5.0
+ *
+ * @param h The handler to call on connection shutdown.
+ */
+ void set_shutdown_handler(shutdown_handler h) {
+ m_shutdown_handler = h;
+ }
+protected:
+ /// Initialize the connection transport
+ /**
+ * Initialize the connection's transport component.
+ *
+ * @param handler The `init_handler` to call when initialization is done
+ */
+ void init(init_handler handler) {
+ m_alog.write(log::alevel::devel,"iostream connection init");
+ handler(lib::error_code());
+ }
+
+ /// Initiate an async_read for at least num_bytes bytes into buf
+ /**
+ * Initiates an async_read request for at least num_bytes bytes. The input
+ * will be read into buf. A maximum of len bytes will be input. When the
+ * operation is complete, handler will be called with the status and number
+ * of bytes read.
+ *
+ * This method may or may not call handler from within the initial call. The
+ * application should be prepared to accept either.
+ *
+ * The application should never call this method a second time before it has
+ * been called back for the first read. If this is done, the second read
+ * will be called back immediately with a double_read error.
+ *
+ * If num_bytes or len are zero handler will be called back immediately
+ * indicating success.
+ *
+ * @param num_bytes Don't call handler until at least this many bytes have
+ * been read.
+ * @param buf The buffer to read bytes into
+ * @param len The size of buf. At maximum, this many bytes will be read.
+ * @param handler The callback to invoke when the operation is complete or
+ * ends in an error
+ */
+ void async_read_at_least(size_t num_bytes, char *buf, size_t len,
+ read_handler handler)
+ {
+ std::stringstream s;
+ s << "iostream_con async_read_at_least: " << num_bytes;
+ m_alog.write(log::alevel::devel,s.str());
+
+ if (num_bytes > len) {
+ handler(make_error_code(error::invalid_num_bytes),size_t(0));
+ return;
+ }
+
+ if (m_reading == true) {
+ handler(make_error_code(error::double_read),size_t(0));
+ return;
+ }
+
+ if (num_bytes == 0 || len == 0) {
+ handler(lib::error_code(),size_t(0));
+ return;
+ }
+
+ m_buf = buf;
+ m_len = len;
+ m_bytes_needed = num_bytes;
+ m_read_handler = handler;
+ m_cursor = 0;
+ m_reading = true;
+ }
+
+ /// Asyncronous Transport Write
+ /**
+ * Write len bytes in buf to the output method. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success. Other possible errors (not exhaustive)
+ * output_stream_required: No output stream was registered to write to
+ * bad_stream: a ostream pass through error
+ *
+ * This method will attempt to write to the registered ostream first. If an
+ * ostream is not registered it will use the write handler. If neither are
+ * registered then an error is passed up to the connection.
+ *
+ * @param buf buffer to read bytes from
+ * @param len number of bytes to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(char const * buf, size_t len, transport::write_handler
+ handler)
+ {
+ m_alog.write(log::alevel::devel,"iostream_con async_write");
+ // TODO: lock transport state?
+
+ lib::error_code ec;
+
+ if (m_output_stream) {
+ m_output_stream->write(buf,len);
+
+ if (m_output_stream->bad()) {
+ ec = make_error_code(error::bad_stream);
+ }
+ } else if (m_write_handler) {
+ ec = m_write_handler(m_connection_hdl, buf, len);
+ } else {
+ ec = make_error_code(error::output_stream_required);
+ }
+
+ handler(ec);
+ }
+
+ /// Asyncronous Transport Write (scatter-gather)
+ /**
+ * Write a sequence of buffers to the output method. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success. Other possible errors (not exhaustive)
+ * output_stream_required: No output stream was registered to write to
+ * bad_stream: a ostream pass through error
+ *
+ * This method will attempt to write to the registered ostream first. If an
+ * ostream is not registered it will use the write handler. If neither are
+ * registered then an error is passed up to the connection.
+ *
+ * @param bufs vector of buffers to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(std::vector<buffer> const & bufs, transport::write_handler
+ handler)
+ {
+ m_alog.write(log::alevel::devel,"iostream_con async_write buffer list");
+ // TODO: lock transport state?
+
+ lib::error_code ec;
+
+ if (m_output_stream) {
+ std::vector<buffer>::const_iterator it;
+ for (it = bufs.begin(); it != bufs.end(); it++) {
+ m_output_stream->write((*it).buf,(*it).len);
+
+ if (m_output_stream->bad()) {
+ ec = make_error_code(error::bad_stream);
+ break;
+ }
+ }
+ } else if (m_vector_write_handler) {
+ ec = m_vector_write_handler(m_connection_hdl, bufs);
+ } else if (m_write_handler) {
+ std::vector<buffer>::const_iterator it;
+ for (it = bufs.begin(); it != bufs.end(); it++) {
+ ec = m_write_handler(m_connection_hdl, (*it).buf, (*it).len);
+ if (ec) {break;}
+ }
+
+ } else {
+ ec = make_error_code(error::output_stream_required);
+ }
+
+ handler(ec);
+ }
+
+ /// Set Connection Handle
+ /**
+ * @param hdl The new handle
+ */
+ void set_handle(connection_hdl hdl) {
+ m_connection_hdl = hdl;
+ }
+
+ /// Call given handler back within the transport's event system (if present)
+ /**
+ * Invoke a callback within the transport's event system if it has one. If
+ * it doesn't, the handler will be invoked immediately before this function
+ * returns.
+ *
+ * @param handler The callback to invoke
+ *
+ * @return Whether or not the transport was able to register the handler for
+ * callback.
+ */
+ lib::error_code dispatch(dispatch_handler handler) {
+ handler();
+ return lib::error_code();
+ }
+
+ /// Perform cleanup on socket shutdown_handler
+ /**
+ * If a shutdown handler is set, call it and pass through its return error
+ * code. Otherwise assume there is nothing to do and pass through a success
+ * code.
+ *
+ * @param handler The `shutdown_handler` to call back when complete
+ */
+ void async_shutdown(transport::shutdown_handler handler) {
+ lib::error_code ec;
+
+ if (m_shutdown_handler) {
+ ec = m_shutdown_handler(m_connection_hdl);
+ }
+
+ handler(ec);
+ }
+private:
+ void read(std::istream &in) {
+ m_alog.write(log::alevel::devel,"iostream_con read");
+
+ while (in.good()) {
+ if (!m_reading) {
+ m_elog.write(log::elevel::devel,"write while not reading");
+ break;
+ }
+
+ in.read(m_buf+m_cursor,static_cast<std::streamsize>(m_len-m_cursor));
+
+ if (in.gcount() == 0) {
+ m_elog.write(log::elevel::devel,"read zero bytes");
+ break;
+ }
+
+ m_cursor += static_cast<size_t>(in.gcount());
+
+ // TODO: error handling
+ if (in.bad()) {
+ m_reading = false;
+ complete_read(make_error_code(error::bad_stream));
+ }
+
+ if (m_cursor >= m_bytes_needed) {
+ m_reading = false;
+ complete_read(lib::error_code());
+ }
+ }
+ }
+
+ size_t read_some_impl(char const * buf, size_t len) {
+ m_alog.write(log::alevel::devel,"iostream_con read_some");
+
+ if (!m_reading) {
+ m_elog.write(log::elevel::devel,"write while not reading");
+ return 0;
+ }
+
+ size_t bytes_to_copy = (std::min)(len,m_len-m_cursor);
+
+ std::copy(buf,buf+bytes_to_copy,m_buf+m_cursor);
+
+ m_cursor += bytes_to_copy;
+
+ if (m_cursor >= m_bytes_needed) {
+ complete_read(lib::error_code());
+ }
+
+ return bytes_to_copy;
+ }
+
+ /// Signal that a requested read is complete
+ /**
+ * Sets the reading flag to false and returns the handler that should be
+ * called back with the result of the read. The cursor position that is sent
+ * is whatever the value of m_cursor is.
+ *
+ * It MUST NOT be called when m_reading is false.
+ * it MUST be called while holding the read lock
+ *
+ * It is important to use this method rather than directly setting/calling
+ * m_read_handler back because this function makes sure to delete the
+ * locally stored handler which contains shared pointers that will otherwise
+ * cause circular reference based memory leaks.
+ *
+ * @param ec The error code to forward to the read handler
+ */
+ void complete_read(lib::error_code const & ec) {
+ m_reading = false;
+
+ read_handler handler = m_read_handler;
+ m_read_handler = read_handler();
+
+ handler(ec,m_cursor);
+ }
+
+ // Read space (Protected by m_read_mutex)
+ char * m_buf;
+ size_t m_len;
+ size_t m_bytes_needed;
+ read_handler m_read_handler;
+ size_t m_cursor;
+
+ // transport resources
+ std::ostream * m_output_stream;
+ connection_hdl m_connection_hdl;
+ write_handler m_write_handler;
+ vector_write_handler m_vector_write_handler;
+ shutdown_handler m_shutdown_handler;
+
+ bool m_reading;
+ bool const m_is_server;
+ bool m_is_secure;
+ alog_type & m_alog;
+ elog_type & m_elog;
+ std::string m_remote_endpoint;
+
+ // This lock ensures that only one thread can edit read data for this
+ // connection. This is a very coarse lock that is basically locked all the
+ // time. The nature of the connection is such that it cannot be
+ // parallelized, the locking is here to prevent intra-connection concurrency
+ // in order to allow inter-connection concurrency.
+ mutex_type m_read_mutex;
+};
+
+
+} // namespace iostream
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
diff --git a/websocketpp/transport/iostream/endpoint.hpp b/websocketpp/transport/iostream/endpoint.hpp
new file mode 100644
index 00000000..14ec6537
--- /dev/null
+++ b/websocketpp/transport/iostream/endpoint.hpp
@@ -0,0 +1,222 @@
+/*
+ * 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_TRANSPORT_IOSTREAM_HPP
+#define WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP
+
+#include <websocketpp/transport/base/endpoint.hpp>
+#include <websocketpp/transport/iostream/connection.hpp>
+
+#include <websocketpp/uri.hpp>
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/memory.hpp>
+
+#include <ostream>
+
+namespace websocketpp {
+namespace transport {
+namespace iostream {
+
+template <typename config>
+class endpoint {
+public:
+ /// Type of this endpoint transport component
+ typedef endpoint type;
+ /// Type of a pointer to this endpoint transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of this endpoint's concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this endpoint's error logging policy
+ typedef typename config::elog_type elog_type;
+ /// Type of this endpoint's access logging policy
+ typedef typename config::alog_type alog_type;
+
+ /// Type of this endpoint transport component's associated connection
+ /// transport component.
+ typedef iostream::connection<config> transport_con_type;
+ /// Type of a shared pointer to this endpoint transport component's
+ /// associated connection transport component
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ // generate and manage our own io_service
+ explicit endpoint() : m_output_stream(NULL), m_is_secure(false)
+ {
+ //std::cout << "transport::iostream::endpoint constructor" << std::endl;
+ }
+
+ /// Register a default output stream
+ /**
+ * The specified output stream will be assigned to future connections as the
+ * default output stream.
+ *
+ * @param o The ostream to use as the default output stream.
+ */
+ void register_ostream(std::ostream * o) {
+ m_alog->write(log::alevel::devel,"register_ostream");
+ m_output_stream = o;
+ }
+
+ /// Set whether or not endpoint can create secure connections
+ /**
+ * The iostream transport does not provide any security features. As such
+ * it defaults to returning false when `is_secure` is called. However, the
+ * iostream transport may be used to wrap an external socket API that may
+ * provide secure transport. This method allows that external API to flag
+ * whether or not it can create secure connections so that users of the
+ * WebSocket++ API will get more accurate information.
+ *
+ * Setting this value only indicates whether or not the endpoint is capable
+ * of producing and managing secure connections. Connections produced by
+ * this endpoint must also be individually flagged as secure if they are.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not the endpoint can create secure connections.
+ */
+ void set_secure(bool value) {
+ m_is_secure = value;
+ }
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * iostream transport will return false by default because it has no
+ * information about the ultimate remote endpoint. This may or may not be
+ * accurate depending on the real source of bytes being input. `set_secure`
+ * may be used by a wrapper API to correct the return value in the case that
+ * secure connections are in fact possible.
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return m_is_secure;
+ }
+
+ /// Sets the write handler
+ /**
+ * The write handler is called when the iostream transport receives data
+ * that needs to be written to the appropriate output location. This handler
+ * can be used in place of registering an ostream for output.
+ *
+ * The signature of the handler is
+ * `lib::error_code (connection_hdl, char const *, size_t)` The
+ * code returned will be reported and logged by the core library.
+ *
+ * @since 0.5.0
+ *
+ * @param h The handler to call on connection shutdown.
+ */
+ void set_write_handler(write_handler h) {
+ m_write_handler = h;
+ }
+
+ /// Sets the shutdown handler
+ /**
+ * The shutdown handler is called when the iostream transport receives a
+ * notification from the core library that it is finished with all read and
+ * write operations and that the underlying transport can be cleaned up.
+ *
+ * If you are using iostream transport with another socket library, this is
+ * a good time to close/shutdown the socket for this connection.
+ *
+ * The signature of the handler is lib::error_code (connection_hdl). The
+ * code returned will be reported and logged by the core library.
+ *
+ * @since 0.5.0
+ *
+ * @param h The handler to call on connection shutdown.
+ */
+ void set_shutdown_handler(shutdown_handler h) {
+ m_shutdown_handler = h;
+ }
+protected:
+ /// Initialize logging
+ /**
+ * The loggers are located in the main endpoint class. As such, the
+ * transport doesn't have direct access to them. This method is called
+ * by the endpoint constructor to allow shared logging from the transport
+ * component. These are raw pointers to member variables of the endpoint.
+ * In particular, they cannot be used in the transport constructor as they
+ * haven't been constructed yet, and cannot be used in the transport
+ * destructor as they will have been destroyed by then.
+ *
+ * @param a A pointer to the access logger to use.
+ * @param e A pointer to the error logger to use.
+ */
+ void init_logging(alog_type * a, elog_type * e) {
+ m_elog = e;
+ m_alog = a;
+ }
+
+ /// Initiate a new connection
+ /**
+ * @param tcon A pointer to the transport connection component of the
+ * connection to connect.
+ * @param u A URI pointer to the URI to connect to.
+ * @param cb The function to call back with the results when complete.
+ */
+ void async_connect(transport_con_ptr, uri_ptr, connect_handler cb) {
+ cb(lib::error_code());
+ }
+
+ /// Initialize a connection
+ /**
+ * Init is called by an endpoint once for each newly created connection.
+ * It's purpose is to give the transport policy the chance to perform any
+ * transport specific initialization that couldn't be done via the default
+ * constructor.
+ *
+ * @param tcon A pointer to the transport portion of the connection.
+ * @return A status code indicating the success or failure of the operation
+ */
+ lib::error_code init(transport_con_ptr tcon) {
+ tcon->register_ostream(m_output_stream);
+ if (m_shutdown_handler) {
+ tcon->set_shutdown_handler(m_shutdown_handler);
+ }
+ if (m_write_handler) {
+ tcon->set_write_handler(m_write_handler);
+ }
+ return lib::error_code();
+ }
+private:
+ std::ostream * m_output_stream;
+ shutdown_handler m_shutdown_handler;
+ write_handler m_write_handler;
+
+ elog_type * m_elog;
+ alog_type * m_alog;
+ bool m_is_secure;
+};
+
+
+} // namespace iostream
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP
diff --git a/websocketpp/transport/stub/base.hpp b/websocketpp/transport/stub/base.hpp
new file mode 100644
index 00000000..754981e2
--- /dev/null
+++ b/websocketpp/transport/stub/base.hpp
@@ -0,0 +1,95 @@
+/*
+ * 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_TRANSPORT_STUB_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_STUB_BASE_HPP
+
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/common/cpp11.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+/// Stub transport policy that has no input or output.
+namespace stub {
+
+/// stub transport errors
+namespace error {
+enum value {
+ /// Catch-all error for transport policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// not implemented
+ not_implemented
+};
+
+/// stub transport error category
+class category : public lib::error_category {
+ public:
+ category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport.stub";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case general:
+ return "Generic stub transport policy error";
+ case not_implemented:
+ return "feature not implemented";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+/// Get a reference to a static copy of the stub transport error category
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+/// Get an error code with the given value and the stub transport category
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace stub
+} // namespace transport
+} // namespace websocketpp
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::transport::stub::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+#endif // WEBSOCKETPP_TRANSPORT_STUB_BASE_HPP
diff --git a/websocketpp/transport/stub/connection.hpp b/websocketpp/transport/stub/connection.hpp
new file mode 100644
index 00000000..59bd4a0a
--- /dev/null
+++ b/websocketpp/transport/stub/connection.hpp
@@ -0,0 +1,286 @@
+/*
+ * 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_TRANSPORT_STUB_CON_HPP
+#define WEBSOCKETPP_TRANSPORT_STUB_CON_HPP
+
+#include <websocketpp/transport/stub/base.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/connection_hdl.hpp>
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/common/platforms.hpp>
+
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace transport {
+namespace stub {
+
+/// Empty timer class to stub out for timer functionality that stub
+/// transport doesn't support
+struct timer {
+ void cancel() {}
+};
+
+template <typename config>
+class connection : public lib::enable_shared_from_this< connection<config> > {
+public:
+ /// Type of this connection transport component
+ typedef connection<config> type;
+ /// Type of a shared pointer to this connection transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// transport concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this transport's access logging policy
+ typedef typename config::alog_type alog_type;
+ /// Type of this transport's error logging policy
+ typedef typename config::elog_type elog_type;
+
+ // Concurrency policy types
+ typedef typename concurrency_type::scoped_lock_type scoped_lock_type;
+ typedef typename concurrency_type::mutex_type mutex_type;
+
+ typedef lib::shared_ptr<timer> timer_ptr;
+
+ explicit connection(bool is_server, alog_type & alog, elog_type & elog)
+ : m_alog(alog), m_elog(elog)
+ {
+ m_alog.write(log::alevel::devel,"stub con transport constructor");
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return type::shared_from_this();
+ }
+
+ /// Set whether or not this connection is secure
+ /**
+ * Todo: docs
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not this connection is secure.
+ */
+ void set_secure(bool value) {}
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * TODO: docs
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return false;
+ }
+
+ /// Set uri hook
+ /**
+ * Called by the endpoint as a connection is being established to provide
+ * the uri being connected to to the transport layer.
+ *
+ * Implementation is optional and can be ignored if the transport has no
+ * need for this information.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr) {}
+
+ /// Set human readable remote endpoint address
+ /**
+ * Sets the remote endpoint address returned by `get_remote_endpoint`. This
+ * value should be a human readable string that describes the remote
+ * endpoint. Typically an IP address or hostname, perhaps with a port. But
+ * may be something else depending on the nature of the underlying
+ * transport.
+ *
+ * If none is set a default is returned.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value The remote endpoint address to set.
+ */
+ void set_remote_endpoint(std::string value) {}
+
+ /// Get human readable remote endpoint address
+ /**
+ * TODO: docs
+ *
+ * This value is used in access and error logs and is available to the end
+ * application for including in user facing interfaces and messages.
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint() const {
+ return "unknown (stub transport)";
+ }
+
+ /// Get the connection handle
+ /**
+ * @return The handle for this connection.
+ */
+ connection_hdl get_handle() const {
+ return connection_hdl();
+ }
+
+ /// Call back a function after a period of time.
+ /**
+ * Timers are not implemented in this transport. The timer pointer will
+ * always be empty. The handler will never be called.
+ *
+ * @param duration Length of time to wait in milliseconds
+ * @param callback The function to call back when the timer has expired
+ * @return A handle that can be used to cancel the timer if it is no longer
+ * needed.
+ */
+ timer_ptr set_timer(long duration, timer_handler handler) {
+ return timer_ptr();
+ }
+protected:
+ /// Initialize the connection transport
+ /**
+ * Initialize the connection's transport component.
+ *
+ * @param handler The `init_handler` to call when initialization is done
+ */
+ void init(init_handler handler) {
+ m_alog.write(log::alevel::devel,"stub connection init");
+ handler(make_error_code(error::not_implemented));
+ }
+
+ /// Initiate an async_read for at least num_bytes bytes into buf
+ /**
+ * Initiates an async_read request for at least num_bytes bytes. The input
+ * will be read into buf. A maximum of len bytes will be input. When the
+ * operation is complete, handler will be called with the status and number
+ * of bytes read.
+ *
+ * This method may or may not call handler from within the initial call. The
+ * application should be prepared to accept either.
+ *
+ * The application should never call this method a second time before it has
+ * been called back for the first read. If this is done, the second read
+ * will be called back immediately with a double_read error.
+ *
+ * If num_bytes or len are zero handler will be called back immediately
+ * indicating success.
+ *
+ * @param num_bytes Don't call handler until at least this many bytes have
+ * been read.
+ * @param buf The buffer to read bytes into
+ * @param len The size of buf. At maximum, this many bytes will be read.
+ * @param handler The callback to invoke when the operation is complete or
+ * ends in an error
+ */
+ void async_read_at_least(size_t num_bytes, char * buf, size_t len,
+ read_handler handler)
+ {
+ m_alog.write(log::alevel::devel, "stub_con async_read_at_least");
+ handler(make_error_code(error::not_implemented), 0);
+ }
+
+ /// Asyncronous Transport Write
+ /**
+ * Write len bytes in buf to the output stream. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success.
+ *
+ * @param buf buffer to read bytes from
+ * @param len number of bytes to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(char const * buf, size_t len, write_handler handler) {
+ m_alog.write(log::alevel::devel,"stub_con async_write");
+ handler(make_error_code(error::not_implemented));
+ }
+
+ /// Asyncronous Transport Write (scatter-gather)
+ /**
+ * Write a sequence of buffers to the output stream. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success.
+ *
+ * @param bufs vector of buffers to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(std::vector<buffer> const & bufs, write_handler handler) {
+ m_alog.write(log::alevel::devel,"stub_con async_write buffer list");
+ handler(make_error_code(error::not_implemented));
+ }
+
+ /// Set Connection Handle
+ /**
+ * @param hdl The new handle
+ */
+ void set_handle(connection_hdl hdl) {}
+
+ /// Call given handler back within the transport's event system (if present)
+ /**
+ * Invoke a callback within the transport's event system if it has one. If
+ * it doesn't, the handler will be invoked immediately before this function
+ * returns.
+ *
+ * @param handler The callback to invoke
+ *
+ * @return Whether or not the transport was able to register the handler for
+ * callback.
+ */
+ lib::error_code dispatch(dispatch_handler handler) {
+ handler();
+ return lib::error_code();
+ }
+
+ /// Perform cleanup on socket shutdown_handler
+ /**
+ * @param h The `shutdown_handler` to call back when complete
+ */
+ void async_shutdown(shutdown_handler handler) {
+ handler(lib::error_code());
+ }
+private:
+ // member variables!
+ alog_type & m_alog;
+ elog_type & m_elog;
+};
+
+
+} // namespace stub
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_STUB_CON_HPP
diff --git a/websocketpp/transport/stub/endpoint.hpp b/websocketpp/transport/stub/endpoint.hpp
new file mode 100644
index 00000000..3bbb78f3
--- /dev/null
+++ b/websocketpp/transport/stub/endpoint.hpp
@@ -0,0 +1,140 @@
+/*
+ * 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_TRANSPORT_STUB_HPP
+#define WEBSOCKETPP_TRANSPORT_STUB_HPP
+
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/transport/base/endpoint.hpp>
+#include <websocketpp/transport/stub/connection.hpp>
+
+namespace websocketpp {
+namespace transport {
+namespace stub {
+
+template <typename config>
+class endpoint {
+public:
+ /// Type of this endpoint transport component
+ typedef endpoint type;
+ /// Type of a pointer to this endpoint transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of this endpoint's concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this endpoint's error logging policy
+ typedef typename config::elog_type elog_type;
+ /// Type of this endpoint's access logging policy
+ typedef typename config::alog_type alog_type;
+
+ /// Type of this endpoint transport component's associated connection
+ /// transport component.
+ typedef stub::connection<config> transport_con_type;
+ /// Type of a shared pointer to this endpoint transport component's
+ /// associated connection transport component
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ // generate and manage our own io_service
+ explicit endpoint()
+ {
+ //std::cout << "transport::iostream::endpoint constructor" << std::endl;
+ }
+
+ /// Set whether or not endpoint can create secure connections
+ /**
+ * TODO: docs
+ *
+ * Setting this value only indicates whether or not the endpoint is capable
+ * of producing and managing secure connections. Connections produced by
+ * this endpoint must also be individually flagged as secure if they are.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not the endpoint can create secure connections.
+ */
+ void set_secure(bool value) {}
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * TODO: docs
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return false;
+ }
+protected:
+ /// Initialize logging
+ /**
+ * The loggers are located in the main endpoint class. As such, the
+ * transport doesn't have direct access to them. This method is called
+ * by the endpoint constructor to allow shared logging from the transport
+ * component. These are raw pointers to member variables of the endpoint.
+ * In particular, they cannot be used in the transport constructor as they
+ * haven't been constructed yet, and cannot be used in the transport
+ * destructor as they will have been destroyed by then.
+ *
+ * @param a A pointer to the access logger to use.
+ * @param e A pointer to the error logger to use.
+ */
+ void init_logging(alog_type * a, elog_type * e) {}
+
+ /// Initiate a new connection
+ /**
+ * @param tcon A pointer to the transport connection component of the
+ * connection to connect.
+ * @param u A URI pointer to the URI to connect to.
+ * @param cb The function to call back with the results when complete.
+ */
+ void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) {
+ cb(make_error_code(error::not_implemented));
+ }
+
+ /// Initialize a connection
+ /**
+ * Init is called by an endpoint once for each newly created connection.
+ * It's purpose is to give the transport policy the chance to perform any
+ * transport specific initialization that couldn't be done via the default
+ * constructor.
+ *
+ * @param tcon A pointer to the transport portion of the connection.
+ * @return A status code indicating the success or failure of the operation
+ */
+ lib::error_code init(transport_con_ptr tcon) {
+ return make_error_code(error::not_implemented);
+ }
+private:
+
+};
+
+} // namespace stub
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_STUB_HPP