diff --git a/Makefile b/Makefile index e004eb30be..f69b12b92a 100644 --- a/Makefile +++ b/Makefile @@ -74,8 +74,8 @@ srcdir ?= src CXX ?= c++ AR ?= ar PIC ?= PIC -BUILD_TYPE ?= "default" -SHARED ?= "1" +BUILD_TYPE ?= default +SHARED ?= 1 # Internal Variables @@ -84,7 +84,7 @@ include_path = $(prefix)/$(includedir) # BUILD_TYPE specific settings ifeq ($(BUILD_TYPE), debug) - CXXFLAGS = $(cxxflags_debug) + CXXFLAGS : = $(cxxflags_debug) $(CXXFLANGS_EXTRA) libname := $(libname_debug) else CXXFLAGS ?= $(cxxflags_default) diff --git a/src/legacy/interfaces/connection_handler.hpp b/src/legacy/interfaces/connection_handler.hpp deleted file mode 100644 index 7fbff6cb35..0000000000 --- a/src/legacy/interfaces/connection_handler.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2011, 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 WEBSOCKET_INTERFACE_CONNECTION_HANDLER_HPP -#define WEBSOCKET_INTERFACE_CONNECTION_HANDLER_HPP - -#include - -#include -#include - -namespace websocketpp { - -class connection_handler { -public: - // validate will be called after a websocket handshake has been received and - // before it is accepted. It provides a handler the ability to refuse a - // connection based on application specific logic (ex: restrict domains or - // negotiate subprotocols). To reject the connection throw a handshake_error - // - // Validate is never called for client sessions. To refuse a client session - // (ex: if you do not like the set of extensions/subprotocols the server - // chose) you can close the connection immediately in the on_open method. - // - // handshake_error parameters: - // log_message - error message to send to server log - // http_error_code - numeric HTTP error code to return to the client - // http_error_msg - (optional) string HTTP error code to return to the - // client (useful for returning non-standard error codes) - virtual void validate(session_ptr session) {}; - - // on_open is called after the websocket session has been successfully - // established and is in the OPEN state. The session is now avaliable to - // send messages and will begin reading frames and calling the on_message/ - // on_close/on_error callbacks. A client may reject the connection by - // closing the session at this point. - virtual void on_open(session_ptr session) = 0; - - // on_close is called whenever an open session is closed for any reason. - // This can be due to either endpoint requesting a connection close or an - // error occuring. Information about why the session was closed can be - // extracted from the session itself. - // - // on_close will be the last time a session calls its handler. If your - // application will need information from `session` after this function you - // should either save the session_ptr somewhere or copy the data out. - virtual void on_close(session_ptr session) = 0; - - // on_message (binary version) will be called when a binary message is - // recieved. Message data is passed as a vector of bytes (unsigned char). - // data will not be avaliable after this callback ends so the handler must - // either completely process the message or copy it somewhere else for - // processing later. - virtual void on_message(session_ptr session, - const std::vector &data) = 0; - - // on_message (text version). Identical to on_message except the data - // parameter is a string interpreted as UTF-8. WebSocket++ guarantees that - // this string is valid UTF-8. - virtual void on_message(session_ptr session,const std::string &msg) = 0; - - - - // #### optional error cases #### - - // on_fail is called whenever a session is terminated or failed before it - // was successfully established. This happens if there is an error during - // the handshake process or if the server refused the connection. - // - // on_fail will be the last time a session calls its handler. If your - // application will need information from `session` after this function you - // should either save the session_ptr somewhere or copy the data out. - virtual void on_fail(session_ptr session) {}; - - // experimental - virtual void on_ping_timeout(session_ptr session) {} -}; - -} -#endif // WEBSOCKET_INTERFACE_CONNECTION_HANDLER_HPP diff --git a/src/legacy/interfaces/frame_parser.hpp b/src/legacy/interfaces/frame_parser.hpp deleted file mode 100644 index 68f5bf86f1..0000000000 --- a/src/legacy/interfaces/frame_parser.hpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2011, 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 WEBSOCKET_INTERFACE_FRAME_PARSER_HPP -#define WEBSOCKET_INTERFACE_FRAME_PARSER_HPP - -#include -#include -#include - -namespace websocketpp { -namespace frame { - -namespace error { - enum value { - FATAL_SESSION_ERROR = 0, // force session end - SOFT_SESSION_ERROR = 1, // should log and ignore - PROTOCOL_VIOLATION = 2, // must end session - PAYLOAD_VIOLATION = 3, // should end session - INTERNAL_SERVER_ERROR = 4, // cleanly end session - MESSAGE_TOO_BIG = 5 // ??? - }; -} - -// Opcodes are 4 bits -// See spec section 5.2 -namespace opcode { - enum value { - CONTINUATION = 0x0, - TEXT = 0x1, - BINARY = 0x2, - RSV3 = 0x3, - RSV4 = 0x4, - RSV5 = 0x5, - RSV6 = 0x6, - RSV7 = 0x7, - CLOSE = 0x8, - PING = 0x9, - PONG = 0xA, - CONTROL_RSVB = 0xB, - CONTROL_RSVC = 0xC, - CONTROL_RSVD = 0xD, - CONTROL_RSVE = 0xE, - CONTROL_RSVF = 0xF, - }; - - inline bool reserved(value v) { - return (v >= RSV3 && v <= RSV7) || - (v >= CONTROL_RSVB && v <= CONTROL_RSVF); - } - - inline bool invalid(value v) { - return (v > 0xF || v < 0); - } - - inline bool is_control(value v) { - return v >= 0x8; - } -} - -namespace limits { - static const uint8_t PAYLOAD_SIZE_BASIC = 125; - static const uint16_t PAYLOAD_SIZE_EXTENDED = 0xFFFF; // 2^16, 65535 - static const uint64_t PAYLOAD_SIZE_JUMBO = 0x7FFFFFFFFFFFFFFF;//2^63 - - // hardcoded limit - static const uint64_t INTERNAL_MAX_PAYLOAD_SIZE = 100000000; // 100MB -} - -class exception : public std::exception { -public: - exception(const std::string& msg, - frame::error::value code = frame::error::FATAL_SESSION_ERROR) - : m_msg(msg),m_code(code) {} - ~exception() throw() {} - - virtual const char* what() const throw() { - return m_msg.c_str(); - } - - frame::error::value code() const throw() { - return m_code; - } - - std::string m_msg; - frame::error::value m_code; -}; - -class interface { -public: - // consume - virtual bool ready() const = 0; - virtual bool uint64_t get_bytes_needed() const = 0; - virtual void reset() = 0; - - virtual void consume(std::istream& s) = 0; - - // retrieve buffers - - // read frame options - - // Is the last fragment in a message sequence? - virtual bool get_fin() const = 0; - virtual opcode::value() const = 0; - -}; - - -} -} -#endif // WEBSOCKET_INTERFACE_FRAME_PARSER_HPP diff --git a/src/legacy/interfaces/session.hpp b/src/legacy/interfaces/session.hpp deleted file mode 100644 index e150bbeb14..0000000000 --- a/src/legacy/interfaces/session.hpp +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright (c) 2011, 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 WEBSOCKET_INTERFACE_SESSION_HPP -#define WEBSOCKET_INTERFACE_SESSION_HPP - -#include - -#include -#include - -#include "../websocket_constants.hpp" -#include "../network_utilities.hpp" - -namespace websocketpp { -namespace session { - -namespace state { - enum value { - CONNECTING = 0, - OPEN = 1, - CLOSING = 2, - CLOSED = 3 - }; -} - -namespace error { - enum value { - FATAL_ERROR = 0, // force session end - SOFT_ERROR = 1, // should log and ignore - PROTOCOL_VIOLATION = 2, // must end session - PAYLOAD_VIOLATION = 3, // should end session - INTERNAL_SERVER_ERROR = 4, // cleanly end session - MESSAGE_TOO_BIG = 5 // ??? - }; -} - -class exception : public std::exception { -public: - exception(const std::string& msg, - error::value code = error::FATAL_ERROR) - : m_msg(msg),m_code(code) {} - ~exception() throw() {} - - virtual const char* what() const throw() { - return m_msg.c_str(); - } - - error::value code() const throw() { - return m_code; - } - - std::string m_msg; - error::value m_code; -}; - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Server API * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* -server(uint16_t port, server_handler_ptr handler) -void run(); - - -*/ - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Server Session API * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -class server { -public: - // Valid always - virtual session::state::value get_state() const = 0; - virtual unsigned int get_version() const = 0; - - virtual std::string get_request_header(const std::string& key) const = 0; - virtual std::string get_origin() const = 0; - - // Information about the requested URI - virtual bool get_secure() const = 0; - virtual std::string get_host() const = 0; - virtual std::string get_resource() const = 0; - virtual uint16_t get_port() const = 0; - - // Information about the connected endpoint - /* Tentative API member function */ virtual boost::asio::ip::tcp::endpoint get_endpoint() const = 0; - - // Valid for CONNECTING state - virtual void add_response_header(const std::string& key, const std::string& value) = 0; - virtual void replace_response_header(const std::string& key, const std::string& value) = 0; - virtual const std::vector& get_subprotocols() const = 0; - virtual const std::vector& get_extensions() const = 0; - virtual void select_subprotocol(const std::string& value) = 0; - virtual void select_extension(const std::string& value) = 0; - - // Valid for OPEN state - virtual void send(const utf8_string& payload) = 0; - virtual void send(const binary_string& data) = 0; - virtual void close(close::status::value code, const utf8_string& reason) = 0; - virtual void ping(const binary_string& payload) = 0; - virtual void pong(const binary_string& payload) = 0; - - virtual uint64_t buffered_amount() const = 0; - - // Valid for CLOSED state - virtual close::status::value get_local_close_code() const = 0; - virtual utf8_string get_local_close_reason() const = 0; - virtual close::status::value get_remote_close_code() const = 0; - virtual utf8_string get_remote_close_reason() const = 0; - virtual bool get_failed_by_me() const = 0; - virtual bool get_dropped_by_me() const = 0; - virtual bool get_closed_by_me() const = 0; -}; -typedef boost::shared_ptr server_ptr; -typedef server_ptr server_session_ptr; - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Server Handler API * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -class server_handler { -public: - virtual ~server_handler() {} - - // validate will be called after a websocket handshake has been received and - // before it is accepted. It provides a handler the ability to refuse a - // connection based on application specific logic (ex: restrict domains or - // negotiate subprotocols). To reject the connection throw a handshake_error - // - // handshake_error parameters: - // log_message - error message to send to server log - // http_error_code - numeric HTTP error code to return to the client - // http_error_msg - (optional) string HTTP error code to return to the - // client (useful for returning non-standard error codes) - virtual void validate(server_ptr session) = 0; - - // on_open is called after the websocket session has been successfully - // established and is in the OPEN state. The session is now avaliable to - // send messages and will begin reading frames and calling the on_message/ - // on_close/on_error callbacks. A client may reject the connection by - // closing the session at this point. - virtual void on_open(server_ptr session) = 0; - - // on_close is called whenever an open session is closed for any reason. - // This can be due to either endpoint requesting a connection close or an - // error occuring. Information about why the session was closed can be - // extracted from the session itself. - // - // on_close will be the last time a session calls its handler. If your - // application will need information from `session` after this function you - // should either save the session_ptr somewhere or copy the data out. - virtual void on_close(server_ptr session) = 0; - - // on_message (binary version) will be called when a binary message is - // recieved. Message data is passed as a vector of bytes (unsigned char). - // data will not be avaliable after this callback ends so the handler must - // either completely process the message or copy it somewhere else for - // processing later. - virtual void on_message(server_ptr session, binary_string_ptr data) = 0; - - // on_message (text version). Identical to on_message except the data - // parameter is a string interpreted as UTF-8. WebSocket++ guarantees that - // this string is valid UTF-8. - virtual void on_message(server_ptr session, utf8_string_ptr msg) = 0; - - // on_fail is called whenever a session is terminated or failed before it - // was successfully established. This happens if there is an error during - // the handshake process or if the server refused the connection. - // - // on_fail will be the last time a session calls its handler. If your - // application will need information from `session` after this function you - // should either save the session_ptr somewhere or copy the data out. - virtual void on_fail(server_ptr session) {}; - - // on_ping is called whenever a ping is recieved with the binary - // application data from the ping. If on_ping returns true the - // implimentation will send a response pong. - virtual bool on_ping(server_ptr session, binary_string_ptr data) { - return true; - } - - // on_pong is called whenever a pong is recieved with the binary - // application data from the pong. - virtual void on_pong(server_ptr session, binary_string_ptr data) {} - - // TODO: on_ping_timeout -}; - -typedef boost::shared_ptr server_handler_ptr; - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Client Session API * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -class client { -public: - client(const std::string& uri) {}; - - // Valid always - virtual session::state::value get_state() const = 0; - virtual int get_version() const = 0; - - virtual std::string get_origin() const = 0; - virtual bool get_secure() const = 0; - virtual std::string get_host() const = 0; - virtual std::string get_resource() const = 0; - virtual uint16_t get_port() const = 0; - - // Valid for CONNECTING state - virtual void set_origin(const std::string& origin) = 0; - virtual void add_request_header(const std::string& key, const std::string& value) = 0; - virtual void replace_request_header(const std::string& key, const std::string& value) = 0; - virtual void request_subprotocol(const std::string& value) = 0; - virtual void request_extension(const std::string& value) = 0; - - // Valid for OPEN state - virtual std::string get_response_header(const std::string& key) const = 0; - virtual std::string get_subprotocol() const; - virtual const std::vector& get_extensions() const = 0; - - virtual void send(const utf8_string& msg) = 0; - virtual void send(const binary_string& data) = 0; - virtual void close(close::status::value code, const binary_string& reason) = 0; - virtual void ping(const binary_string& payload) = 0; - virtual void pong(const binary_string& payload) = 0; - - // Valid for CLOSED state - virtual close::status::value get_local_close_code() const = 0; - virtual utf8_string get_local_close_reason() const = 0; - virtual close::status::value get_remote_close_code() const = 0; - virtual utf8_string get_remote_close_reason() const = 0; - virtual bool failed_by_me() const = 0; - virtual bool dropped_by_me() const = 0; - virtual bool closed_by_me() const = 0; -}; - -typedef boost::shared_ptr client_ptr; - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Client Handler API * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -class client_handler { -public: - // on_open is called after the websocket session has been successfully - // established and is in the OPEN state. The session is now avaliable to - // send messages and will begin reading frames and calling the on_message/ - // on_close/on_error callbacks. A client may reject the connection by - // closing the session at this point. - virtual void on_open(client_ptr session) = 0; - - // on_close is called whenever an open session is closed for any reason. - // This can be due to either endpoint requesting a connection close or an - // error occuring. Information about why the session was closed can be - // extracted from the session itself. - // - // on_close will be the last time a session calls its handler. If your - // application will need information from `session` after this function you - // should either save the session_ptr somewhere or copy the data out. - virtual void on_close(client_ptr session) = 0; - - // on_message (binary version) will be called when a binary message is - // recieved. Message data is passed as a vector of bytes (unsigned char). - // data will not be avaliable after this callback ends so the handler must - // either completely process the message or copy it somewhere else for - // processing later. - virtual void on_message(client_ptr session, binary_string_ptr data) = 0; - - // on_message (text version). Identical to on_message except the data - // parameter is a string interpreted as UTF-8. WebSocket++ guarantees that - // this string is valid UTF-8. - virtual void on_message(client_ptr session, utf8_string_ptr msg) = 0; - - // on_fail is called whenever a session is terminated or failed before it - // was successfully established. This happens if there is an error during - // the handshake process or if the server refused the connection. - // - // on_fail will be the last time a session calls its handler. If your - // application will need information from `session` after this function you - // should either save the session_ptr somewhere or copy the data out. - virtual void on_fail(client_ptr session) {}; - - // on_ping is called whenever a ping is recieved with the binary - // application data from the ping. If on_ping returns true the - // implimentation will send a response pong. - virtual bool on_ping(server_ptr session, binary_string_ptr data) { - return true; - } - - // on_pong is called whenever a pong is recieved with the binary - // application data from the pong. - virtual void on_pong(server_ptr session, binary_string_ptr data) {} - - // TODO: on_ping_timeout -}; - -typedef boost::shared_ptr client_handler_ptr; - -} -} -#endif // WEBSOCKET_INTERFACE_SESSION_HPP diff --git a/src/legacy/policy.cpp b/src/legacy/policy.cpp deleted file mode 100644 index dd34320642..0000000000 --- a/src/legacy/policy.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* -build situation -endpoint.hpp -- libboost_system -- libboost_date_time (if you use the default logger) - -sockets/ssl.hpp -- libcrypto -- libssl - -*/ - -// Application start -#include "endpoint.hpp" -#include "roles/server.hpp" -#include "sockets/ssl.hpp" - -typedef websocketpp::endpoint endpoint_type; -//typedef websocketpp::endpoint endpoint_type; -typedef websocketpp::role::server::handler handler_type; -typedef websocketpp::role::server::handler_ptr handler_ptr; - -// application headers -class application_server_handler : public handler_type { -public: - void validate(handler_type::connection_ptr connection) { - //std::cout << "state: " << connection->get_state() << std::endl; - } - - void on_open(handler_type::connection_ptr connection) { - //std::cout << "connection opened" << std::endl; - } - - void on_close(handler_type::connection_ptr connection) { - //std::cout << "connection closed" << std::endl; - } - - void on_message(connection_ptr connection,websocketpp::utf8_string_ptr msg) { - //std::cout << "got message: " << *msg << std::endl; - connection->send(*msg); - } - void on_message(connection_ptr connection,websocketpp::binary_string_ptr data) { - //std::cout << "got binary message of length: " << data->size() << std::endl; - connection->send(*data); - } - - void http(handler_type::connection_ptr connection) { - connection->set_body("HTTP Response!!"); - } - - void on_fail(handler_type::connection_ptr connection) { - std::cout << "connection failed" << std::endl; - } -}; - -typedef websocketpp::endpoint basic_client; -typedef websocketpp::role::client::handler client_handler_type; -typedef websocketpp::role::client::handler_ptr client_handler_ptr; - -class application_client_handler : public client_handler_type { - void validate(handler_type::connection_ptr connection) { - std::cout << "state: " << connection->get_state() << std::endl; - } - - void on_open(handler_type::connection_ptr connection) { - std::cout << "connection opened" << std::endl; - } - - void on_close(handler_type::connection_ptr connection) { - std::cout << "connection closed" << std::endl; - } - - void on_message(connection_ptr connection,websocketpp::utf8_string_ptr msg) { - std::cout << "got message: " << *msg << std::endl; - connection->send(*msg); - } - void on_message(connection_ptr connection,websocketpp::binary_string_ptr data) { - std::cout << "got binary message of length: " << data->size() << std::endl; - connection->send(*data); - } - - void http(handler_type::connection_ptr connection) { - connection->set_body("HTTP Response!!"); - } - - void on_fail(handler_type::connection_ptr connection) { - std::cout << "connection failed" << std::endl; - } -}; - - -int main () { - handler_ptr h(new application_client_handler()); - endpoint_type e(h); - - e.alog().set_level(websocketpp::log::alevel::ALL); - e.elog().set_level(websocketpp::log::elevel::ALL); - - - e.connect("ws://localhost:9002"); - - return 0; -} \ No newline at end of file diff --git a/src/legacy/session_handler_interface.hpp b/src/legacy/session_handler_interface.hpp deleted file mode 100644 index 236904fdc0..0000000000 --- a/src/legacy/session_handler_interface.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2011, 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_SESSION_HANDLER_INTERFACE_HPP -#define WEBSOCKETPP_SESSION_HANDLER_INTERFACE_HPP - -#include - -#include -#include - -namespace websocketpp { -namespace session { - - - -class session_handler_interface { -public: - -}; - - -} -} -#endif // WEBSOCKETPP_SESSION_HANDLER_INTERFACE_HPP diff --git a/src/legacy/websocket_client.cpp b/src/legacy/websocket_client.cpp deleted file mode 100644 index 1e08d9d6bd..0000000000 --- a/src/legacy/websocket_client.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2011, 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. - * - */ - -#include "websocket_client.hpp" - -#include -#include - -#include - -using websocketpp::client; -using boost::asio::ip::tcp; - -client::client(boost::asio::io_service& io_service, - websocketpp::connection_handler_ptr defc) - : m_elog_level(LOG_OFF), - m_alog_level(ALOG_OFF), - m_state(CLIENT_STATE_NULL), - m_max_message_size(DEFAULT_MAX_MESSAGE_SIZE), - m_io_service(io_service), - m_resolver(io_service), - m_def_con_handler(defc) {} - -void client::init() { - // TODO: sanity check whether the session buffer size bound could be reduced - m_client_session = client_session_ptr( - new client_session( - shared_from_this(), - m_io_service, - m_def_con_handler, - m_max_message_size*2 - ) - ); - m_state = CLIENT_STATE_INITIALIZED; -} - -void client::connect(const std::string& uri) { - if (m_state != CLIENT_STATE_INITIALIZED) { - throw client_error("connect can only be called after init and before a connection has been established"); - } - - m_client_session->set_uri(uri); - - std::stringstream port; - port << m_client_session->get_port(); - - - tcp::resolver::query query(m_client_session->get_host(), - port.str()); - tcp::resolver::iterator iterator = m_resolver.resolve(query); - - boost::asio::async_connect(m_client_session->socket(), - iterator,boost::bind(&client::handle_connect, - this, - boost::asio::placeholders::error)); - m_state = CLIENT_STATE_CONNECTING; -} - - -void client::add_subprotocol(const std::string& p) { - if (m_state != CLIENT_STATE_INITIALIZED) { - throw client_error("add_protocol can only be called after init and before connect"); - } - m_client_session->add_subprotocol(p); -} - -void client::set_header(const std::string& key,const std::string& val) { - if (m_state != CLIENT_STATE_INITIALIZED) { - throw client_error("set_header can only be called after init and before connect"); - } - m_client_session->set_header(key,val); -} - -void client::set_origin(const std::string& val) { - if (m_state != CLIENT_STATE_INITIALIZED) { - throw client_error("set_origin can only be called after init and before connect"); - } - m_client_session->set_origin(val); -} - - -void client::set_max_message_size(uint64_t val) { - if (val > frame::PAYLOAD_64BIT_LIMIT) { - std::stringstream err; - err << "Invalid maximum message size: " << val; - - // TODO: Figure out what the ideal error behavior for this method. - // Options: - // Throw exception - // Log error and set value to maximum allowed - // Log error and leave value at whatever it was before - log(err.str(),LOG_WARN); - //throw client_error(err.str()); - } - m_max_message_size = val; -} - -bool client::test_elog_level(uint16_t level) { - return (level >= m_elog_level); -} -void client::set_elog_level(uint16_t level) { - std::stringstream msg; - msg << "Error logging level changing from " - << m_elog_level << " to " << level; - log(msg.str(),LOG_INFO); - - m_elog_level = level; -} -bool client::test_alog_level(uint16_t level) { - return ((level & m_alog_level) != 0); -} -void client::set_alog_level(uint16_t level) { - if (test_alog_level(level)) { - return; - } - std::stringstream msg; - msg << "Access logging level " << level << " being set"; - access_log(msg.str(),ALOG_INFO); - - m_alog_level |= level; -} -void client::unset_alog_level(uint16_t level) { - if (!test_alog_level(level)) { - return; - } - std::stringstream msg; - msg << "Access logging level " << level << " being unset"; - access_log(msg.str(),ALOG_INFO); - - m_alog_level &= ~level; -} - -bool client::validate_message_size(uint64_t val) { - if (val > m_max_message_size) { - return false; - } - return true; -} - -void client::log(std::string msg,uint16_t level) { - if (!test_elog_level(level)) { - return; - } - std::cerr << "[Error Log] " - << boost::posix_time::to_iso_extended_string( - boost::posix_time::second_clock::local_time()) - << " " << msg << std::endl; -} -void client::access_log(std::string msg,uint16_t level) { - if (!test_alog_level(level)) { - return; - } - std::cout << "[Access Log] " - << boost::posix_time::to_iso_extended_string( - boost::posix_time::second_clock::local_time()) - << " " << msg << std::endl; -} - -void client::handle_connect(const boost::system::error_code& error) { - if (!error) { - std::stringstream err; - err << "Successful Connection "; - log(err.str(),LOG_ERROR); - - std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " TCP established" << std::endl; - - m_state = CLIENT_STATE_CONNECTED; - m_client_session->on_connect(); - } else { - std::stringstream err; - err << "An error occurred while establishing a connection: " << error; - - log(err.str(),LOG_ERROR); - throw client_error(err.str()); - } -} diff --git a/src/legacy/websocket_client.hpp b/src/legacy/websocket_client.hpp deleted file mode 100644 index 0d0bd655eb..0000000000 --- a/src/legacy/websocket_client.hpp +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2011, 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 WEBSOCKET_CLIENT_HPP -#define WEBSOCKET_CLIENT_HPP - -#include -#include -#include -#include -namespace po = boost::program_options; - -#include - -#include "websocketpp.hpp" -#include "websocket_session.hpp" -#include "websocket_connection_handler.hpp" - -#include - -#include "rng/boost_rng.hpp" - -#include "http/parser.hpp" - -using boost::asio::ip::tcp; - -namespace websocketpp { - -class client_error : public std::exception { -public: - client_error(const std::string& msg) - : m_msg(msg) {} - ~client_error() throw() {} - - virtual const char* what() const throw() { - return m_msg.c_str(); - } -private: - std::string m_msg; -}; - -template -class client : public boost::enable_shared_from_this > { -public: - typedef rng_policy rng_t; - - typedef client endpoint_type; - typedef session session_type; - typedef connection_handler connection_handler_type; - - typedef boost::shared_ptr ptr; - typedef boost::shared_ptr session_ptr; - typedef boost::shared_ptr connection_handler_ptr; - - static const uint16_t CLIENT_STATE_NULL = 0; - static const uint16_t CLIENT_STATE_INITIALIZED = 1; - static const uint16_t CLIENT_STATE_CONNECTING = 2; - static const uint16_t CLIENT_STATE_CONNECTED = 3; - - client(boost::asio::io_service& io_service, - connection_handler_ptr defc) - : m_elog_level(LOG_OFF), - m_alog_level(ALOG_OFF), - m_state(CLIENT_STATE_NULL), - m_max_message_size(DEFAULT_MAX_MESSAGE_SIZE), - m_io_service(io_service), - m_resolver(io_service), - m_def_con_handler(defc) {} - - // INTERFACE FOR LOCAL APPLICATIONS - - // initializes the session. Methods that affect the opening handshake - // such as add_protocol and set_header must be called after init and - // before connect. - void init() { - // TODO: sanity check whether the session buffer size bound could be reduced - m_client_session = session_ptr( - new session_type( - endpoint_type::shared_from_this(), - m_io_service, - m_def_con_handler, - m_max_message_size*2 - ) - ); - m_state = CLIENT_STATE_INITIALIZED; - } - - // starts the connection process. Should be called before - // io_service.run(), connection process will not start until run() has - // been called. - void connect(const std::string& u) { - if (m_state != CLIENT_STATE_INITIALIZED) { - throw client_error("connect can only be called after init and before a connection has been established"); - } - - ws_uri uri; - - if (!uri.parse(u)) { - throw client_error("Invalid WebSocket URI"); - } - - if (uri.secure) { - throw client_error("wss / secure connections are not supported at this time"); - } - - m_client_session->set_uri(uri); - - std::stringstream port; - port << uri.port; - - tcp::resolver::query query(uri.host,port.str()); - tcp::resolver::iterator iterator = m_resolver.resolve(query); - - boost::asio::async_connect( - m_client_session->socket(), - iterator, - boost::bind( - &endpoint_type::handle_connect, - endpoint_type::shared_from_this(), - boost::asio::placeholders::error - ) - ); - m_state = CLIENT_STATE_CONNECTING; - } - - // Adds a protocol to the opening handshake. - // Must be called before connect - void add_subprotocol(const std::string& p) { - if (m_state != CLIENT_STATE_INITIALIZED) { - throw client_error("add_protocol can only be called after init and before connect"); - } - m_client_session->add_subprotocol(p); - } - - // Sets the value of the given HTTP header to be sent during the - // opening handshake. Must be called before connect - void set_header(const std::string& key,const std::string& val) { - if (m_state != CLIENT_STATE_INITIALIZED) { - throw client_error("set_header can only be called after init and before connect"); - } - m_client_session->set_request_header(key,val); - } - - void set_origin(const std::string& val) { - if (m_state != CLIENT_STATE_INITIALIZED) { - throw client_error("set_origin can only be called after init and before connect"); - } - m_client_session->set_origin(val); - } - - void set_max_message_size(uint64_t val) { - if (val > frame::limits::PAYLOAD_SIZE_JUMBO) { - std::stringstream err; - err << "Invalid maximum message size: " << val; - - // TODO: Figure out what the ideal error behavior for this method. - // Options: - // Throw exception - // Log error and set value to maximum allowed - // Log error and leave value at whatever it was before - log(err.str(),LOG_WARN); - //throw client_error(err.str()); - } - m_max_message_size = val; - } - - // Test methods determine if a message of the given level should be - // written. elog shows all values above the level set. alog shows only - // the values explicitly set. - bool test_elog_level(uint16_t level) { - return (level >= m_elog_level); - } - void set_elog_level(uint16_t level) { - std::stringstream msg; - msg << "Error logging level changing from " - << m_elog_level << " to " << level; - log(msg.str(),LOG_INFO); - - m_elog_level = level; - } - - bool test_alog_level(uint16_t level) { - return ((level & m_alog_level) != 0); - } - void set_alog_level(uint16_t level) { - if (test_alog_level(level)) { - return; - } - std::stringstream msg; - msg << "Access logging level " << level << " being set"; - access_log(msg.str(),ALOG_INFO); - - m_alog_level |= level; - } - void unset_alog_level(uint16_t level) { - if (!test_alog_level(level)) { - return; - } - std::stringstream msg; - msg << "Access logging level " << level << " being unset"; - access_log(msg.str(),ALOG_INFO); - - m_alog_level &= ~level; - } - - // INTERFACE FOR SESSIONS - - // Check if message size is within server's acceptable parameters - bool validate_message_size(uint64_t val) { - if (val > m_max_message_size) { - return false; - } - return true; - } - - // write to the server's logs - void log(std::string msg,uint16_t level = LOG_ERROR) { - if (!test_elog_level(level)) { - return; - } - std::cerr << "[Error Log] " - << boost::posix_time::to_iso_extended_string( - boost::posix_time::second_clock::local_time()) - << " " << msg << std::endl; - } - void access_log(std::string msg,uint16_t level) { - if (!test_alog_level(level)) { - return; - } - std::cout << "[Access Log] " - << boost::posix_time::to_iso_extended_string( - boost::posix_time::second_clock::local_time()) - << " " << msg << std::endl; - } -private: - // if no errors starts the session's read loop and returns to the - // start_accept phase. - void handle_connect(const boost::system::error_code& error) { - if (!error) { - std::stringstream err; - err << "Successful Connection "; - log(err.str(),LOG_ERROR); - - m_state = CLIENT_STATE_CONNECTED; - m_client_session->on_connect(); - } else { - std::stringstream err; - err << "An error occurred while establishing a connection: " << error; - - log(err.str(),LOG_ERROR); - throw client_error(err.str()); - } - } - -private: - uint16_t m_elog_level; - uint16_t m_alog_level; - - uint16_t m_state; - - std::set m_hosts; - uint64_t m_max_message_size; - boost::asio::io_service& m_io_service; - tcp::resolver m_resolver; - session_ptr m_client_session; - connection_handler_ptr m_def_con_handler; -}; - -} - -#endif // WEBSOCKET_CLIENT_HPP diff --git a/src/legacy/websocket_client_session.cpp b/src/legacy/websocket_client_session.cpp deleted file mode 100644 index 8532afdde6..0000000000 --- a/src/legacy/websocket_client_session.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright (c) 2011, 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. - * - */ - -#include "websocketpp.hpp" -#include "websocket_client_session.hpp" - -#include "websocket_frame.hpp" -#include "utf8_validator/utf8_validator.hpp" - -#include -#include -#include -#include -#include - - -#include -#include -#include -#include - -using websocketpp::client_session; - -client_session::client_session (websocketpp::client_ptr c, - boost::asio::io_service& io_service, - websocketpp::connection_handler_ptr defc, - uint64_t buf_size) - : session(io_service,defc,buf_size),m_client(c) {} - -void client_session::on_connect() { - // TODO: section 4.1: Figure out if we have another connection to this - // host/port pending. - write_handshake(); -} - -void client_session::set_uri(const std::string& uri) { - if (!m_uri.parse(uri)) { - throw client_error("Invalid WebSocket URI"); - } - - if (m_uri.secure) { - throw client_error("wss / secure connections are not supported at this time"); - } - - m_resource = m_uri.resource; - - std::stringstream l; - - l << "parsed websocket url: secure: " << m_uri.secure << " host: " << m_uri.host - << " port (final): " << m_uri.port << " resource " << m_uri.resource; - - log(l.str(),LOG_DEBUG); -} - -bool client_session::get_secure() const { - return m_uri.secure; -} - -std::string client_session::get_host() const{ - return m_uri.host; -} - -uint16_t client_session::get_port() const { - return m_uri.port; -} - -void client_session::set_header(const std::string &key,const std::string &val) { - // TODO: prevent use of reserved headers - m_client_headers[key] = val; -} - -void client_session::set_origin(const std::string& val) { - // TODO: input validation - m_client_origin = val; -} - -void client_session::add_subprotocol(const std::string &val) { - // TODO: input validation - m_client_subprotocols.push_back(val); -} - -void client_session::add_extension(const std::string& val) { - // TODO: input validation - m_client_extensions.push_back(val); -} - -void client_session::read_handshake() { - boost::asio::async_read_until( - m_socket, - m_buf, - "\r\n\r\n", - boost::bind( - &session::handle_read_handshake, - shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred - ) - ); -} - -void client_session::handle_read_handshake(const boost::system::error_code& e, - std::size_t bytes_transferred) { - - if (e) { - log_error("Error reading server handshake",e); - drop_tcp(); - return; - } - - // parse server handshake - std::istream response_stream(&m_buf); - std::string header; - std::string::size_type end; - - // get status line - std::getline(response_stream, header); - if (header[header.size()-1] == '\r') { - header.erase(header.end()-1); - m_server_http_request = header; - m_raw_server_handshake += header+"\n"; - } - - // get headers - while (std::getline(response_stream, header) && header != "\r") { - if (header[header.size()-1] != '\r') { - continue; // ignore malformed header lines? - } else { - header.erase(header.end()-1); - } - - end = header.find(": ",0); - - if (end != std::string::npos) { - std::string h = header.substr(0,end); - if (get_server_header(h) == "") { - m_server_headers[h] = header.substr(end+2); - } else { - m_server_headers[h] += ", " + header.substr(end+2); - } - } - - m_raw_server_handshake += header+"\n"; - } - - // temporary debugging - if (m_buf.size() > 0) { - std::stringstream foo; - foo << "bytes left over: " << m_buf.size(); - access_log(foo.str(), ALOG_HANDSHAKE); - } - - m_client->access_log(m_raw_server_handshake,ALOG_HANDSHAKE); - - // handshake error checking - try { - std::stringstream err; - std::string h; - - // TODO: allow versions greater than 1.1 - if (m_server_http_request.substr(0,9) != "HTTP/1.1 ") { - err << "Websocket handshake has invalid HTTP version: " - << m_server_http_request.substr(0,9); - - throw(handshake_error(err.str(),400)); - } - - // check the HTTP version - if (m_server_http_request.substr(9,3) != "101") { - err << "Websocket handshake ended with status " - << m_server_http_request.substr(9); - - // TODO: check version header for other supported versions. - - throw(handshake_error(err.str(),400)); - } - - // verify the presence of required headers - h = get_server_header("Upgrade"); - if (h == "") { - throw(handshake_error("Required Upgrade header is missing",400)); - } else if (!boost::iequals(h,"websocket")) { - err << "Upgrade header was \"" << h << "\" instead of \"websocket\""; - throw(handshake_error(err.str(),400)); - } - - h = get_server_header("Connection"); - if (h == "") { - throw(handshake_error("Required Connection header is missing",400)); - } else if (!boost::ifind_first(h,"upgrade")) { - err << "Connection header, \"" << h - << "\", does not contain required token \"upgrade\""; - throw(handshake_error(err.str(),400)); - } - - if (get_server_header("Sec-WebSocket-Accept") == "") { - throw(handshake_error("Required Sec-WebSocket-Key header is missing",400)); - } else { - // TODO: make a helper function for this. - std::string server_key = m_client_key; - server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - SHA1 sha; - uint32_t message_digest[5]; - - sha.Reset(); - sha << server_key.c_str(); - - if (!sha.Result(message_digest)) { - m_client->log("Error computing handshake sha1 hash.",LOG_ERROR); - // TODO: close behavior - return; - } - - // convert sha1 hash bytes to network byte order because this sha1 - // library works on ints rather than bytes - for (int i = 0; i < 5; i++) { - message_digest[i] = htonl(message_digest[i]); - } - - server_key = base64_encode( - reinterpret_cast(message_digest),20); - if (server_key != get_server_header("Sec-WebSocket-Accept")) { - m_client->log("Server key does not match",LOG_ERROR); - // TODO: close behavior - return; - } - } - } catch (const handshake_error& e) { - std::stringstream err; - err << "Caught handshake exception: " << e.what(); - - m_client->access_log(e.what(),ALOG_HANDSHAKE); - m_client->log(err.str(),LOG_ERROR); - - // TODO: close behavior - return; - } - - log_open_result(); - - m_state = STATE_OPEN; - - if (m_local_interface) { - m_local_interface->on_open(shared_from_this()); - } - - reset_message(); - read_frame(); -} - -void client_session::write_handshake() { - // generate client handshake. - std::string client_handshake; - - client_handshake += "GET "+m_resource+" HTTP/1.1\r\n"; - - set_header("Upgrade","websocket"); - set_header("Connection","Upgrade"); - set_header("Sec-WebSocket-Version","13"); - - set_header("Host",m_uri.host); - - if (m_client_origin != "") { - set_header("Origin",m_client_origin); - } - - // TODO: generate proper key - m_client_key = "XO4pxrIMLnK1CEVQP9untQ=="; - - int32_t raw_key[4]; - - boost::random::random_device rng; - boost::random::variate_generator > gen(rng, boost::random::uniform_int_distribution<>(INT32_MIN,INT32_MAX)); - - for (int i = 0; i < 4; i++) { - raw_key[i] = gen(); - } - - m_client_key = base64_encode(reinterpret_cast(raw_key), 16); - - m_client->access_log("Client key chosen: "+m_client_key, ALOG_HANDSHAKE); - - set_header("Sec-WebSocket-Key",m_client_key); - - - - set_header("User Agent","WebSocket++/2011-09-25"); - - header_list::iterator it; - for (it = m_client_headers.begin(); it != m_client_headers.end(); it++) { - client_handshake += it->first + ": " + it->second + "\r\n"; - } - - client_handshake += "\r\n"; - - m_raw_client_handshake = client_handshake; - - // start async write to handle_write_handshake - boost::asio::async_write( - m_socket, - boost::asio::buffer(m_raw_client_handshake), - boost::bind( - &session::handle_write_handshake, - shared_from_this(), - boost::asio::placeholders::error - ) - ); -} - -void client_session::handle_write_handshake(const boost::system::error_code& error) { - if (error) { - log_error("Error writing handshake",error); - drop_tcp(); - return; - } - - read_handshake(); -} - -void client_session::log(const std::string& msg, uint16_t level) const { - m_client->log(msg,level); -} - -void client_session::access_log(const std::string& msg, uint16_t level) const { - m_client->access_log(msg,level); -} diff --git a/src/legacy/websocket_client_session.hpp b/src/legacy/websocket_client_session.hpp deleted file mode 100644 index 2dd431e272..0000000000 --- a/src/legacy/websocket_client_session.hpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2011, 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 WEBSOCKET_CLIENT_SESSION_HPP -#define WEBSOCKET_CLIENT_SESSION_HPP - -#include -#include - -#include -#include - -#if defined(WIN32) -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace websocketpp { - class client_session; - typedef boost::shared_ptr client_session_ptr; -} - -#include "websocket_session.hpp" -#include "websocket_client.hpp" - -using boost::asio::ip::tcp; - -namespace websocketpp { - -class client_session : public session { -public: - client_session (client_ptr c, - boost::asio::io_service& io_service, - connection_handler_ptr defc, - uint64_t buf_size); - - /*** CLIENT INTERFACE ***/ - - // This function is called when a tcp connection has been established and - // the connection is ready to start the opening handshake. - void on_connect(); - - /*** HANDSHAKE INTERFACE ***/ - - void set_uri(const std::string& url); - - bool get_secure() const; - std::string get_host() const; - uint16_t get_port() const; - - // Set an HTTP header for the outgoing client handshake. - void set_header(const std::string& key,const std::string& val); - - // adds a subprotocol. This will result in the appropriate - // Sec-WebSocket-Protocol header being sent with the opening connection. - // Values will be sent in the order they were added. Servers interpret this - // order as the preferred order. - void add_subprotocol(const std::string &val); - - // Sets the origin value that will be sent to the server - void set_origin(const std::string &val); - - // Adds an extension to the extension list. Extensions are sent in the - // order added - void add_extension(const std::string& val); - - /*** SESSION INTERFACE ***/ - // see session - bool is_server() const {return false;} - - void log(const std::string& msg, uint16_t level) const; - void access_log(const std::string& msg, uint16_t level) const; -protected: - // Opening handshake processors and callbacks. - virtual void write_handshake(); - virtual void handle_write_handshake(const boost::system::error_code& e); - virtual void read_handshake(); - virtual void handle_read_handshake(const boost::system::error_code& e, - std::size_t bytes_transferred); - -private: - -protected: - ws_uri m_uri; - // url parts - bool m_secure; - std::string m_host; - uint16_t m_port; - - // handshake stuff - std::string m_client_key; - - // connection resources - client_ptr m_client; -private: - -}; - -} - -#endif // WEBSOCKET_CLIENT_SESSION_HPP diff --git a/src/legacy/websocket_connection_handler.hpp b/src/legacy/websocket_connection_handler.hpp deleted file mode 100644 index 652f5347f1..0000000000 --- a/src/legacy/websocket_connection_handler.hpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2011, 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 WEBSOCKET_CONNECTION_HANDLER_HPP -#define WEBSOCKET_CONNECTION_HANDLER_HPP - -#include - -#include -#include - -namespace websocketpp { - -template -class connection_handler { -public: - typedef connection_handler connection_handler_type; - - typedef boost::shared_ptr ptr; - typedef boost::shared_ptr session_ptr; - - // validate will be called after a websocket handshake has been received and - // before it is accepted. It provides a handler the ability to refuse a - // connection based on application specific logic (ex: restrict domains or - // negotiate subprotocols). To reject the connection throw a handshake_error - // - // Validate is never called for client sessions. To refuse a client session - // (ex: if you do not like the set of extensions/subprotocols the server - // chose) you can close the connection immediately in the on_open method. - // - // handshake_error parameters: - // log_message - error message to send to server log - // http_error_code - numeric HTTP error code to return to the client - // http_error_msg - (optional) string HTTP error code to return to the - // client (useful for returning non-standard error codes) - virtual void validate(session_ptr session) {}; - - // on_open is called after the websocket session has been successfully - // established and is in the OPEN state. The session is now avaliable to - // send messages and will begin reading frames and calling the on_message/ - // on_close/on_error callbacks. A client may reject the connection by - // closing the session at this point. - virtual void on_open(session_ptr session) = 0; - - // on_close is called whenever an open session is closed for any reason. - // This can be due to either endpoint requesting a connection close or an - // error occuring. Information about why the session was closed can be - // extracted from the session itself. - // - // on_close will be the last time a session calls its handler. If your - // application will need information from `session` after this function you - // should either save the session_ptr somewhere or copy the data out. - virtual void on_close(session_ptr session) = 0; - - // on_message (binary version) will be called when a binary message is - // recieved. Message data is passed as a vector of bytes (unsigned char). - // data will not be avaliable after this callback ends so the handler must - // either completely process the message or copy it somewhere else for - // processing later. - virtual void on_message(session_ptr session, - const std::vector &data) = 0; - - // on_message (text version). Identical to on_message except the data - // parameter is a string interpreted as UTF-8. WebSocket++ guarantees that - // this string is valid UTF-8. - virtual void on_message(session_ptr session,const std::string &msg) = 0; - - - - // #### optional error cases #### - - // on_fail is called whenever a session is terminated or failed before it - // was successfully established. This happens if there is an error during - // the handshake process or if the server refused the connection. - // - // on_fail will be the last time a session calls its handler. If your - // application will need information from `session` after this function you - // should either save the session_ptr somewhere or copy the data out. - virtual void on_fail(session_ptr session) {}; - - // experimental - virtual void on_ping_timeout(session_ptr session) {} -}; - - - -} -#endif // WEBSOCKET_CONNECTION_HANDLER_HPP diff --git a/src/legacy/websocket_endpoint.hpp b/src/legacy/websocket_endpoint.hpp deleted file mode 100644 index 4f80bf992f..0000000000 --- a/src/legacy/websocket_endpoint.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2011, 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 WEBSOCKET_ENDPOINT_HPP -#define WEBSOCKET_ENDPOINT_HPP - -#include - -#include -#include - -namespace websocketpp { - class endpoint; - typedef boost::shared_ptr endpoint_ptr; -} - -#include "websocket_session.hpp" - -namespace websocketpp { - -class endpoint : public boost::enable_shared_from_this { -public: - virtual bool is_server() = 0; - // log - // access_log -}; - - - -} -#endif // WEBSOCKET_ENDPOINT_HPP diff --git a/src/legacy/websocket_frame.cpp b/src/legacy/websocket_frame.cpp deleted file mode 100644 index d1001affb6..0000000000 --- a/src/legacy/websocket_frame.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2011, 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. - * - */ - diff --git a/src/legacy/websocket_server.cpp b/src/legacy/websocket_server.cpp deleted file mode 100644 index d1001affb6..0000000000 --- a/src/legacy/websocket_server.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2011, 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. - * - */ - diff --git a/src/legacy/websocket_server.hpp b/src/legacy/websocket_server.hpp deleted file mode 100644 index b55f81c7ab..0000000000 --- a/src/legacy/websocket_server.hpp +++ /dev/null @@ -1,1176 +0,0 @@ -/* - * Copyright (c) 2011, 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 WEBSOCKET_SERVER_HPP -#define WEBSOCKET_SERVER_HPP - -#include -#include -#include -#include -#include -#include - -//#include // for the ssl policy only - -namespace po = boost::program_options; - -#include -#include - -#include "websocketpp.hpp" - -#include "interfaces/session.hpp" - -// Session processors -#include "interfaces/protocol.hpp" -#include "hybi_legacy_processor.hpp" -#include "hybi_processor.hpp" - -#include "websocket_connection_handler.hpp" - -#include "rng/blank_rng.hpp" -#include "http/parser.hpp" -#include "logger/logger.hpp" - -using boost::asio::ip::tcp; -using websocketpp::session::server_handler_ptr; -using websocketpp::protocol::processor_ptr; - -namespace websocketpp { -namespace server { - -namespace write_state { - enum value { - IDLE = 0, - WRITING = 1, - INTURRUPT = 2 - }; -} - -template - < - typename server_policy, - template class security_policy - > -class connection - : - public security_policy< connection >, - public boost::enable_shared_from_this< connection > { -public: - typedef server_policy server_type; - typedef connection connection_type; - typedef security_policy< connection > security_policy_type; - - typedef boost::shared_ptr ptr; - typedef boost::shared_ptr server_ptr; - - connection(server_ptr s, - boost::asio::io_service& io_service, - server_handler_ptr handler) - : security_policy_type(io_service), - m_server(s), - m_io_service(io_service), - //m_socket(io_service), - m_timer(io_service,boost::posix_time::seconds(0)), - m_buf(/* TODO: needs a max here */), - m_handler(handler), - m_state(session::state::CONNECTING), - m_write_buffer(0), - m_write_state(write_state::IDLE) {} - - // implimentation of the server session API - - // Valid always - session::state::value get_state() const { - // TODO: syncronize - return m_state; - } - - unsigned int get_version() const { - return m_version; - } - - std::string get_origin() const { - return m_origin; - } - - std::string get_request_header(const std::string& key) const { - return m_request.header(key); - } - - bool get_secure() const { - // TODO - return false; - } - - std::string get_host() const { - return m_uri.host; - } - - uint16_t get_port() const { - return m_uri.port; - } - - std::string get_resource() const { - return m_uri.resource; - } - - tcp::endpoint get_endpoint() const { - return security_policy_type::socket().remote_endpoint(); - } - - // Valid for CONNECTING state - void add_response_header(const std::string& key, const std::string& value) { - m_response.add_header(key,value); - } - void replace_response_header(const std::string& key, const std::string& value) { - m_response.replace_header(key,value); - } - const std::vector& get_subprotocols() const { - return m_requested_subprotocols; - } - const std::vector& get_extensions() const { - return m_requested_extensions; - } - void select_subprotocol(const std::string& value) { - std::vector::iterator it; - - it = std::find(m_requested_subprotocols.begin(), - m_requested_subprotocols.end(), - value); - - if (value != "" && it == m_requested_subprotocols.end()) { - throw server_error("Attempted to choose a subprotocol not proposed by the client"); - } - - m_subprotocol = value; - } - void select_extension(const std::string& value) { - if (value == "") { - return; - } - - std::vector::iterator it; - - it = std::find(m_requested_extensions.begin(), - m_requested_extensions.end(), - value); - - if (it == m_requested_extensions.end()) { - throw server_error("Attempted to choose an extension not proposed by the client"); - } - - m_extensions.push_back(value); - } - - // Valid for OPEN state - - // These functions invoke write_message through the io_service to gain - // thread safety - void send(const utf8_string& payload) { - binary_string_ptr msg(m_processor->prepare_frame(frame::opcode::TEXT,false,payload)); - - m_io_service.post(boost::bind(&connection_type::write_message,connection_type::shared_from_this(),msg)); - - // TODO: return bytes in flight somehow? - } - - void send(const binary_string& data) { - binary_string_ptr msg(m_processor->prepare_frame(frame::opcode::BINARY,false,data)); - m_io_service.post(boost::bind(&connection_type::write_message,connection_type::shared_from_this(),msg)); - } - - void close(close::status::value code, const utf8_string& reason) { - // TODO - } - - void ping(const binary_string& payload) { - binary_string_ptr msg(m_processor->prepare_frame(frame::opcode::PING,false,payload)); - - m_io_service.post(boost::bind(&connection_type::write_message,connection_type::shared_from_this(),msg)); - } - - void pong(const binary_string& payload) { - binary_string_ptr msg(m_processor->prepare_frame(frame::opcode::PONG,false,payload)); - m_io_service.post(boost::bind(&connection_type::write_message,connection_type::shared_from_this(),msg)); - } - - uint64_t buffered_amount() const { - // TODO: syncronize this member function - return m_write_buffer; - } - - // Valid for CLOSED state - close::status::value get_local_close_code() const { - return m_local_close_code; - } - utf8_string get_local_close_reason() const { - return m_local_close_reason; - } - close::status::value get_remote_close_code() const { - return m_remote_close_code; - } - utf8_string get_remote_close_reason() const { - return m_remote_close_reason; - } - bool get_failed_by_me() const { - return m_failed_by_me; - } - bool get_dropped_by_me() const { - return m_dropped_by_me; - } - bool get_closed_by_me() const { - return m_closed_by_me; - } - - //////// - - // now provided by a policy class - /*tcp::socket& get_socket() { - return m_socket; - }*/ - - void read_request() { - // start reading HTTP header and attempt to determine if the incoming - // connection is a websocket connection. If it is determine the version - // and generate a session processor for that version. If it is not a - // websocket connection either drop or pass to the default HTTP pass - // through handler. - - m_timer.expires_from_now(boost::posix_time::seconds(5 /* TODO */)); - - m_timer.async_wait( - boost::bind( - &connection_type::fail_on_expire, - connection_type::shared_from_this(), - boost::asio::placeholders::error - ) - ); - - boost::asio::async_read_until( - security_policy_type::socket(), - m_buf, - "\r\n\r\n", - boost::bind( - &connection_type::handle_read_request, - connection_type::shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred - ) - ); - } - - void handle_read_request(const boost::system::error_code& e, - std::size_t bytes_transferred) { - if (e) { - log_error("Error reading HTTP request",e); - terminate_connection(false); - return; - } - - try { - std::istream request(&m_buf); - - // TODO: use a more generic consume api where we just call read_some - // and have the handshake consume and validate as we go. - // - // For now, because it simplifies things we will use the parse_header - // member function which requires the complete header to be passed in - // initially. ASIO can guarantee us this. - // - // - //m_remote_handshake.consume(response_stream); - if (!m_request.parse_complete(request)) { - // not a valid HTTP request/response - throw http::exception("Recieved invalid HTTP Request",http::status_code::BAD_REQUEST); - } - - // Log the raw handshake. - m_server->alog().at(log::alevel::DEBUG_HANDSHAKE) << m_request.raw() << log::endl; - - // Determine what sort of connection this is: - m_version = -1; - - std::string h = m_request.header("Upgrade"); - if (boost::ifind_first(h,"websocket")) { - h = m_request.header("Sec-WebSocket-Version"); - if (h == "") { - m_version = 0; - } else { - m_version = atoi(h.c_str()); - if (m_version == 0) { - throw(http::exception("Unable to determine connection version",http::status_code::BAD_REQUEST)); - } - } - } - - m_server->alog().at(log::alevel::DEBUG_HANDSHAKE) << "determined connection version: " << m_version << log::endl; - - if (m_version == -1) { - // Probably a plain HTTP request - // TODO: forward to an http handler? - - } else { - // websocket connection - // create a processor based on version. - if (m_version == 0) { - // create hybi 00 processor - - // grab hybi00 token first - char foo[9]; - foo[8] = 0; - - request.get(foo,9); - - if (request.gcount() != 8) { - throw http::exception("Missing Key3",http::status_code::BAD_REQUEST); - } - m_request.add_header("Sec-WebSocket-Key3",std::string(foo)); - - m_processor = processor_ptr(new protocol::hybi_legacy_processor(false)); - } else if (m_version == 7 || m_version == 8 || m_version == 13) { - // create hybi 17 processor - m_processor = processor_ptr(new protocol::hybi_processor(false,m_rng)); - } else { - // TODO: respond with unknown version message per spec - } - - // ask new protocol whether this set of headers is valid - m_processor->validate_handshake(m_request); - m_origin = m_processor->get_origin(m_request); - m_uri = m_processor->get_uri(m_request); - - // ask local application to confirm that it wants to accept - m_handler->validate(boost::static_pointer_cast(connection_type::shared_from_this())); - - m_response.set_status(http::status_code::SWITCHING_PROTOCOLS); - } - - } catch (const http::exception& e) { - m_server->alog().at(log::alevel::DEBUG_HANDSHAKE) << e.what() << log::endl; - - m_server->elog().at(log::elevel::ERROR) - << "Caught handshake exception: " << e.what() << log::endl; - - m_response.set_status(e.m_error_code,e.m_error_msg); - } - - write_response(); - } - - // write the response to the client's request. - void write_response() { - std::string response; - - m_response.set_version("HTTP/1.1"); - - if (m_response.status_code() == http::status_code::SWITCHING_PROTOCOLS) { - // websocket response - m_processor->handshake_response(m_request,m_response); - - if (m_subprotocol != "") { - m_response.replace_header("Sec-WebSocket-Protocol",m_subprotocol); - } - - // TODO: return negotiated extensions - } else { - // HTTP response - } - - m_response.replace_header("Server","WebSocket++/2011-10-31"); - - std::string raw = m_response.raw(); - - // Hack for legacy HyBi - if (m_version == 0) { - raw += boost::dynamic_pointer_cast(m_processor)->get_key3(); - } - - m_server->alog().at(log::alevel::DEBUG_HANDSHAKE) << raw << log::endl; - - // start async write to handle_write_handshake - boost::asio::async_write( - security_policy_type::socket(), - boost::asio::buffer(raw), - boost::bind( - &connection_type::handle_write_response, - connection_type::shared_from_this(), - boost::asio::placeholders::error - ) - ); - } - - void handle_write_response(const boost::system::error_code& error) { - // stop the handshake timer - m_timer.cancel(); - - if (error) { - log_error("Network error writing handshake response",error); - terminate_connection(false); - m_handler->on_fail(boost::static_pointer_cast(connection_type::shared_from_this())); - return; - } - - log_open_result(); - - // log error if this was - if (m_version != -1 && m_response.status_code() != http::status_code::SWITCHING_PROTOCOLS) { - m_server->elog().at(log::elevel::ERROR) - << "Handshake ended with HTTP error: " - << m_response.status_code() << " " << m_response.status_msg() - << log::endl; - - terminate_connection(true); - m_handler->on_fail(boost::static_pointer_cast(connection_type::shared_from_this())); - return; - } - - m_state = session::state::OPEN; - - m_handler->on_open(boost::static_pointer_cast(connection_type::shared_from_this())); - - // TODO: start read message loop. - m_server->alog().at(log::alevel::DEVEL) << "calling handle_read_frame" << log::endl; - handle_read_frame(boost::system::error_code()); - } - - void handle_read_frame (const boost::system::error_code& error) { - // check if state changed while we were waiting for a read. - if (m_state == session::state::CLOSED) { return; } - - if (error) { - if (error == boost::asio::error::eof) { - // got unexpected EOF - // TODO: log error - terminate_connection(false); - } else if (error == boost::asio::error::operation_aborted) { - // got unexpected abort (likely our server issued an abort on - // all connections on this io_service) - - // TODO: log error - terminate_connection(true); - } else { - // Other unexpected error - - // TODO: log error - terminate_connection(false); - } - } - - // process data from the buffer just read into - std::istream s(&m_buf); - - m_server->alog().at(log::alevel::DEVEL) << "starting while, buffer size: " << m_buf.size() << log::endl; - - while (m_state != session::state::CLOSED && m_buf.size() > 0) { - try { - m_server->alog().at(log::alevel::DEVEL) << "starting consume, buffer size: " << m_buf.size() << log::endl; - m_processor->consume(s); - m_server->alog().at(log::alevel::DEVEL) << "done consume, buffer size: " << m_buf.size() << log::endl; - - if (m_processor->ready()) { - m_server->alog().at(log::alevel::DEVEL) << "new message ready" << m_buf.size() << log::endl; - - bool response; - switch (m_processor->get_opcode()) { - case frame::opcode::TEXT: - m_handler->on_message(boost::static_pointer_cast(connection_type::shared_from_this()),m_processor->get_utf8_payload()); - break; - case frame::opcode::BINARY: - m_handler->on_message(boost::static_pointer_cast(connection_type::shared_from_this()),m_processor->get_binary_payload()); - break; - case frame::opcode::PING: - response = m_handler->on_ping(boost::static_pointer_cast(connection_type::shared_from_this()),m_processor->get_binary_payload()); - - if (response) { - // send response ping - write_message(m_processor->prepare_frame(frame::opcode::PONG,false,*m_processor->get_binary_payload())); - } - break; - case frame::opcode::PONG: - m_handler->on_pong(boost::static_pointer_cast(connection_type::shared_from_this()),m_processor->get_binary_payload()); - - // TODO: disable ping response timer - - break; - case frame::opcode::CLOSE: - m_remote_close_code = m_processor->get_close_code(); - m_remote_close_reason = m_processor->get_close_reason(); - - // check that the codes we got over the wire are - // valid - - if (close::status::invalid(m_remote_close_code)) { - throw session::exception("Invalid close code",session::error::PROTOCOL_VIOLATION); - } - - if (close::status::reserved(m_remote_close_code)) { - throw session::exception("Reserved close code",session::error::PROTOCOL_VIOLATION); - } - - if (m_state == session::state::OPEN) { - // other end is initiating - m_server->elog().at(log::elevel::DEVEL) - << "sending close ack" << log::endl; - - send_close_ack(); - } else if (m_state == session::state::CLOSING) { - // ack of our close - m_server->elog().at(log::elevel::DEVEL) - << "got close ack" << log::endl; - - terminate_connection(false); - // TODO: start terminate timer (if client) - } - break; - default: - throw session::exception("Invalid Opcode",session::error::PROTOCOL_VIOLATION); - break; - } - m_processor->reset(); - } - } catch (const session::exception& e) { - m_server->elog().at(log::elevel::ERROR) - << "Caught session exception: " << e.what() << log::endl; - - // if the exception happened while processing. - // TODO: this is not elegant, perhaps separate frame read vs process - // exceptions need to be used. - if (m_processor->ready()) { - m_processor->reset(); - } - - if (e.code() == session::error::PROTOCOL_VIOLATION) { - send_close(close::status::PROTOCOL_ERROR, e.what()); - } else if (e.code() == session::error::PAYLOAD_VIOLATION) { - send_close(close::status::INVALID_PAYLOAD, e.what()); - } else if (e.code() == session::error::INTERNAL_SERVER_ERROR) { - send_close(close::status::POLICY_VIOLATION, e.what()); - } else if (e.code() == session::error::SOFT_ERROR) { - // ignore and continue processing frames - continue; - } else { - // Fatal error, forcibly end connection immediately. - m_server->elog().at(log::elevel::DEVEL) - << "Dropping TCP due to unrecoverable exception" - << log::endl; - terminate_connection(true); - } - break; - } - } - - // try and read more - if (m_state != session::state::CLOSED && - m_processor->get_bytes_needed() > 0) { - // TODO: read timeout timer? - - boost::asio::async_read( - security_policy_type::socket(), - m_buf, - boost::asio::transfer_at_least(m_processor->get_bytes_needed()), - boost::bind( - &connection_type::handle_read_frame, - connection_type::shared_from_this(), - boost::asio::placeholders::error - ) - ); - } - } - - void send_close(close::status::value code, const std::string& reason) { - if (m_state != session::state::OPEN) { - m_server->elog().at(log::elevel::WARN) - << "Tried to disconnect a session that wasn't open" << log::endl; - return; - } - - if (close::status::invalid(code)) { - m_server->elog().at(log::elevel::WARN) - << "Tried to close a connection with invalid close code: " << code << log::endl; - return; - } else if (close::status::reserved(code)) { - m_server->elog().at(log::elevel::WARN) - << "Tried to close a connection with reserved close code: " << code << log::endl; - return; - } - - m_state = session::state::CLOSING; - - m_closed_by_me = true; - - m_timer.expires_from_now(boost::posix_time::milliseconds(1000)); - m_timer.async_wait( - boost::bind( - &connection_type::fail_on_expire, - connection_type::shared_from_this(), - boost::asio::placeholders::error - ) - ); - - m_local_close_code = code; - m_local_close_reason = reason; - - - write_message(m_processor->prepare_close_frame(m_local_close_code, - false, - m_local_close_reason)); - m_write_state = write_state::INTURRUPT; - } - - // send an acknowledgement close frame - void send_close_ack() { - // TODO: state should be OPEN - - // echo close value unless there is a good reason not to. - if (m_remote_close_code == close::status::NO_STATUS) { - m_local_close_code = close::status::NORMAL; - m_local_close_reason = ""; - } else if (m_remote_close_code == close::status::ABNORMAL_CLOSE) { - // TODO: can we possibly get here? This means send_close_ack was - // called after a connection ended without getting a close - // frame - throw "shouldn't be here"; - } else if (close::status::invalid(m_remote_close_code)) { - m_local_close_code = close::status::PROTOCOL_ERROR; - m_local_close_reason = "Status code is invalid"; - } else if (close::status::reserved(m_remote_close_code)) { - m_local_close_code = close::status::PROTOCOL_ERROR; - m_local_close_reason = "Status code is reserved"; - } else { - m_local_close_code = m_remote_close_code; - m_local_close_reason = m_remote_close_reason; - } - - // TODO: check whether we should cancel the current in flight write. - // if not canceled the close message will be sent as soon as the - // current write completes. - - - write_message(m_processor->prepare_close_frame(m_local_close_code, - false, - m_local_close_reason)); - m_write_state = write_state::INTURRUPT; - } - - void write_message(binary_string_ptr msg) { - m_write_buffer += msg->size(); - m_write_queue.push(msg); - write(); - } - - void write() { - switch (m_write_state) { - case write_state::IDLE: - break; - case write_state::WRITING: - // already writing. write() will get called again by the write - // handler once it is ready. - return; - case write_state::INTURRUPT: - // clear the queue except for the last message - while (m_write_queue.size() > 1) { - m_write_buffer -= m_write_queue.front()->size(); - m_write_queue.pop(); - } - break; - default: - // TODO: assert shouldn't be here - break; - } - - if (m_write_queue.size() > 0) { - if (m_write_state == write_state::IDLE) { - m_write_state = write_state::WRITING; - } - - boost::asio::async_write( - security_policy_type::socket(), - boost::asio::buffer(*m_write_queue.front()), - boost::bind( - &connection_type::handle_write, - connection_type::shared_from_this(), - boost::asio::placeholders::error - ) - ); - } else { - // if we are in an inturrupted state and had nothing else to write - // it is safe to terminate the connection. - if (m_write_state == write_state::INTURRUPT) { - terminate_connection(false); - } - } - } - - void handle_write(const boost::system::error_code& error) { - if (error) { - if (error == boost::asio::error::operation_aborted) { - // previous write was aborted - std::cout << "aborted" << std::endl; - } else { - log_error("Error writing frame data",error); - terminate_connection(false); - return; - } - } - - if (m_write_queue.size() == 0) { - std::cout << "handle_write called with empty queue" << std::endl; - return; - } - - m_write_buffer -= m_write_queue.front()->size(); - m_write_queue.pop(); - - if (m_write_state == write_state::WRITING) { - m_write_state = write_state::IDLE; - } - - write(); - } - - // end conditions - // - tcp connection is closed - // - session state is CLOSED - // - session end flags are set - // - application is notified - void terminate_connection(bool failed_by_me) { - m_server->alog().at(log::alevel::DEBUG_CLOSE) << "terminate called" << log::endl; - - if (m_state == session::state::CLOSED) { - // shouldn't be here - } - - // cancel the close timeout - m_timer.cancel(); - - try { - if (security_policy_type::socket().is_open()) { - security_policy_type::socket().shutdown(tcp::socket::shutdown_both); - security_policy_type::socket().close(); - m_dropped_by_me = true; - } - } catch (boost::system::system_error& e) { - if (e.code() == boost::asio::error::not_connected) { - // this means the socket was disconnected by the other side before - // we had a chance to. Ignore and continue. - } else { - throw e; - } - } - - m_failed_by_me = failed_by_me; - - session::state::value old_state = m_state; - m_state = session::state::CLOSED; - - // If we called terminate from the connecting state call on_fail - if (old_state == session::state::CONNECTING) { - m_handler->on_fail(boost::static_pointer_cast(connection_type::shared_from_this())); - } else if (old_state == session::state::OPEN || - old_state == session::state::CLOSING) { - m_handler->on_close(boost::static_pointer_cast(connection_type::shared_from_this())); - } else { - // if we were already closed something is wrong - } - } - - // this is called when an async asio call encounters an error - void log_error(std::string msg,const boost::system::error_code& e) { - m_server->elog().at(log::elevel::ERROR) - << msg << "(" << e << ")" << log::endl; - } - void log_close_result() { - m_server->alog().at(log::alevel::DISCONNECT) - //<< "Disconnect " << (m_was_clean ? "Clean" : "Unclean") - << "Disconnect " - << " close local:[" << m_local_close_code - << (m_local_close_reason == "" ? "" : ","+m_local_close_reason) - << "] remote:[" << m_remote_close_code - << (m_remote_close_reason == "" ? "" : ","+m_remote_close_reason) << "]" - << log::endl; - } - void log_open_result() { - m_server->alog().at(log::alevel::CONNECT) << "Connection " - << security_policy_type::socket().remote_endpoint() << " v" << m_version << " " - << (get_request_header("User-Agent") == "" ? "NULL" : get_request_header("User-Agent")) - << " " << m_uri.resource << " " << m_response.status_code() - << log::endl; - } - - void fail_on_expire(const boost::system::error_code& error) { - if (error) { - if (error != boost::asio::error::operation_aborted) { - m_server->elog().at(log::elevel::DEVEL) - << "fail_on_expire timer ended in unknown error" << log::endl; - terminate_connection(false); - } - return; - } - m_server->elog().at(log::elevel::DEVEL) - << "fail_on_expire timer expired" << log::endl; - terminate_connection(true); - } - -private: - server_ptr m_server; - - boost::asio::io_service& m_io_service; - //tcp::socket m_socket; - boost::asio::deadline_timer m_timer; - boost::asio::streambuf m_buf; - - server_handler_ptr m_handler; - processor_ptr m_processor; - blank_rng m_rng; - - // Connection state - http::parser::request m_request; - http::parser::response m_response; - - std::vector m_requested_subprotocols; - std::vector m_requested_extensions; - std::string m_subprotocol; - std::vector m_extensions; - std::string m_origin; - unsigned int m_version; - bool m_secure; - ws_uri m_uri; - - session::state::value m_state; - - // Write queue - std::queue m_write_queue; - uint64_t m_write_buffer; - write_state::value m_write_state; - - // Close state - close::status::value m_local_close_code; - std::string m_local_close_reason; - close::status::value m_remote_close_code; - std::string m_remote_close_reason; - bool m_closed_by_me; - bool m_failed_by_me; - bool m_dropped_by_me; -}; - -class server_connection_policy { - -}; - -class server_role { - typedef websocketpp::session::server connection_interface; - typedef websocketpp::server::server_connection_policy connection_policy; -}; - -// ******* SSL SECURITY POLICY ******* -//typedef boost::asio::ssl::stream ssl_socket; - -/*class connection_ssl { -public: - ssl_socket::lowest_layer_type& socket() { - return m_socket.lowest_layer(); - } - - void handshake() { - m_socket.async_handshake( - boost::asio::ssl::stream_base::server, - boost::bind( - &connection_ssl::handle_handshake, - shared_from_this(), - boost::asio::placeholders::error - ) - ); - } - - void handle_handshake() { - read_request(); - } - -private: - ssl_socket m_socket; -}; - -class endpoint_ssl { -public: - typedef websocketpp::server::connection_ssl connection_policy; - -private: - boost::asio::ssl::context m_context; -};*/ -// ******* END SSL SECURITY POLICY ******* - -// ******* PLAIN SECURITY POLICY ******* -template -class connection_plain { -public: - connection_plain(boost::asio::io_service& io_service) : m_socket(io_service) {} - - boost::asio::ip::tcp::socket& socket() { - return m_socket; - } - - void handshake() { - connection_type::read_request(); - } -private: - boost::asio::ip::tcp::socket m_socket; -}; - -template -class endpoint_plain { -protected: - // base security class for connections that plain endpoints create - template - class connection_security_policy { - public: - connection_security_policy(boost::asio::io_service& io_service) : m_socket(io_service) {} - - typedef connection_security_policy foo; - - boost::asio::ip::tcp::socket& socket() { - return m_socket; - } - - void handshake(boost::shared_ptr foo) { - //connection_type::read_request(); - //static_cast(this)->read_request(); - foo->read_request(); - - //boost::static_pointer_cast(foo::shared_from_this()) - - //m_handler->on_close(boost::static_pointer_cast(connection_type::shared_from_this())); - } - private: - boost::asio::ip::tcp::socket m_socket; - }; - -}; -// ******* END PLAIN SECURITY POLICY ******* - -// TODO: potential policies: -// - http parser -template - < - template class security_policy, - template class logger_type = log::logger - > -class endpoint - : - public security_policy< endpoint >, - public boost::enable_shared_from_this< endpoint > { -public: - typedef endpoint endpoint_type; - typedef security_policy< endpoint > security_type; - - typedef logger_type alog_type; - typedef logger_type elog_type; - - using security_type::connection_security_policy; - - typedef connection connection_type; - - typedef boost::shared_ptr ptr; - //typedef websocketpp::session::server_ptr session_ptr; - typedef boost::shared_ptr connection_ptr; - - endpoint(uint16_t port, server_handler_ptr handler) - : m_endpoint(tcp::v6(),port), - m_acceptor(m_io_service,m_endpoint), - m_handler(handler), - m_max_message_size(DEFAULT_MAX_MESSAGE_SIZE), - m_desc("websocketpp::server") - { - m_desc.add_options() - ("help", "produce help message") - ("host,h",po::value >()->multitoken()->composing(), "hostnames to listen on") - ("port,p",po::value(), "port to listen on") - ; - } - - void run() { - start_accept(); - m_io_service.run(); - } - - - - // INTERFACE FOR LOCAL APPLICATIONS - void set_max_message_size(uint64_t val) { - if (val > frame::limits::PAYLOAD_SIZE_JUMBO) { - // TODO: Figure out what the ideal error behavior for this method. - // Options: - // Throw exception - // Log error and set value to maximum allowed - // Log error and leave value at whatever it was before - elog().at(log::elevel::WARN) << "Invalid maximum message size: " - << val << log::endl; - //throw server_error(err.str()); - } - m_max_message_size = val; - } - - void parse_command_line(int ac, char* av[]) { - po::store(po::parse_command_line(ac,av, m_desc),m_vm); - po::notify(m_vm); - - if (m_vm.count("help") ) { - std::cout << m_desc << std::endl; - } - - //m_vm["host"].as(); - - // TODO: template "as" weirdness - // const std::vector< std::string > &foo = m_vm["host"].as< std::vector >(); - - const std::vector< std::string > &foo = m_vm["host"].template as< std::vector >(); - - for (int i = 0; i < foo.size(); i++) { - std::cout << foo[i] << std::endl; - } - - //std::cout << m_vm["host"].as< std::vector >() << std::endl; - } - - // INTERFACE FOR SESSIONS - - static const bool is_server = true; - - /*rng_policy& get_rng() { - return m_rng; - }*/ - - // Confirms that the port in the host string matches the port we are listening - // on. End user application is responsible for checking the /host/ part. - bool validate_host(std::string host) { - // find colon. - // if no colon assume default port - - // if port == port - // return true - // else - // return false - - // TODO: just check the port. Otherwise user is responsible for checking this - - return true; - } - - // Check if message size is within server's acceptable parameters - bool validate_message_size(uint64_t val) { - if (val > m_max_message_size) { - return false; - } - return true; - } - - logger_type& alog() { - return m_alog; - } - - logger_type& elog() { - return m_elog; - } - -private: - // creates a new session object and connects the next websocket - // connection to it. - void start_accept() { - // TODO: sanity check whether the session buffer size bound could be reduced - connection_ptr new_session( - new connection_type( - endpoint_type::shared_from_this(), - m_io_service, - m_handler - ) - ); - - m_acceptor.async_accept( - new_session->socket(), - boost::bind( - &endpoint_type::handle_accept, - endpoint_type::shared_from_this(), - new_session, - boost::asio::placeholders::error - ) - ); - } - - // if no errors starts the session's read loop and returns to the - // start_accept phase. - void handle_accept(connection_ptr connection,const boost::system::error_code& error) - { - if (!error) { - connection->handshake(connection); - - // TODO: add session to local session vector - } else { - std::stringstream err; - err << "Error accepting socket connection: " << error; - - elog().at(log::elevel::ERROR) << err.str() << log::endl; - throw server_error(err.str()); - } - - this->start_accept(); - } - -private: - boost::asio::io_service m_io_service; - tcp::endpoint m_endpoint; - tcp::acceptor m_acceptor; - - server_handler_ptr m_handler; - - logger_type m_alog; - logger_type m_elog; - - std::vector m_connections; - - uint64_t m_max_message_size; - - po::options_description m_desc; - po::variables_map m_vm; -}; - -} - -// convenience type interface - -// websocketpp::server_ptr represents a basic non-secure websocket server -typedef server::endpoint basic_server; -typedef basic_server::ptr basic_server_ptr; - -// websocketpp::secure_server_ptr represents a basic secure websocket server -// TODO: -//typedef server::server<> secure_server; -//typedef secure_server::ptr secure_server_ptr; - -} - -#endif // WEBSOCKET_SERVER_HPP diff --git a/src/legacy/websocket_server_session.cpp b/src/legacy/websocket_server_session.cpp deleted file mode 100644 index d8f0aa6c38..0000000000 --- a/src/legacy/websocket_server_session.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright (c) 2011, 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. - * - */ - -#include "websocketpp.hpp" -#include "websocket_server_session.hpp" - -#include "websocket_frame.hpp" -#include "utf8_validator/utf8_validator.hpp" - -#include -#include -#include - - -#include -#include -#include -#include - -using websocketpp::server_session; - -server_session::server_session(websocketpp::server_ptr s, - boost::asio::io_service& io_service, - websocketpp::connection_handler_ptr defc, - uint64_t buf_size) - : session(io_service,defc,buf_size),m_server(s) {} - -void server_session::on_connect() { - read_handshake(); -} - - -void server_session::set_header(const std::string &key,const std::string &val) { - // TODO: prevent use of reserved headers; - m_server_headers[key] = val; -} - -void server_session::select_subprotocol(const std::string& val) { - std::vector::iterator it; - - it = std::find(m_client_subprotocols.begin(), - m_client_subprotocols.end(), - val); - - if (val != "" && it == m_client_subprotocols.end()) { - throw server_error("Attempted to choose a subprotocol not proposed by the client"); - } - - m_server_subprotocol = val; -} - -void server_session::select_extension(const std::string& val) { - if (val == "") { - return; - } - - std::vector::iterator it; - - it = std::find(m_client_extensions.begin(), - m_client_extensions.end(), - val); - - if (it == m_client_extensions.end()) { - throw server_error("Attempted to choose an extension not proposed by the client"); - } - - m_server_extensions.push_back(val); -} - -void server_session::read_handshake() { - m_timer.expires_from_now(boost::posix_time::seconds(5)); - - m_timer.async_wait( - boost::bind( - &session::handle_handshake_expired, - shared_from_this(), - boost::asio::placeholders::error - ) - ); - - boost::asio::async_read_until( - m_socket, - m_buf, - "\r\n\r\n", - boost::bind( - &session::handle_read_handshake, - shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred - ) - ); -} - -void server_session::handle_read_handshake(const boost::system::error_code& e, - std::size_t bytes_transferred) { - std::ostringstream line; - line << &m_buf; - m_raw_client_handshake += line.str(); - - access_log(m_raw_client_handshake,ALOG_HANDSHAKE); - - std::vector tokens; - std::string::size_type start = 0; - std::string::size_type end; - - // Get request and parse headers - end = m_raw_client_handshake.find("\r\n",start); - - while(end != std::string::npos) { - tokens.push_back(m_raw_client_handshake.substr(start, end - start)); - - start = end + 2; - - end = m_raw_client_handshake.find("\r\n",start); - } - - for (size_t i = 0; i < tokens.size(); i++) { - if (i == 0) { - m_client_http_request = tokens[i]; - } - - end = tokens[i].find(": ",0); - - if (end != std::string::npos) { - std::string h = tokens[i].substr(0,end); - - if (get_client_header(h) == "") { - m_client_headers[h] = tokens[i].substr(end+2); - } else { - m_client_headers[h] += ", " + tokens[i].substr(end+2); - } - } - } - - // handshake error checking - try { - std::stringstream err; - std::string h; - - // check the method - if (m_client_http_request.substr(0,4) != "GET ") { - err << "Websocket handshake has invalid method: " - << m_client_http_request.substr(0,4); - - throw(handshake_error(err.str(),400)); - } - - // check the HTTP version - // TODO: allow versions greater than 1.1 - end = m_client_http_request.find(" HTTP/1.1",4); - if (end == std::string::npos) { - err << "Websocket handshake has invalid HTTP version"; - throw(handshake_error(err.str(),400)); - } - - m_resource = m_client_http_request.substr(4,end-4); - - // verify the presence of required headers - h = get_client_header("Host"); - if (h == "") { - throw(handshake_error("Required Host header is missing",400)); - } else if (!m_server->validate_host(h)) { - err << "Host " << h << " is not one of this server's names."; - throw(handshake_error(err.str(),400)); - } - - h = get_client_header("Upgrade"); - if (h == "") { - throw(handshake_error("Required Upgrade header is missing",400)); - } else if (!boost::iequals(h,"websocket")) { - err << "Upgrade header was " << h << " instead of \"websocket\""; - throw(handshake_error(err.str(),400)); - } - - h = get_client_header("Connection"); - if (h == "") { - throw(handshake_error("Required Connection header is missing",400)); - } else if (!boost::ifind_first(h,"upgrade")) { - err << "Connection header, \"" << h - << "\", does not contain required token \"upgrade\""; - throw(handshake_error(err.str(),400)); - } - - if (get_client_header("Sec-WebSocket-Key") == "") { - throw(handshake_error("Required Sec-WebSocket-Key header is missing",400)); - } - - h = get_client_header("Sec-WebSocket-Version"); - if (h == "") { - throw(handshake_error("Required Sec-WebSocket-Version header is missing",400)); - } else { - m_version = atoi(h.c_str()); - - if (m_version != 7 && m_version != 8 && m_version != 13) { - err << "This server doesn't support WebSocket protocol version " - << m_version; - throw(handshake_error(err.str(),400)); - } - } - - if (m_version < 13) { - h = get_client_header("Sec-WebSocket-Origin"); - } else { - h = get_client_header("Origin"); - } - - if (h != "") { - m_client_origin = h; - } - - // TODO: extract subprotocols - // TODO: extract extensions - - // optional headers (delegated to the local interface) - if (m_local_interface) { - m_local_interface->validate(shared_from_this()); - } - - m_server_http_code = 101; - m_server_http_string = "Switching Protocols"; - } catch (const handshake_error& e) { - std::stringstream err; - err << "Caught handshake exception: " << e.what(); - - access_log(e.what(),ALOG_HANDSHAKE); - log(err.str(),LOG_ERROR); - - m_server_http_code = e.m_http_error_code; - m_server_http_string = e.m_http_error_msg; - } - - write_handshake(); -} - -void server_session::write_handshake() { - std::stringstream h; - - - - if (m_server_http_code == 101) { - std::string server_key = get_client_header("Sec-WebSocket-Key"); - server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - SHA1 sha; - uint32_t message_digest[5]; - - sha.Reset(); - sha << server_key.c_str(); - - if (sha.Result(message_digest)){ - // convert sha1 hash bytes to network byte order because this sha1 - // library works on ints rather than bytes - for (int i = 0; i < 5; i++) { - message_digest[i] = htonl(message_digest[i]); - } - - server_key = base64_encode( - reinterpret_cast(message_digest),20); - - // set handshake accept headers - set_header("Sec-WebSocket-Accept",server_key); - set_header("Upgrade","websocket"); - set_header("Connection","Upgrade"); - } else { - log("Error computing handshake sha1 hash.",LOG_ERROR); - m_server_http_code = 500; - m_server_http_string = ""; - } - } - - // hardcoded server headers - set_header("Server","WebSocket++/2011-09-25"); - - h << "HTTP/1.1 " << m_server_http_code << " " - << (m_server_http_string != "" ? m_server_http_string : - lookup_http_error_string(m_server_http_code)) - << "\r\n"; - - header_list::iterator it; - for (it = m_server_headers.begin(); it != m_server_headers.end(); it++) { - h << it->first << ": " << it->second << "\r\n"; - } - - h << "\r\n"; - - m_raw_server_handshake = h.str(); - - // start async write to handle_write_handshake - boost::asio::async_write( - m_socket, - boost::asio::buffer(m_raw_server_handshake), - boost::bind( - &session::handle_write_handshake, - shared_from_this(), - boost::asio::placeholders::error - ) - ); -} - -void server_session::handle_write_handshake(const boost::system::error_code& error) { - if (error) { - log_error("Error writing handshake response",error); - drop_tcp(); - return; - } - - log_open_result(); - - if (m_server_http_code != 101) { - std::stringstream err; - err << "Handshake ended with HTTP error: " << m_server_http_code << " " - << (m_server_http_string != "" ? m_server_http_string : lookup_http_error_string(m_server_http_code)); - log(err.str(),LOG_ERROR); - drop_tcp(); - // TODO: tell client that connection failed. - return; - } - - m_state = STATE_OPEN; - - // stop the handshake timer - m_timer.cancel(); - - if (m_local_interface) { - m_local_interface->on_open(shared_from_this()); - } - - reset_message(); - this->read_frame(); -} - -void server_session::log(const std::string& msg, uint16_t level) const { - m_server->log(msg,level); -} - -void server_session::access_log(const std::string& msg, uint16_t level) const { - m_server->access_log(msg,level); -} diff --git a/src/legacy/websocket_server_session.hpp b/src/legacy/websocket_server_session.hpp deleted file mode 100644 index 538edaba72..0000000000 --- a/src/legacy/websocket_server_session.hpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2011, 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 WEBSOCKET_SERVER_SESSION_HPP -#define WEBSOCKET_SERVER_SESSION_HPP - -#include -#include - -#include -#include - -#if defined(WIN32) -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace websocketpp { - class server_session; - typedef boost::shared_ptr server_session_ptr; -} - -#include "websocket_session.hpp" -#include "websocket_server.hpp" - -using boost::asio::ip::tcp; - -namespace websocketpp { - -class server_session : public session { -public: - server_session (server_ptr s, - boost::asio::io_service& io_service, - connection_handler_ptr defc, - uint64_t buf_size); - - /*** SERVER INTERFACE ***/ - - // This function is called when a connection to a new client has been - // established and the server is ready to read the client handshake. - void on_connect(); - - /*** HANDSHAKE INTERFACE ***/ - - // Set an HTTP header for the outgoing server handshake response. - void set_header(const std::string& key, const std::string& val); - - // Selects a subprotocol for the connection to use. val must be a value - // present in the client's opening handshake or the empty string for null. - void select_subprotocol(const std::string& val); - - // Selects an extension from the list offered by the client. Each extension - // selected must have been offered by the client. Extensions will be used - // in the order that they were selected here. - void select_extension(const std::string& val); - - /*** SESSION INTERFACE ***/ - // see session - virtual bool is_server() const { return true;} - - void log(const std::string& msg, uint16_t level) const; - void access_log(const std::string& msg, uint16_t level) const; -protected: - // Opening handshake processors and callbacks. These need to be defined in - virtual void write_handshake(); - virtual void handle_write_handshake(const boost::system::error_code& e); - virtual void read_handshake(); - virtual void handle_read_handshake(const boost::system::error_code& e, - std::size_t bytes_transferred); - - - -private: - -protected: - // connection resources - server_ptr m_server; -private: - -}; - -} - -#endif // WEBSOCKET_SERVER_SESSION_HPP diff --git a/src/legacy/websocket_session.cpp b/src/legacy/websocket_session.cpp deleted file mode 100644 index 65d401e974..0000000000 --- a/src/legacy/websocket_session.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2011, 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. - * - */ diff --git a/src/legacy/websocket_session.hpp b/src/legacy/websocket_session.hpp deleted file mode 100644 index 76c48bddf0..0000000000 --- a/src/legacy/websocket_session.hpp +++ /dev/null @@ -1,1451 +0,0 @@ -/* - * Copyright (c) 2011, 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. - * - */ - -/* - - Exit path mapping - - In every path: - - If it is safe to close cleanly, close cleanly - - Write to the access log on clean close - - Write to the error log on unclean close and clean closes with a server error. - - If session state is open and a local client is connected, send on_close msg - - - make sure the following bits are properly set: - - - If we initiated the close by sending the first close frame or by dropping the TCP connection, set closed_by_me. If the other endpoint sent the first close method or we got an EOF while reading clear closed_by_me - - If we initiated the TCP connection drop set dropped_by_me. If we got EOF while reading clear dropped_by_me - - If we sent and received a close frame or we received and sent an acknowledgement close frame set was_clean to true. - - - If we are the server we should drop TCP immediately - - If we are the client we should drop TCP immediately except in the case where we just recieved an acknowledgement close frame. In this case wait a certain period of time for the server EOF. - - Questions: - - if the client rejects - - Paths: (+ indicates path has been checked and implimented) - Server Handshake Paths - - Accept connection, read handshake, handshake is valid, write handshake, no errors. This is the correct path and leads to the frame reading paths - - Accept connection, connection is not in state open after a time out (due to no bytes being read or no CRLFCRLF being read). This needs a time out after which we drop TCP. - - Accept connection, read handshake, handshake is invalid. write HTTP error. drop TCP - - Accept connection, read handshake, handshake is valid, write handshake returns EOF. This means client rejected something about our response. We should drop and notify our client. (note alternative client handshake reject method is to accept the handshake then immediately send a close message with the non-acceptance reason) - - Accept connection, read handshake, handshake is valid, write handshake returns another error. We should drop and notify our client. - Client Handshake Paths - - - Server Frame Reading Paths - - async read returns EOF. Close our own socket and notify our local interface. - - async read returns another error - - - - - Timeouts: - - handshake timeout - - wait for close frame after error - - (client) wait for server to drop tcp after close handshake - - idle client timeout? API specifiable? - - wait for pong? - - - - INTERFACES - - CLIENT ENDPOINT - - SERVER ENDPOINT - - CLIENT HANDLER - Valid for OPEN connections - - get_state() // CONNECTING, OPEN, CLOSING, CLOSED - - get_origin() - - get_request_header(const std::string&) - - get_version() - - get_uri() // includes secure, host, port, resource - - get_secure() - - - send(const std::string&) - - send(const std::vector&) - - close(status::code::value,const std::string&) - - ping(const std::string&) - - pong(const std::string&) - - - get_subprotocol() - - **** get extensions **** - - get_request_header(const std::string&) - - get_response_header(const std::string&) - - - Valid for CLOSED connections - - get_local_close_code() - - get_local_close_reason() - - get_remote_close_code() - - get_remote_close_reason() - - dropped_by_me? - - failed_by_me? - - closed_by_me? - - Callbacks that may be implimented - - on_message(const std::string&) - - on_message(const std::vector&) - - on_close - - on_fail? - - on_write_avaliable - - SERVER HANDLER - - Valid for CONNECTING connections - - **** get subprotocols **** - - **** get extensions **** - - set_request_header(const std::string&,const std::string&) - - select_subprotocol(const std::string&) - - select_extension(const std::string&) - - Valid during and after CONNECTING - - get_origin() - - get_request_header(const std::string&) - - get_version() - - get_uri() // includes secure, host, port, resource - - get_secure() - - get_state() // CONNECTING, OPEN, CLOSING, CLOSED - - Valid for OPEN connections - - send(const std::string&) - - send(const std::vector&) - - close(status::code::value,const std::string&) - - ping(const std::string&) - - pong(const std::string&) - - - get_subprotocol() - - **** get extensions **** - - get_response_header(const std::string&) - - Valid for CLOSED connections - - get_local_close_code() - - get_local_close_reason() - - get_remote_close_code() - - get_remote_close_reason() - - dropped_by_me? - - failed_by_me? - - closed_by_me? - - Callbacks that may be implimented - - validate - - on_message(const std::string&) - - on_message(const std::vector&) - - on_close - - on_fail? - - on_write_avaliable - - */ - -#ifndef WEBSOCKET_SESSION_HPP -#define WEBSOCKET_SESSION_HPP - -#include -#include -#include - -#include -#include - -#if defined(WIN32) -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace websocketpp { - class handshake_error; -} - -#include "websocketpp.hpp" -#include "websocket_frame.hpp" -#include "websocket_server.hpp" // for server error? -#include "websocket_connection_handler.hpp" - -#include "base64/base64.h" -#include "sha1/sha1.h" -#include "utf8_validator/utf8_validator.hpp" - -#include "http/parser.hpp" -#include "logger/logger.hpp" - -using boost::asio::ip::tcp; - -namespace websocketpp { - -namespace state { - enum value { - CONNECTING = 0, - OPEN = 1, - CLOSING = 2, - CLOSED = 3 - }; -} - -// Exception classes - -class handshake_error : public std::exception { -public: - handshake_error(const std::string& msg, - http::status_code::value http_error, - const std::string& http_msg = "") - : m_msg(msg),m_http_error_code(http_error),m_http_error_msg(http_msg) {} - ~handshake_error() throw() {} - - virtual const char* what() const throw() { - return m_msg.c_str(); - } - - std::string m_msg; - http::status_code::value m_http_error_code; - std::string m_http_error_msg; -}; - -typedef std::map header_list; - -template -class session : public boost::enable_shared_from_this< session > { -public: - typedef endpoint_policy endpoint_type; - typedef session session_type; - typedef connection_handler connection_handler_type; - - typedef boost::shared_ptr endpoint_ptr; - typedef boost::shared_ptr ptr; - typedef boost::shared_ptr connection_handler_ptr; - - friend class handshake_error; - - session (endpoint_ptr e, - boost::asio::io_service& io_service, - connection_handler_ptr defc, - uint64_t buf_size) - : m_secure(false), - m_state(state::CONNECTING), - m_writing(false), - m_local_close_code(close::status::NO_STATUS), - m_remote_close_code(close::status::NO_STATUS), - m_was_clean(false), - m_closed_by_me(false), - m_dropped_by_me(false), - m_socket(io_service), - m_io_service(io_service), - m_endpoint(e), - m_local_interface(defc), - m_timer(io_service,boost::posix_time::seconds(0)), - m_buf(buf_size), // maximum buffered (unconsumed) bytes from network - m_utf8_state(utf8_validator::UTF8_ACCEPT), - m_utf8_codepoint(0), - m_read_frame(e->get_rng()), - m_write_frame(e->get_rng()) {} - - /*** ENDPOINT INTERFACE ***/ - tcp::socket& socket() { - return m_socket; - } - - boost::asio::io_service& io_service() { - return m_io_service; - } - - void set_uri(const ws_uri& uri) { - m_uri = uri; - } - - const ws_uri& get_uri() { - return m_uri; - } - - void set_origin(const std::string& val) { - // TODO: input validation - m_origin = val; - } - - // TODO: should these be one set? - void set_request_header(const std::string& key,const std::string& val) { - // TODO: input validation - m_request.set_header(key,val); - } - - void set_response_header(const std::string& key,const std::string& val) { - // TODO: input validation - m_response.set_header(key,val); - } - - // Adds a subprotocol to the list to propose to the remote endpoint - // TODO: this should not be callable by server handlers - void request_subprotocol(const std::string &val) { - // TODO: input validation - m_requested_subprotocols.push_back(val); - } - - // Adds an extension to the list to propose to the remote endpoint - // TODO: this should not be callable by server handlers - void request_extension(const std::string& val) { - // TODO: input validation - m_requested_extensions.push_back(val); - } - - // Selects a subprotocol from the requested list to use. - // TODO: this should only be callable by server handlers - void select_subprotocol(const std::string& val) { - std::vector::iterator it; - - it = std::find(m_requested_subprotocols.begin(), - m_requested_subprotocols.end(), - val); - - if (val != "" && it == m_requested_subprotocols.end()) { - throw server_error("Attempted to choose a subprotocol not proposed by the client"); - } - - m_subprotocol = val; - } - - // Selects an extension from the requested list to use. - // TODO: this should only be callable by server handlers - void select_extension(const std::string& val) { - if (val == "") { - return; - } - - std::vector::iterator it; - - it = std::find(m_requested_extensions.begin(), - m_requested_extensions.end(), - val); - - if (it == m_requested_extensions.end()) { - throw server_error("Attempted to choose an extension not proposed by the client"); - } - - m_extensions.push_back(val); - } - - /*** SERVER INTERFACE ***/ - - // sets the internal connection handler of this connection to new_con. - // This is useful if you want to switch handler objects during a connection - // Example: a generic lobby handler could validate the handshake negotiate a - // sub protocol to talk to and then pass the connection off to a handler for - // that sub protocol. - void set_handler(connection_handler_ptr new_con) { - if (m_local_interface) { - // TODO: this should be another method and not reusing onclose - //m_local_interface->disconnect(shared_from_this(),4000,"Setting new connection handler"); - } - m_local_interface = new_con; - m_local_interface->on_open(session_type::shared_from_this()); - } - - - /*** HANDSHAKE INTERFACE ***/ - // Set session connection information (avaliable only before/during the - // opening handshake) - - // Get session status (valid once the connection is open) - - // returns the subprotocol that was negotiated during the opening handshake - // or the empty string if no subprotocol was requested. - const std::string& get_subprotocol() const { - if (m_state == state::CONNECTING) { - m_endpoint->elog().at(log::elevel::LIBRARY) - << "Subprotocol is not avaliable before the handshake has completed" - << log::endl; - - // TODO: fix server_error - //throw server_error("Subprotocol is not avaliable before the handshake has completed."); - throw "Subprotocol is not avaliable before the handshake has completed"; - } - return m_subprotocol; - } - - const std::string& get_resource() const { - return m_uri.resource; - } - const std::string& get_origin() const { - return m_origin; - } - std::string get_request_header(const std::string& key) const { - return m_request.header(key); - } - std::string get_response_header(const std::string& key) const { - return m_response.header(key); - } - const std::vector& get_extensions() const { - return m_extensions; - } - unsigned int get_version() const { - return m_version; - } - - /*** SESSION INTERFACE ***/ - - // send basic frame types - void send(const std::string &msg) { - if (m_state != state::OPEN) { - m_endpoint->elog().at(log::elevel::WARN) - << "Tried to send a message from a session that wasn't open" - << log::endl; - - return; - } - m_write_frame.set_fin(true); - m_write_frame.set_opcode(frame::opcode::TEXT); - m_write_frame.set_payload(msg); - - write_frame(); - } - - void send(const std::vector &data) { - if (m_state != state::OPEN) { - m_endpoint->elog().at(log::elevel::WARN) - << "Tried to send a message from a session that wasn't open" - << log::endl; - return; - } - m_write_frame.set_fin(true); - m_write_frame.set_opcode(frame::opcode::BINARY); - m_write_frame.set_payload(data); - - write_frame(); - } - void ping(const std::string &msg) { - if (m_state != state::OPEN) { - m_endpoint->elog().at(log::elevel::WARN) - << "Tried to send a ping from a session that wasn't open" - << log::endl; - return; - } - m_write_frame.set_fin(true); - m_write_frame.set_opcode(frame::opcode::PING); - m_write_frame.set_payload(msg); - - write_frame(); - } - void pong(const std::string &msg) { - if (m_state != state::OPEN) { - m_endpoint->elog().at(log::elevel::WARN) - << "Tried to send a pong from a session that wasn't open" - << log::endl; - return; - } - m_write_frame.set_fin(true); - m_write_frame.set_opcode(frame::opcode::PONG); - m_write_frame.set_payload(msg); - - write_frame(); - } - - // initiate a connection close - void close(close::status::value status,const std::string &reason) { - validate_app_close_status(status); - send_close(status,reason); - } - - bool is_server() const { - return endpoint_type::is_server; - } - - // These two function series are called to begin the session loop. The first - // method and all that come after it are called as a result of an async - // event completing. if any method in this chain returns before adding a new - // async event the session will end. - - // ****** Read Handshake Thread *********************** - // read_request -> handle_read_request -> - // write_response > handle_write_response -> - // read frame - // **************************************************** - - // Initiates the read of an HTTP request - void read_request() { - m_timer.expires_from_now(boost::posix_time::seconds(5)); - - m_timer.async_wait( - boost::bind( - &session_type::handle_handshake_expired, - session_type::shared_from_this(), - boost::asio::placeholders::error - ) - ); - - boost::asio::async_read_until( - m_socket, - m_buf, - "\r\n\r\n", - boost::bind( - &session_type::handle_read_request, - session_type::shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred - ) - ); - } - - // Callback for reading an HTTP request - void handle_read_request(const boost::system::error_code& e, - std::size_t bytes_transferred) { - if (e) { - log_error("Error reading HTTP request",e); - drop_tcp(); - return; - } - - try { - std::istream request(&m_buf); - - // TODO: use a more generic consume api where we just call read_some - // and have the handshake consume and validate as we go. - // - // For now, because it simplifies things we will use the parse_header - // member function which requires the complete header to be passed in - // initially. ASIO can guarantee us this. - // - // - //m_remote_handshake.consume(response_stream); - if (!m_request.parse_complete(request)) { - // not a valid HTTP request/response - throw handshake_error("Recieved invalid HTTP Request",http::status_code::BAD_REQUEST); - } - - // Log the raw handshake. - m_endpoint->alog().at(log::alevel::DEBUG_HANDSHAKE) << m_request.raw() << log::endl; - - // confirm that this is a valid handshake / response for our endpoint type. - m_endpoint->validate_handshake(m_request); - - // set some connection state from the handshake - // The endpoint validation should have ensured that all of these values - // exist and are acceptable. - std::string h = m_request.header("Sec-WebSocket-Version"); - - m_version = (h == "" ? 0 : atoi(h.c_str())); - - h = (m_version < 13 ? "Sec-WebSocket-Origin" : "Origin"); - - m_origin = m_request.header(h); - - // TODO: how does a server know if it is secure or not? - // set m_uri based on client host header - // TODO: what if uri is a full uri? - m_uri.secure = false; - m_uri.host = "localhost"; - m_uri.port = 9002; - m_uri.resource = m_request.uri(); - - h = m_request.header("Sec-WebSocket-Version"); - - // TODO: the generic consume API will handle this better. - // if we have determined that this is trying to be a websocket - // connection for hybi-00 read the key after the HTTP request. - if (m_version == 0) { - char foo[9]; - foo[8] = 0; - - request.get(foo,9); - - if (request.gcount() != 8) { - - std::cout << "gcount: " << request.gcount() << " foo: " << foo << std::endl; - - throw handshake_error("Missing Key3",http::status_code::BAD_REQUEST); - } - - m_request.set_header("Sec-WebSocket-Key3",std::string(foo)); - } - - // TODO: extract subprotocols? - // TODO: extract extensions? - - // Check with the local interface to confirm that it wants to accept - // this connection. - if (m_local_interface) { - m_local_interface->validate(session_type::shared_from_this()); - } - - m_response.set_status(http::status_code::SWITCHING_PROTOCOLS); - } catch (const handshake_error& e) { - // TODO: add a hook here for passing the request to the local handler - // in case they can answer it instead of returning an error. - - m_endpoint->alog().at(log::alevel::DEBUG_HANDSHAKE) << e.what() << log::endl; - - m_endpoint->elog().at(log::elevel::ERROR) - << "Caught handshake exception: " << e.what() << log::endl; - - m_response.set_status(e.m_http_error_code,e.m_http_error_msg); - } - - write_response(); - } - - // write the response to the client's request. - void write_response() { - std::stringstream h; - - m_response.set_version("HTTP/1.1"); - - char digest[17]; - - if (m_response.status_code() == http::status_code::SWITCHING_PROTOCOLS) { - if (m_version == 0) { - char key_final[16]; - - // key1 - *reinterpret_cast(&key_final[0]) = decode_hybi_00_client_key( - m_request.header("Sec-WebSocket-Key2")); - - // key2 - *reinterpret_cast(&key_final[4]) = decode_hybi_00_client_key( - m_request.header("Sec-WebSocket-Key2")); - - // key3 - memcpy(&key_final[8], - m_request.header("Sec-WebSocket-Key3").c_str(), - 8); - - // md5 - md5_hash_string(key_final,digest); - digest[16] = 0; - - m_response.set_header("Upgrade","websocket"); - m_response.set_header("Connection","Upgrade"); - - // Echo back client's origin unless our local application set a - // more restrictive one. - if (m_response.header("Sec-WebSocket-Origin") == "") { - m_response.set_header("Sec-WebSocket-Origin",m_request.header("Origin")); - } - - // Echo back the client's request host unless our local application - // set a different one. - if (m_response.header("Sec-WebSocket-Location") == "") { - m_response.set_header("Sec-WebSocket-Location",m_uri.base()); - } - } else { - std::string server_key = m_request.header("Sec-WebSocket-Key"); - server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - SHA1 sha; - uint32_t message_digest[5]; - - sha.Reset(); - sha << server_key.c_str(); - - if (sha.Result(message_digest)){ - // convert sha1 hash bytes to network byte order because this sha1 - // library works on ints rather than bytes - for (int i = 0; i < 5; i++) { - message_digest[i] = htonl(message_digest[i]); - } - - server_key = base64_encode( - reinterpret_cast(message_digest),20 - ); - - // set handshake accept headers - m_response.replace_header("Sec-WebSocket-Accept",server_key); - m_response.set_header("Upgrade","websocket"); - m_response.set_header("Connection","Upgrade"); - } else { - m_endpoint->elog().at(log::elevel::ERROR) - << "Error computing handshake sha1 hash" << log::endl; - - m_response.set_status(http::status_code::INTERNAL_SERVER_ERROR); - } - } - } - - if (m_subprotocol != "") { - m_response.replace_header("Sec-WebSocket-Protocol",m_subprotocol); - } - - // TODO: return negotiated extensions - - // hardcoded server headers - // TODO: make this configurable - m_response.replace_header("Server","WebSocket++/2011-10-31"); - - m_endpoint->alog().at(log::alevel::DEBUG_HANDSHAKE) - << m_response.raw() << log::endl; - - std::string raw = m_response.raw(); - - if (m_version == 0) { - raw += digest; - } - - // start async write to handle_write_handshake - boost::asio::async_write( - m_socket, - boost::asio::buffer(raw), - boost::bind( - &session_type::handle_write_response, - session_type::shared_from_this(), - boost::asio::placeholders::error - ) - ); - } - - // - void handle_write_response(const boost::system::error_code& error) { - if (error) { - log_error("Error writing handshake response",error); - drop_tcp(); - return; - } - - log_open_result(); - - if (m_response.status_code() != http::status_code::SWITCHING_PROTOCOLS) { - m_endpoint->elog().at(log::elevel::ERROR) - << "Handshake ended with HTTP error: " - << m_response.status_code() << " " << m_response.status_msg() - << log::endl; - - drop_tcp(); - // TODO: tell client that connection failed? - // use on_fail? - return; - } - - m_state = state::OPEN; - - // stop the handshake timer - m_timer.cancel(); - - if (m_local_interface) { - m_local_interface->on_open(session_type::shared_from_this()); - } - - reset_message(); - this->read_frame(); - } - - // ****** Write Handshake Thread ********************** - // write_request -> handle_write_request -> - // read_response > handle_read_response -> - // read frame - // **************************************************** - - void write_request() { - m_request.set_method("GET"); - m_request.set_uri(m_uri.resource); - m_request.set_version("HTTP/1.1"); - - // Set request headers - m_request.set_header("Upgrade","websocket"); - m_request.set_header("Connection","Upgrade"); - m_request.replace_header("Sec-WebSocket-Version","13"); - m_version = 13; - - std::stringstream host; - if (m_uri.port == (m_secure ? 443 : 80)) { - host << m_uri.host; - } else { - host << m_uri.host << ":" << m_uri.port; - } - m_request.replace_header("Host",host.str()); - - if (m_origin != "") { - m_request.replace_header("Origin",m_origin); - } - - std::string client_key; - int32_t raw_key[4]; - - for (int i = 0; i < 4; i++) { - raw_key[i] = m_endpoint->get_rng().gen(); - } - - client_key = base64_encode(reinterpret_cast(raw_key), 16); - - m_endpoint->alog().at(log::alevel::DEBUG_HANDSHAKE) - << "Client key chosen: " << client_key << log::endl; - - m_request.replace_header("Sec-WebSocket-Key",client_key); - m_request.replace_header("User Agent","WebSocket++/2011-10-31"); - - // start async write to write the request - boost::asio::async_write( - m_socket, - boost::asio::buffer(m_request.raw()), - boost::bind( - &session_type::handle_write_request, - session_type::shared_from_this(), - boost::asio::placeholders::error - ) - ); - } - - void handle_write_request(const boost::system::error_code& error) { - if (error) { - log_error("Error writing HTTP request",error); - drop_tcp(); - return; - } - - read_response(); - } - - void read_response() { - boost::asio::async_read_until( - m_socket, - m_buf, - "\r\n\r\n", - boost::bind( - &session_type::handle_read_response, - session_type::shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred - ) - ); - } - - void handle_read_response(const boost::system::error_code& e,std::size_t bytes_transferred) { - - } - - void read_frame() { - // the initial read in the handshake may have read in the first frame. - // handle it (if it exists) before we read anything else. - handle_read_frame(boost::system::error_code()); - } - // handle_read_frame reads and processes all socket read commands for the - // session by consuming the read buffer and then starting an async read with - // itself as the callback. The connection is over when this method returns. - void handle_read_frame (const boost::system::error_code& error) { - if (m_state != state::OPEN && m_state != state::CLOSING) { - m_endpoint->elog().at(log::elevel::ERROR) - << "handle_read_frame called in invalid state" << log::endl; - return; - } - - if (error) { - if (error == boost::asio::error::eof) { - // if this is a case where we are expecting eof, return, else log & drop - - log_error("Recieved EOF",error); - //drop_tcp(false); - //m_state = STATE_CLOSED; - } else if (error == boost::asio::error::operation_aborted) { - // some other part of our client called shutdown on our socket. - // This is usually due to a write error. Everything should have - // already been logged and dropped so we just return here - return; - } else { - log_error("Error reading frame",error); - //drop_tcp(false); - m_state = state::CLOSED; - } - } - - std::istream s(&m_buf); - - while (m_buf.size() > 0 && m_state != state::CLOSED) { - try { - if (m_read_frame.get_bytes_needed() == 0) { - throw frame::exception("have bytes that no frame needs",frame::error::FATAL_SESSION_ERROR); - } - - // Consume will read bytes from s - // will throw a frame_error on error. - - - //err << "consuming. have: " << m_buf.size() << " bytes. Need: " << m_read_frame.get_bytes_needed() << " state: " << (int)m_read_frame.get_state(); - //log(err.str(),LOG_DEBUG); - m_read_frame.consume(s); - - //err.str(""); - //err << "consume complete, " << m_buf.size() << " bytes left, " << m_read_frame.get_bytes_needed() << " still needed, state: " << (int)m_read_frame.get_state(); - //log(err.str(),LOG_DEBUG); - - if (m_read_frame.ready()) { - // process frame and reset frame state for the next frame. - // will throw a frame_error on error. May set m_state to CLOSED, - // if so no more frames should be processed. - m_endpoint->elog().at(log::elevel::DEVEL) - << "processing frame " << m_buf.size() << log::endl; - - m_timer.cancel(); - process_frame(); - } - } catch (const frame::exception& e) { - m_endpoint->elog().at(log::elevel::ERROR) - << "Caught frame exception: " << e.what() << log::endl; - - // if the exception happened while processing. - // TODO: this is not elegant, perhaps separate frame read vs process - // exceptions need to be used. - if (m_read_frame.ready()) { - m_read_frame.reset(); - } - - // process different types of frame errors - // - if (e.code() == frame::error::PROTOCOL_VIOLATION) { - send_close(close::status::PROTOCOL_ERROR, e.what()); - } else if (e.code() == frame::error::PAYLOAD_VIOLATION) { - send_close(close::status::INVALID_PAYLOAD, e.what()); - } else if (e.code() == frame::error::INTERNAL_SERVER_ERROR) { - send_close(close::status::ABNORMAL_CLOSE, e.what()); - } else if (e.code() == frame::error::SOFT_SESSION_ERROR) { - // ignore and continue processing frames - continue; - } else { - // Fatal error, forcibly end connection immediately. - m_endpoint->elog().at(log::elevel::DEVEL) - << "Dropping TCP due to unrecoverable exception" - << log::endl; - drop_tcp(true); - } - - break; - } - } - - if (error == boost::asio::error::eof) { - m_state = state::CLOSED; - } - - // we have read everything, check if we should read more - - if ((m_state == state::OPEN || m_state == state::CLOSING) && m_read_frame.get_bytes_needed() > 0) { - m_endpoint->elog().at(log::elevel::DEVEL) - << "Starting async read for " << m_read_frame.get_bytes_needed() - << " bytes" << log::endl; - - - // TODO: set a timer here in case we don't want to read forever. - // Ex: when the frame is in a degraded state. - - boost::asio::async_read( - m_socket, - m_buf, - boost::asio::transfer_at_least(m_read_frame.get_bytes_needed()), - boost::bind( - &session::handle_read_frame, - session::shared_from_this(), - boost::asio::placeholders::error - ) - ); - } else if (m_state == state::CLOSED) { - log_close_result(); - - if (m_local_interface) { - // TODO: make sure close code/msg are properly set. - m_local_interface->on_close(session_type::shared_from_this()); - } - - m_timer.cancel(); - } else { - m_endpoint->elog().at(log::elevel::ERROR) - << "handle_read_frame ended in an invalid state" << log::endl; - } - } - - // write m_write_frame out to the socket. - void write_frame() { - if (!is_server()) { - m_write_frame.set_masked(true); // client must mask frames - } - - m_write_frame.process_payload(); - - std::vector data; - - data.push_back( - boost::asio::buffer( - m_write_frame.get_header(), - m_write_frame.get_header_len() - ) - ); - data.push_back( - boost::asio::buffer(m_write_frame.get_payload()) - ); - - m_endpoint->elog().at(log::elevel::DEVEL) - << "Write Frame: " << m_write_frame.print_frame() << log::endl; - - m_writing = true; - - boost::asio::async_write( - m_socket, - data, - boost::bind( - &session::handle_write_frame, - session::shared_from_this(), - boost::asio::placeholders::error - ) - ); - - } - void handle_write_frame (const boost::system::error_code& error) { - if (error) { - log_error("Error writing frame data",error); - drop_tcp(false); - } - - m_writing = false; - } - - void handle_timer_expired(const boost::system::error_code& error) { - if (error) { - if (error == boost::asio::error::operation_aborted) { - m_endpoint->elog().at(log::elevel::DEVEL) - << "timer was aborted" << log::endl; - } else { - m_endpoint->elog().at(log::elevel::DEVEL) - << "timer ended with error" << log::endl; - } - return; - } - - m_endpoint->elog().at(log::elevel::DEVEL) - << "timer ended without error" << log::endl; - } - void handle_handshake_expired(const boost::system::error_code& error) { - if (error) { - if (error != boost::asio::error::operation_aborted) { - m_endpoint->elog().at(log::elevel::DEVEL) - << "unexpected handshake timer error" << log::endl; - drop_tcp(true); - } - return; - } - - m_endpoint->elog().at(log::elevel::DEVEL) - << "Handshake timed out" << log::endl; - drop_tcp(true); - } - void handle_close_expired(const boost::system::error_code& error) { - if (error) { - if (error == boost::asio::error::operation_aborted) { - m_endpoint->elog().at(log::elevel::DEVEL) - << "timer was aborted" << log::endl; - } else { - m_endpoint->elog().at(log::elevel::DEVEL) - << "Unexpected close timer error" << log::endl; - drop_tcp(false); - } - return; - } - - if (m_state != state::CLOSED) { - m_endpoint->elog().at(log::elevel::DEVEL) - << "close timed out" << log::endl; - drop_tcp(false); - } - } - // The error timer is set when we want to give the other endpoint some time to - // do something but don't want to wait forever. There is a special error code - // that represents the timer being canceled by us (because the other endpoint - // responded in time. All other cases should assume that the other endpoint is - // irrepairibly broken and drop the TCP connection. - void handle_error_timer_expired (const boost::system::error_code& error) { - if (error) { - if (error == boost::asio::error::operation_aborted) { - m_endpoint->elog().at(log::elevel::DEVEL) - << "error timer was aborted" << log::endl; - } else { - m_endpoint->elog().at(log::elevel::DEVEL) - << "error timer ended with error" << log::endl; - drop_tcp(true); - } - return; - } - - m_endpoint->elog().at(log::elevel::DEVEL) - << "error timer ended without error" << log::endl; - drop_tcp(true); - } - - // helper functions for processing each opcode - void process_frame() { - if (m_state == state::OPEN) { - switch (m_read_frame.get_opcode()) { - case frame::opcode::CONTINUATION: - process_continuation(); - break; - case frame::opcode::TEXT: - process_text(); - break; - case frame::opcode::BINARY: - process_binary(); - break; - case frame::opcode::CLOSE: - process_close(); - break; - case frame::opcode::PING: - process_ping(); - break; - case frame::opcode::PONG: - process_pong(); - break; - default: - throw frame::exception("Invalid Opcode", - frame::error::PROTOCOL_VIOLATION); - break; - } - } else if (m_state == state::CLOSING) { - if (m_read_frame.get_opcode() == frame::opcode::CLOSE) { - process_close(); - } else { - // Ignore all other frames in closing state - m_endpoint->elog().at(log::elevel::DEVEL) - << "ignoring this frame" << log::endl; - } - } else { - // Recieved message before or after connection was opened/closed - throw frame::exception("process_frame called from invalid state"); - } - - m_read_frame.reset(); - } - void process_ping() { - m_endpoint->alog().at(log::alevel::CONTROL) - << "Recieved Ping Frame" << log::endl; - // TODO: on_ping - - // send pong - m_write_frame.set_fin(true); - m_write_frame.set_opcode(frame::opcode::PONG); - m_write_frame.set_payload(m_read_frame.get_payload()); - - write_frame(); - } - void process_pong() { - m_endpoint->alog().at(log::alevel::CONTROL) - << "Recieved Pong Frame" << log::endl; - // TODO: on_pong - } - void process_text() { - // this will throw an exception if validation fails at any point - m_read_frame.validate_utf8(&m_utf8_state,&m_utf8_codepoint); - - // otherwise, treat as binary - process_binary(); - } - void process_binary() { - if (m_fragmented) { - throw frame::exception("Got a new message before the previous was finished.",frame::error::PROTOCOL_VIOLATION); - } - - m_current_opcode = m_read_frame.get_opcode(); - - if (m_read_frame.get_fin()) { - deliver_message(); - reset_message(); - } else { - m_fragmented = true; - extract_payload(); - } - } - void process_continuation() { - if (!m_fragmented) { - throw frame::exception("Got a continuation frame without an outstanding message.",frame::error::PROTOCOL_VIOLATION); - } - - if (m_current_opcode == frame::opcode::TEXT) { - // this will throw an exception if validation fails at any point - m_read_frame.validate_utf8(&m_utf8_state,&m_utf8_codepoint); - } - - extract_payload(); - - // check if we are done - if (m_read_frame.get_fin()) { - deliver_message(); - reset_message(); - } - } - void process_close() { - m_remote_close_code = m_read_frame.get_close_status(); - m_remote_close_msg = m_read_frame.get_close_msg(); - - if (m_state == state::OPEN) { - m_endpoint->elog().at(log::elevel::DEVEL) - << "process_close sending ack" << log::endl; - // This is the case where the remote initiated the close. - m_closed_by_me = false; - // send acknowledgement - - // TODO: check if the remote close code - if (m_remote_close_code >= close::status::RSV_START) { - - } - - send_close(m_remote_close_code,m_remote_close_msg); - } else if (m_state == state::CLOSING) { - m_endpoint->elog().at(log::elevel::DEVEL) - << "process_close got ack" << log::endl; - // this is an ack of our close message - m_closed_by_me = true; - } else { - throw frame::exception("process_closed called from wrong state"); - } - - m_was_clean = true; - m_state = state::CLOSED; - } - - // deliver message if we have a local interface attached - void deliver_message() { - if (!m_local_interface) { - return; - } - - if (m_current_opcode == frame::opcode::BINARY) { - //log("Dispatching Binary Message",LOG_DEBUG); - if (m_fragmented) { - m_local_interface->on_message(session_type::shared_from_this(),m_current_message); - } else { - m_local_interface->on_message(session_type::shared_from_this(), - m_read_frame.get_payload()); - } - } else if (m_current_opcode == frame::opcode::TEXT) { - std::string msg; - - // make sure the finished frame is valid utf8 - // the streaming validator checks for bad codepoints as it goes. It - // doesn't know where the end of the message is though, so we need to - // check here to make sure the final message ends on a valid codepoint. - if (m_utf8_state != utf8_validator::UTF8_ACCEPT) { - throw frame::exception("Invalid UTF-8 Data", - frame::error::PAYLOAD_VIOLATION); - } - - if (m_fragmented) { - msg.append(m_current_message.begin(),m_current_message.end()); - } else { - msg.append( - m_read_frame.get_payload().begin(), - m_read_frame.get_payload().end() - ); - } - - //log("Dispatching Text Message",LOG_DEBUG); - m_local_interface->on_message(session_type::shared_from_this(),msg); - } else { - // Not sure if this should be a fatal error or not - std::stringstream err; - err << "Attempted to deliver a message of unsupported opcode " << m_current_opcode; - throw frame::exception(err.str(),frame::error::SOFT_SESSION_ERROR); - } - } - - // copies the current read frame payload into the session so that the read - // frame can be cleared for the next read. This is done when fragmented - // messages are recieved. - void extract_payload() { - std::vector &msg = m_read_frame.get_payload(); - m_current_message.resize(m_current_message.size()+msg.size()); - std::copy(msg.begin(),msg.end(),m_current_message.end()-msg.size()); - } - - // reset session for a new message - void reset_message() { - m_fragmented = false; - m_current_message.clear(); - - m_utf8_state = utf8_validator::UTF8_ACCEPT; - m_utf8_codepoint = 0; - } - - // logging - void log_close_result() { - m_endpoint->alog().at(log::alevel::DISCONNECT) - << "Disconnect " << (m_was_clean ? "Clean" : "Unclean") - << " close local:[" << m_local_close_code - << (m_local_close_msg == "" ? "" : ","+m_local_close_msg) - << "] remote:[" << m_remote_close_code - << (m_remote_close_msg == "" ? "" : ","+m_remote_close_msg) << "]" - << log::endl; - } - void log_open_result() { - m_endpoint->alog().at(log::alevel::CONNECT) << "Connection " - << m_socket.remote_endpoint() << " v" << m_version << " " - << (get_request_header("User-Agent") == "" ? "NULL" : get_request_header("User-Agent")) - << " " << m_uri.resource << " " << m_response.status_code() - << log::endl; - } - // this is called when an async asio call encounters an error - void log_error(std::string msg,const boost::system::error_code& e) { - m_endpoint->elog().at(log::elevel::ERROR) - << msg << "(" << e << ")" << log::endl; - } - - // misc helpers - - // validates status codes that the end application is allowed to use - bool validate_app_close_status(close::status::value status) { - if (status == close::status::NORMAL) { - return true; - } - - if (status >= 4000 && status < 5000) { - return true; - } - - return false; - } - - void send_close(close::status::value status,const std::string& reason) { - if (m_state != state::OPEN) { - m_endpoint->elog().at(log::elevel::WARN) - << "Tried to disconnect a session that wasn't open" << log::endl; - return; - } - - m_state = state::CLOSING; - - m_timer.expires_from_now(boost::posix_time::milliseconds(1000)); - - m_timer.async_wait( - boost::bind( - &session::handle_close_expired, - session::shared_from_this(), - boost::asio::placeholders::error - ) - ); - - m_local_close_code = status; - m_local_close_msg = reason; - - m_write_frame.set_fin(true); - m_write_frame.set_opcode(frame::opcode::CLOSE); - - // echo close value unless there is a good reason not to. - if (status == close::status::NO_STATUS) { - m_write_frame.set_status(close::status::NORMAL,""); - } else if (status == close::status::ABNORMAL_CLOSE) { - // Internal implimentation error. There is no good close code for this. - m_write_frame.set_status(close::status::POLICY_VIOLATION,reason); - } else if (close::status::invalid(status)) { - m_write_frame.set_status(close::status::PROTOCOL_ERROR,"Status code is invalid"); - } else if (close::status::reserved(status)) { - m_write_frame.set_status(close::status::PROTOCOL_ERROR,"Status code is reserved"); - } else { - m_write_frame.set_status(status,reason); - } - - write_frame(); - } - - void drop_tcp(bool dropped_by_me = true) { - m_timer.cancel(); - try { - if (m_socket.is_open()) { - m_socket.shutdown(tcp::socket::shutdown_both); - m_socket.close(); - } - } catch (boost::system::system_error& e) { - if (e.code() == boost::asio::error::not_connected) { - // this means the socket was disconnected by the other side before - // we had a chance to. Ignore and continue. - } else { - throw e; - } - } - m_dropped_by_me = dropped_by_me; - m_state = state::CLOSED; - } -private: - std::string get_header(const std::string& key, - const header_list& list) const { - header_list::const_iterator h = list.find(key); - - if (h == list.end()) { - return ""; - } else { - return h->second; - } - } - - http::parser::request m_request; - http::parser::response m_response; - - // some settings about the connection - std::vector m_requested_subprotocols; - std::vector m_requested_extensions; - std::string m_subprotocol; - std::vector m_extensions; - std::string m_origin; - unsigned int m_version; - bool m_secure; - ws_uri m_uri; - - // Mutable connection state; - uint8_t m_state; - bool m_writing; - - // Close state - close::status::value m_local_close_code; - std::string m_local_close_msg; - close::status::value m_remote_close_code; - std::string m_remote_close_msg; - bool m_was_clean; - bool m_closed_by_me; - bool m_dropped_by_me; - - // Connection Resources - tcp::socket m_socket; - boost::asio::io_service& m_io_service; - endpoint_ptr m_endpoint; - connection_handler_ptr m_local_interface; - boost::asio::deadline_timer m_timer; - - // Buffers - boost::asio::streambuf m_buf; - - // current message state - uint32_t m_utf8_state; - uint32_t m_utf8_codepoint; - std::vector m_current_message; - bool m_fragmented; - frame::opcode::value m_current_opcode; - - // frame parsers - frame::parser m_read_frame; - frame::parser m_write_frame; -}; - -} - -#endif // WEBSOCKET_SESSION_HPP