diff --git a/src/websocket_constants.hpp b/src/common.hpp similarity index 54% rename from src/websocket_constants.hpp rename to src/common.hpp index 0084f550a0..4ef947c000 100644 --- a/src/websocket_constants.hpp +++ b/src/common.hpp @@ -30,9 +30,7 @@ #include -// for exceptions that should be somewhere else #include -#include #include #include @@ -52,34 +50,19 @@ namespace websocketpp { inline uint16_t default_port(bool secure) { return (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT); + } + + namespace session { + namespace state { + enum value { + CONNECTING = 0, + OPEN = 1, + CLOSING = 2, + CLOSED = 3 + }; + } } - // System logging levels - static const uint16_t LOG_ALL = 0; - static const uint16_t LOG_DEBUG = 1; - static const uint16_t LOG_INFO = 2; - static const uint16_t LOG_WARN = 3; - static const uint16_t LOG_ERROR = 4; - static const uint16_t LOG_FATAL = 5; - static const uint16_t LOG_OFF = 6; - - // Access logging controls - // Individual bits - static const uint16_t ALOG_CONNECT = 0x1; - static const uint16_t ALOG_DISCONNECT = 0x2; - static const uint16_t ALOG_MISC_CONTROL = 0x4; - static const uint16_t ALOG_FRAME = 0x8; - static const uint16_t ALOG_MESSAGE = 0x10; - static const uint16_t ALOG_INFO = 0x20; - static const uint16_t ALOG_HANDSHAKE = 0x40; - // Useful groups - static const uint16_t ALOG_OFF = 0x0; - static const uint16_t ALOG_CONTROL = ALOG_CONNECT - & ALOG_DISCONNECT - & ALOG_MISC_CONTROL; - static const uint16_t ALOG_ALL = 0xFFFF; - - namespace close { namespace status { enum value { @@ -115,76 +98,6 @@ 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 - } - } - - - - // TODO: these classes need a better place to live - class server_error : public std::exception { - public: - server_error(const std::string& msg) - : m_msg(msg) {} - ~server_error() throw() {} - - virtual const char* what() const throw() { - return m_msg.c_str(); - } - private: - std::string m_msg; - }; } #endif // WEBSOCKET_CONSTANTS_HPP diff --git a/src/connection.hpp b/src/connection.hpp index 20fd322a3e..7ba42eb5e7 100644 --- a/src/connection.hpp +++ b/src/connection.hpp @@ -28,12 +28,14 @@ #ifndef WEBSOCKETPP_CONNECTION_HPP #define WEBSOCKETPP_CONNECTION_HPP -//#include "endpoint.hpp" +#include "common.hpp" #include "http/parser.hpp" #include "logger.hpp" #include "roles/server.hpp" +#include "processors/hybi.hpp" + #include #include #include @@ -42,62 +44,9 @@ #include // temporary? #include #include +#include namespace websocketpp { - -// types to use for utf8 string and binary messages -typedef std::vector binary_string; -typedef boost::shared_ptr binary_string_ptr; - -typedef std::string utf8_string; -typedef boost::shared_ptr utf8_string_ptr; - -// Universal close status codes. Might be better somewhere else. -namespace close { -namespace status { - enum value { - INVALID_END = 999, - NORMAL = 1000, - GOING_AWAY = 1001, - PROTOCOL_ERROR = 1002, - UNSUPPORTED_DATA = 1003, - RSV_ADHOC_1 = 1004, - NO_STATUS = 1005, - ABNORMAL_CLOSE = 1006, - INVALID_PAYLOAD = 1007, - POLICY_VIOLATION = 1008, - MESSAGE_TOO_BIG = 1009, - EXTENSION_REQUIRE = 1010, - RSV_START = 1011, - RSV_END = 2999, - INVALID_START = 5000 - }; - - inline bool reserved(value s) { - return ((s >= RSV_START && s <= RSV_END) || - s == RSV_ADHOC_1); - } - - inline bool invalid(value s) { - return ((s <= INVALID_END || s >= INVALID_START) || - s == NO_STATUS || - s == ABNORMAL_CLOSE); - } - - // TODO functions for application ranges? -} -} - -namespace session { -namespace state { - enum value { - CONNECTING = 0, - OPEN = 1, - CLOSING = 2, - CLOSED = 3 - }; -} -} template struct connection_traits; @@ -124,14 +73,28 @@ public: // friends (would require C++11) this would enable connection::start to be // protected instead of public. // friend typename endpoint_traits::role_type; + //friend class role; + //friend class socket; + friend class role:: template connection; + friend class socket:: template connection; + enum write_state { + IDLE = 0, + WRITING = 1, + INTURRUPT = 2 + }; connection(endpoint_type& e) : role_type(e), socket_type(e), - m_endpoint(e) {} + m_endpoint(e), + m_timer(e.endpoint_base::m_io_service,boost::posix_time::seconds(0)), + m_state(session::state::CONNECTING), + m_write_buffer(0), + m_write_state(IDLE) {} + // SHOULD BE PROTECTED void start() { // initialize the socket. socket_type::async_init( @@ -142,6 +105,7 @@ public: ) ); } + // END PROTECTED // Valid always session::state::value get_state() const { @@ -149,62 +113,155 @@ public: } // Valid for OPEN state - void send(utf8_string_ptr msg) {} - void send(binary_string_ptr data) {} - void close(close::status::value code, const utf8_string& reason) {} - void ping(binary_string_ptr data) {} - void pong(binary_string_ptr data) {} + void send(const utf8_string& payload) { + binary_string_ptr msg(m_processor->prepare_frame(frame::opcode::TEXT, + false,payload)); + + m_endpoint.endpoint_base::m_io_service.post( + boost::bind( + &type::write_message, + type::shared_from_this(), + msg)); + } + void send(const binary_string& data) { + binary_string_ptr msg(m_processor->prepare_frame(frame::opcode::BINARY, + false,data)); + m_endpoint.endpoint_base::m_io_service.post( + boost::bind( + &type::write_message, + 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_endpoint.m_io_service.post( + boost::bind( + &type::write_message, + 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_endpoint.m_io_service.post( + boost::bind( + &type::write_message, + type::shared_from_this(), + msg)); + } - uint64_t buffered_amount() const; + uint64_t buffered_amount() const { + return m_write_buffer; + } // Valid for CLOSED state - close::status::value get_local_close_code() const {}; - utf8_string get_local_close_reason() const {}; - close::status::value get_remote_close_code() const {}; - utf8_string get_remote_close_reason() const {}; - bool failed_by_me() const {}; - bool dropped_by_me() const {}; - bool closed_by_me() const {}; + 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; + } protected: void handle_socket_init(const boost::system::error_code& error) { if (error) { m_endpoint.elog().at(log::elevel::ERROR) << "Connection initialization failed, error code: " << error << log::endl; - this->terminate(); + this->terminate(false); return; } - this->websocket_handshake(); + role_type::async_init(); } - void websocket_handshake() { - m_endpoint.alog().at(log::alevel::DEVEL) << "Websocket Handshake" << log::endl; + 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; } - socket_type::get_socket().async_read_some( - boost::asio::buffer(m_data, 512), - boost::bind( - &type::handle_read, - type::shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred - ) - ); - } - - void handle_read(const boost::system::error_code& error, - size_t bytes_transferred) - { if (error) { - std::cout << "read error" << std::endl; - } else { - std::cout << "read complete: " << m_data << std::endl; + if (error == boost::asio::error::eof) { + // got unexpected EOF + // TODO: log error + terminate(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(true); + } else { + // Other unexpected error + + // TODO: log error + terminate(false); + } + } + + // process data from the buffer just read into + std::istream s(&m_buf); + + while (m_state != session::state::CLOSED && m_buf.size() > 0) { + try { + m_processor->consume(s); + + if (m_processor->ready()) { + process_message(); + m_processor->reset(); + } + } catch (const processor::exception& e) { + if (m_processor->ready()) { + m_processor->reset(); + } + + if (e.code() == processor::error::PROTOCOL_VIOLATION) { + send_close(close::status::PROTOCOL_ERROR, e.what()); + } else if (e.code() == processor::error::PAYLOAD_VIOLATION) { + send_close(close::status::INVALID_PAYLOAD, e.what()); + } else if (e.code() == processor::error::INTERNAL_SERVER_ERROR) { + send_close(close::status::POLICY_VIOLATION, e.what()); + } else if (e.code() == processor::error::SOFT_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; + terminate(true); + } + break; + } + } + + // try and read more + if (m_state != session::state::CLOSED && + m_processor->get_bytes_needed() > 0) { + // TODO: read timeout timer? - std::string r = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nbar"; - - boost::asio::async_write( + boost::asio::async_read( socket_type::get_socket(), - boost::asio::buffer(r, r.size()), + m_buf, + boost::asio::transfer_at_least(m_processor->get_bytes_needed()), boost::bind( - &type::handle_write, + &type::handle_read_frame, type::shared_from_this(), boost::asio::placeholders::error ) @@ -212,29 +269,325 @@ protected: } } + void process_message() { + bool response; + switch (m_processor->get_opcode()) { + case frame::opcode::TEXT: + m_endpoint.get_handler()->on_message( + type::shared_from_this(), + m_processor->get_utf8_payload()); + break; + case frame::opcode::BINARY: + m_endpoint.get_handler()->on_message( + type::shared_from_this(), + m_processor->get_binary_payload()); + break; + case frame::opcode::PING: + response = m_endpoint.get_handler()->on_ping( + 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_endpoint.get_handler()->on_pong( + 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 processor::exception("Invalid close code",processor::error::PROTOCOL_VIOLATION); + } + + if (close::status::reserved(m_remote_close_code)) { + throw processor::exception("Reserved close code",processor::error::PROTOCOL_VIOLATION); + } + + if (m_state == session::state::OPEN) { + // other end is initiating + m_endpoint.elog().at(log::elevel::DEVEL) + << "sending close ack" << log::endl; + + // TODO: + send_close_ack(); + } else if (m_state == session::state::CLOSING) { + // ack of our close + m_endpoint.elog().at(log::elevel::DEVEL) + << "got close ack" << log::endl; + + terminate(false); + // TODO: start terminate timer (if client) + } + break; + default: + throw processor::exception("Invalid Opcode",processor::error::PROTOCOL_VIOLATION); + break; + } + + } + + void send_close(close::status::value code, const std::string& reason) { + if (m_state != session::state::OPEN) { + m_endpoint.elog().at(log::elevel::WARN) + << "Tried to disconnect a session that wasn't open" << log::endl; + return; + } + + if (close::status::invalid(code)) { + m_endpoint.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_endpoint.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( + &type::fail_on_expire, + 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 = 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 = 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 IDLE: + break; + case WRITING: + // already writing. write() will get called again by the write + // handler once it is ready. + return; + case 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 == IDLE) { + m_write_state = WRITING; + } + + boost::asio::async_write( + socket_type::get_socket(), + boost::asio::buffer(*m_write_queue.front()), + boost::bind( + &type::handle_write, + 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 == INTURRUPT) { + terminate(false); + } + } + } + void handle_write(const boost::system::error_code& error) { if (error) { - std::cout << "write error" << std::endl; - } else { - std::cout << "write successful" << std::endl; - // end session - this->terminate(); + 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(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 == WRITING) { + m_write_state = IDLE; + } + + write(); } // terminate cleans up a connection and removes it from the endpoint's // connection list. - void terminate() { - // TODO: close socket cleanly + void terminate(bool failed_by_me) { + m_endpoint.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(); + + // TODO: figure out why this is really slow + m_dropped_by_me = socket_type::shutdown(); + + m_failed_by_me = failed_by_me; + + session::state::value old_state = m_state; + m_state = session::state::CLOSED; + + // If this was a websocket connection notify the application handler + // about the close using either on_fail or on_close + if (role_type::get_version() != -1) { + if (old_state == session::state::CONNECTING) { + m_endpoint.get_handler()->on_fail(type::shared_from_this()); + } else if (old_state == session::state::OPEN || + old_state == session::state::CLOSING) { + m_endpoint.get_handler()->on_close(type::shared_from_this()); + } else { + // if we were already closed something is wrong + } + log_close_result(); + } + // finally remove this connection from the endpoint's list. This will + // remove the last shared pointer to the connection held by WS++. m_endpoint.remove_connection(type::shared_from_this()); } + + // 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; + } + + void log_close_result() { + m_endpoint.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 fail_on_expire(const boost::system::error_code& error) { + if (error) { + if (error != boost::asio::error::operation_aborted) { + m_endpoint.elog().at(log::elevel::DEVEL) + << "fail_on_expire timer ended in unknown error" << log::endl; + terminate(false); + } + return; + } + m_endpoint.elog().at(log::elevel::DEVEL) + << "fail_on_expire timer expired" << log::endl; + terminate(true); + } + + boost::asio::streambuf& buffer() { + return m_buf; + } + protected: - endpoint_type& m_endpoint; - char m_data[512]; // temporary + endpoint_type& m_endpoint; + // Network resources boost::asio::streambuf m_buf; + boost::asio::deadline_timer m_timer; + // WebSocket connection state session::state::value m_state; + + // stuff that actually does the work + processor::ptr m_processor; + + + // Write queue + std::queue m_write_queue; + uint64_t m_write_buffer; + write_state 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; }; // connection related types that it and its policy classes need. diff --git a/src/http/parser.hpp b/src/http/parser.hpp index db56eda79e..cc452ab92d 100644 --- a/src/http/parser.hpp +++ b/src/http/parser.hpp @@ -92,6 +92,10 @@ public: void replace_header(const std::string &key,const std::string &val) { m_headers[key] = val; } + + void remove_header(const std::string &key) { + m_headers.erase(key); + } protected: bool parse_headers(std::istream& s) { std::string header; @@ -229,6 +233,8 @@ public: raw << version() << " " << m_status_code << " " << m_status_msg << "\r\n"; raw << raw_headers() << "\r\n"; + raw << m_body; + return raw.str(); } @@ -244,6 +250,19 @@ public: m_status_msg = msg; } + void set_body(const std::string& value) { + if (value.size() == 0) { + remove_header("Content-Length"); + m_body = ""; + return; + } + + std::stringstream foo; + foo << value.size(); + replace_header("Content-Length", foo.str()); + m_body = value; + } + status_code::value status_code() const { return m_status_code; } @@ -254,6 +273,7 @@ public: private: status_code::value m_status_code; std::string m_status_msg; + std::string m_body; }; } diff --git a/src/md5/md5.hpp b/src/md5/md5.hpp new file mode 100644 index 0000000000..3cecfa0cff --- /dev/null +++ b/src/md5/md5.hpp @@ -0,0 +1,49 @@ +/* + * 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_MD5_WRAPPER_HPP +#define WEBSOCKETPP_MD5_WRAPPER_HPP + +#include "md5.h" + +namespace websocketpp { + +// could be compiled separately +inline void md5_hash_string(const std::string& s); + char digest[17]; + + md5_state_t state; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)s.c_str(), 16); + md5_finish(&state, (md5_byte_t *)digest); + + digest[16] = '\0'; + return std::string(digest); +} + +#endif // WEBSOCKETPP_MD5_WRAPPER_HPP \ No newline at end of file diff --git a/src/network_utilities.cpp b/src/network_utilities.cpp index 05aafec63b..0e299233df 100644 --- a/src/network_utilities.cpp +++ b/src/network_utilities.cpp @@ -26,10 +26,6 @@ */ #include "network_utilities.hpp" -#include "websocket_constants.hpp" - -#include -#include "md5/md5.h" uint64_t htonll(uint64_t src) { static int typ = TYP_INIT; @@ -56,59 +52,6 @@ uint64_t ntohll(uint64_t src) { return htonll(src); } -std::string lookup_http_error_string(int code) { - switch (code) { - case 400: - return "Bad Request"; - case 401: - return "Unauthorized"; - case 403: - return "Forbidden"; - case 404: - return "Not Found"; - case 405: - return "Method Not Allowed"; - case 406: - return "Not Acceptable"; - case 407: - return "Proxy Authentication Required"; - case 408: - return "Request Timeout"; - case 409: - return "Conflict"; - case 410: - return "Gone"; - case 411: - return "Length Required"; - case 412: - return "Precondition Failed"; - case 413: - return "Request Entity Too Large"; - case 414: - return "Request-URI Too Long"; - case 415: - return "Unsupported Media Type"; - case 416: - return "Requested Range Not Satisfiable"; - case 417: - return "Expectation Failed"; - case 500: - return "Internal Server Error"; - case 501: - return "Not Implimented"; - case 502: - return "Bad Gateway"; - case 503: - return "Service Unavailable"; - case 504: - return "Gateway Timeout"; - case 505: - return "HTTP Version Not Supported"; - default: - return "Unknown"; - } -} - std::string lookup_ws_close_status_string(uint16_t code) { switch (code) { case 1000: @@ -136,103 +79,4 @@ std::string lookup_ws_close_status_string(uint16_t code) { default: return "Unknown"; } -} - -bool websocketpp::ws_uri::parse(const std::string& uri) { - boost::cmatch what; - static const boost::regex expression("(ws|wss)://([^/:\\[]+|\\[[0-9:]+\\])(:\\d{1,5})?(/[^#]*)?"); - - // TODO: should this split resource into path/query? - - if (boost::regex_match(uri.c_str(), what, expression)) { - if (what[1] == "wss") { - secure = true; - } else { - secure = false; - } - - host = what[2]; - - if (what[3] == "") { - port = (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT); - } else { - unsigned int t_port = atoi(std::string(what[3]).substr(1).c_str()); - - if (t_port > 65535) { - return false; - } - - port = atoi(std::string(what[3]).substr(1).c_str()); - } - - if (what[4] == "") { - resource = "/"; - } else { - resource = what[4]; - } - - return true; - } else { - return false; - } - -} - -std::string websocketpp::ws_uri::base() { - std::stringstream s; - - s << "ws" << (secure ? "s" : "") << "://" << host; - - if (port != (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT)) { - s << ":" << port; - } - - s << "/"; - return s.str(); -} - -std::string websocketpp::ws_uri::str() { - std::stringstream s; - - s << "ws" << (secure ? "s" : "") << "://" << host; - - if (port != (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT)) { - s << ":" << port; - } - - s << resource; - return s.str(); -} - -void md5_hash_string(char *string,char *hash) { - md5_state_t state; - - md5_init(&state); - md5_append(&state, (const md5_byte_t *)string, 16); - md5_finish(&state, (md5_byte_t *)hash); -} - -// Given a hybi 00 websocket key returns the 32 bit decoded value or 0 on error. -uint32_t decode_hybi_00_client_key(const std::string& key) { - int spaces = 0; - std::string digits = ""; - uint32_t num; - - // key2 - for (size_t i = 0; i < key.size(); i++) { - if (key[i] == ' ') { - spaces++; - } else if (key[i] >= '0' && key[i] <= '9') { - digits += key[i]; - } - } - - num = atoi(digits.c_str()); - if (spaces > 0 && num > 0) { - return htonl(num/spaces); - } else { - return 0; - } -} - - +} \ No newline at end of file diff --git a/src/network_utilities.hpp b/src/network_utilities.hpp index f79390af1f..f10f449c2d 100644 --- a/src/network_utilities.hpp +++ b/src/network_utilities.hpp @@ -30,9 +30,6 @@ #include #include -#include - - // http://www.viva64.com/en/k/0018/ // TODO: impliment stuff from here: @@ -45,27 +42,6 @@ uint64_t htonll(uint64_t src); uint64_t ntohll(uint64_t src); -std::string lookup_http_error_string(int code); std::string lookup_ws_close_status_string(uint16_t code); -namespace websocketpp { -struct ws_uri { - bool parse(const std::string& uri); - std::string base(); - std::string str(); - - bool secure; - std::string host; - uint16_t port; - std::string resource; -}; -} - -// calculate the md5 hash of string and store it in the 16 byte hash buffer -void md5_hash_string(char *string,char *hash); - - - -uint32_t decode_hybi_00_client_key(const std::string& key); - #endif // NETWORK_UTILITIES_HPP diff --git a/src/policy.cpp b/src/policy.cpp index 4f0b6671fa..a97f1cc68a 100644 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -15,15 +15,41 @@ sockets/ssl.hpp #include "roles/server.hpp" #include "sockets/ssl.hpp" -typedef websocketpp::endpoint endpoint_type; +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 { - void on_action() { - std::cout << "application_server_handler::on_action()" << std::endl; +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; } }; @@ -43,7 +69,7 @@ int main () { e.elog().set_level(websocketpp::log::elevel::ALL); - e.listen(9000); + e.listen(9002); //e.connect(); //e.public_api(); std::cout << std::endl; diff --git a/src/hybi_processor.hpp b/src/processors/hybi.hpp similarity index 81% rename from src/hybi_processor.hpp rename to src/processors/hybi.hpp index 30df3bee48..a235af6c73 100644 --- a/src/hybi_processor.hpp +++ b/src/processors/hybi.hpp @@ -25,21 +25,18 @@ * */ -#ifndef WEBSOCKET_HYBI_PROCESSOR_HPP -#define WEBSOCKET_HYBI_PROCESSOR_HPP +#ifndef WEBSOCKET_PROCESSOR_HYBI_HPP +#define WEBSOCKET_PROCESSOR_HYBI_HPP -#include "interfaces/protocol.hpp" +#include "processor.hpp" -#include "websocket_frame.hpp" +#include "../base64/base64.h" +#include "../sha1/sha1.h" -#include "network_utilities.hpp" -#include "http/parser.hpp" - -#include "base64/base64.h" -#include "sha1/sha1.h" +#include namespace websocketpp { -namespace protocol { +namespace processor { namespace hybi_state { enum value { @@ -50,9 +47,9 @@ namespace hybi_state { } template -class hybi_processor : public processor { +class hybi : public processor_base { public: - hybi_processor(bool secure,rng_policy &rng) : m_secure(secure),m_fragmented_opcode(frame::opcode::CONTINUATION),m_utf8_payload(new utf8_string()),m_binary_payload(new binary_string()),m_read_frame(rng),m_write_frame(rng) { + hybi(bool secure,rng_policy &rng) : m_secure(secure),m_fragmented_opcode(frame::opcode::CONTINUATION),m_utf8_payload(new utf8_string()),m_binary_payload(new binary_string()),m_read_frame(rng),m_write_frame(rng) { reset(); } @@ -128,35 +125,34 @@ public: } } - ws_uri get_uri(const http::parser::request& request) const { - ws_uri uri; - - uri.secure = m_secure; + uri get_uri(const http::parser::request& request) const { + uri connection_uri; + connection_uri.secure = m_secure; std::string h = request.header("Host"); size_t found = h.find(":"); if (found == std::string::npos) { - uri.host = h; - uri.port = (m_secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT); + connection_uri.host = h; + connection_uri.port = (m_secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT); } else { uint16_t p = atoi(h.substr(found+1).c_str()); if (p == 0) { throw(http::exception("Could not determine request uri. Check host header.",http::status_code::BAD_REQUEST)); } else { - uri.host = h.substr(0,found); - uri.port = p; + connection_uri.host = h.substr(0,found); + connection_uri.port = p; } } // TODO: check if get_uri is a full uri - uri.resource = request.uri(); + connection_uri.resource = request.uri(); - std::cout << "parsed uri: " << uri.str() << std::endl; + std::cout << "parsed uri: " << connection_uri.str() << std::endl; - return uri; + return connection_uri; } void handshake_response(const http::parser::request& request,http::parser::response& response) { @@ -201,57 +197,61 @@ public: m_read_frame.consume(s); if (m_read_frame.ready()) { - 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: - if (!utf8_validator::validate(m_read_frame.get_close_msg())) { - throw session::exception("Invalid UTF8",session::error::PAYLOAD_VIOLATION); - } - - m_opcode = frame::opcode::CLOSE; - m_close_code = m_read_frame.get_close_status(); - m_close_reason = m_read_frame.get_close_msg(); - - break; - case frame::opcode::PING: - case frame::opcode::PONG: - m_opcode = m_read_frame.get_opcode(); - extract_binary(m_control_payload); - break; - default: - throw session::exception("Invalid Opcode",session::error::PROTOCOL_VIOLATION); - break; - } - if (m_read_frame.get_fin()) { - m_state = hybi_state::DONE; - if (m_opcode == frame::opcode::TEXT) { - if (!m_validator.complete()) { - m_validator.reset(); - throw session::exception("Invalid UTF8",session::error::PAYLOAD_VIOLATION); - } - m_validator.reset(); - } - } - m_read_frame.reset(); + process_frame(); } - } catch (const frame::exception& e) { + } catch (const processor::exception& e) { if (m_read_frame.ready()) { m_read_frame.reset(); } - throw session::exception("Frame Error",session::error::PROTOCOL_VIOLATION); + throw e; } } } + void process_frame() { + 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: + if (!utf8_validator::validate(m_read_frame.get_close_msg())) { + throw processor::exception("Invalid UTF8",processor::error::PAYLOAD_VIOLATION); + } + + m_opcode = frame::opcode::CLOSE; + m_close_code = m_read_frame.get_close_status(); + m_close_reason = m_read_frame.get_close_msg(); + + break; + case frame::opcode::PING: + case frame::opcode::PONG: + m_opcode = m_read_frame.get_opcode(); + extract_binary(m_control_payload); + break; + default: + throw processor::exception("Invalid Opcode",processor::error::PROTOCOL_VIOLATION); + break; + } + if (m_read_frame.get_fin()) { + m_state = hybi_state::DONE; + if (m_opcode == frame::opcode::TEXT) { + if (!m_validator.complete()) { + m_validator.reset(); + throw processor::exception("Invalid UTF8",processor::error::PAYLOAD_VIOLATION); + } + m_validator.reset(); + } + } + m_read_frame.reset(); + } + // frame type handlers: void process_continuation() { if (m_fragmented_opcode == frame::opcode::BINARY) { @@ -260,7 +260,7 @@ public: extract_utf8(m_utf8_payload); } else if (m_fragmented_opcode == frame::opcode::CONTINUATION) { // got continuation frame without a message to continue. - throw session::exception("No message to continue.",session::error::PROTOCOL_VIOLATION); + throw processor::exception("No message to continue.",processor::error::PROTOCOL_VIOLATION); } else { // can't be here @@ -272,7 +272,7 @@ public: void process_text() { if (m_fragmented_opcode != frame::opcode::CONTINUATION) { - throw session::exception("New message started without closing previous.",session::error::PROTOCOL_VIOLATION); + throw processor::exception("New message started without closing previous.",processor::error::PROTOCOL_VIOLATION); } extract_utf8(m_utf8_payload); m_opcode = frame::opcode::TEXT; @@ -281,7 +281,7 @@ public: void process_binary() { if (m_fragmented_opcode != frame::opcode::CONTINUATION) { - throw session::exception("New message started without closing previous.",session::error::PROTOCOL_VIOLATION); + throw processor::exception("New message started without closing previous.",processor::error::PROTOCOL_VIOLATION); } m_opcode = frame::opcode::BINARY; m_fragmented_opcode = frame::opcode::BINARY; @@ -298,7 +298,7 @@ public: binary_string &msg = m_read_frame.get_payload(); if (!m_validator.decode(msg.begin(),msg.end())) { - throw session::exception("Invalid UTF8",session::error::PAYLOAD_VIOLATION); + throw processor::exception("Invalid UTF8",processor::error::PAYLOAD_VIOLATION); } dest->reserve(dest->size() + msg.size()); @@ -477,9 +477,8 @@ private: frame::parser m_read_frame; frame::parser m_write_frame; }; - -//typedef boost::shared_ptr hybi_processor_ptr; -} -} -#endif // WEBSOCKET_HYBI_PROCESSOR_HPP +} // namespace processor +} // namespace websocketpp + +#endif // WEBSOCKET_PROCESSOR_HYBI_HPP diff --git a/src/hybi_legacy_processor.hpp b/src/processors/hybi_legacy.hpp similarity index 100% rename from src/hybi_legacy_processor.hpp rename to src/processors/hybi_legacy.hpp diff --git a/src/interfaces/protocol.hpp b/src/processors/processor.hpp similarity index 73% rename from src/interfaces/protocol.hpp rename to src/processors/processor.hpp index 1b2f6892f4..fdcacb4490 100644 --- a/src/interfaces/protocol.hpp +++ b/src/processors/processor.hpp @@ -25,19 +25,60 @@ * */ -#ifndef WEBSOCKET_INTERFACE_FRAME_PARSER_HPP -#define WEBSOCKET_INTERFACE_FRAME_PARSER_HPP +#ifndef WEBSOCKET_PROCESSOR_HPP +#define WEBSOCKET_PROCESSOR_HPP +#include +#include + +namespace websocketpp { +namespace processor { + +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; +}; + +} // namespace processor +} // namespace websocketpp + #include "../http/parser.hpp" +#include "../uri.hpp" +#include "../websocket_frame.hpp" // TODO: clean up #include #include namespace websocketpp { -namespace protocol { - -class processor { +namespace processor { + +class processor_base { public: // validate client handshake // validate server handshake @@ -53,7 +94,7 @@ public: // Extracts client origin from a handshake request virtual std::string get_origin(const http::parser::request& request) const = 0; // Extracts client uri from a handshake request - virtual ws_uri get_uri(const http::parser::request& request) const = 0; + virtual uri get_uri(const http::parser::request& request) const = 0; // consume bytes, throw on exception virtual void consume(std::istream& s) = 0; @@ -88,8 +129,9 @@ public: }; -typedef boost::shared_ptr processor_ptr; +typedef boost::shared_ptr ptr; -} -} -#endif // WEBSOCKET_INTERFACE_FRAME_PARSER_HPP +} // namespace processor +} // namespace websocketpp + +#endif // WEBSOCKET_PROCESSOR_HPP diff --git a/src/roles/server.hpp b/src/roles/server.hpp index e6d3f83bd9..001cc0b4dd 100644 --- a/src/roles/server.hpp +++ b/src/roles/server.hpp @@ -29,12 +29,16 @@ #define WEBSOCKETPP_ROLE_SERVER_HPP #include "../endpoint.hpp" +#include "../processors/hybi.hpp" +#include "../rng/blank_rng.hpp" +#include #include #include #include #include +#include namespace websocketpp { namespace role { @@ -46,49 +50,281 @@ public: template class connection { public: + typedef connection type; + typedef endpoint endpoint_type; + // Valid always - std::string get_request_header(const std::string& key) const {} - std::string get_origin() const {} + unsigned int get_version() const { + return m_version; + } + std::string get_request_header(const std::string& key) const { + return m_request.header(key); + } + std::string get_origin() const { + return m_origin; + } + + // Information about the requested URI + bool get_secure() const { + return m_uri.secure; + } + std::string get_host() const { + return m_uri.host; + } + std::string get_resource() const { + return m_uri.resource; + } + unsigned short get_port() const { + return m_uri.port; + } // Valid for CONNECTING state - void add_response_header(const std::string& key, const std::string& value) {}; - void replace_response_header(const std::string& key, const std::string& e) {}; - const std::vector& get_subprotocols() const {}; - const std::vector& get_extensions() const {}; - void select_subprotocol(const std::string& value) {}; - void select_extension(const std::string& value) {}; + 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); + } + void remove_response_header(const std::string& key) { + m_response.remove_header(key); + } + + 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 std::invalid_argument("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 std::invalid_argument("Attempted to choose an extension not proposed by the client"); + } + + m_extensions.push_back(value); + } + + // Valid if get_version() returns -1 (ie this is an http connection) + void set_body(const std::string& value) { + if (m_connection.m_version != -1) { + // TODO: throw exception + throw std::invalid_argument("set_body called from invalid state"); + } + + m_response.set_body(value); + } protected: //connection(server& e) : m_endpoint(e) {} - connection(endpoint& e) : m_endpoint(e) {} + connection(endpoint& e) + : m_endpoint(e), + m_connection(static_cast< connection_type& >(*this)), + m_version(-1) {} // initializes the websocket connection void async_init() { - /*boost::asio::async_read_until( - m_endpoint.socket(), - m_buf, + boost::asio::async_read_until( + m_connection.get_socket(), + m_connection.buffer(), "\r\n\r\n", boost::bind( - &connection_type::handle_read_request, - connection_type::shared_from_this(), + &type::handle_read_request, + m_connection.shared_from_this(), // shared from this? boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred ) - );*/ + ); } + void handle_read_request(const boost::system::error_code& error, + std::size_t bytes_transferred) + { + if (error) { + // log error + m_endpoint.elog().at(log::elevel::ERROR) << "Error reading HTTP request. code: " << error << log::endl; + m_connection.terminate(false); + return; + } + + try { + std::istream request(&m_connection.buffer()); + + if (!m_request.parse_complete(request)) { + // not a valid HTTP request/response + throw http::exception("Recieved invalid HTTP Request",http::status_code::BAD_REQUEST); + } + + m_endpoint.alog().at(log::alevel::DEBUG_HANDSHAKE) << m_request.raw() << log::endl; + + std::string h = m_request.header("Upgrade"); + if (boost::ifind_first(h,"websocket")) { + h = m_request.header("Sec-WebSocket-Version"); + if (h == "") { + // websocket upgrade is present but version is not. + // assume hybi00 + 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)); + } + } + + // create a websocket processor + if (m_version == 0) { + m_response.add_header("Sec-WebSocket-Version","13, 8, 7"); + + throw(http::exception("Unsupported WebSocket version",http::status_code::BAD_REQUEST)); + } else if (m_version == 7 || + m_version == 8 || + m_version == 13) { + m_connection.m_processor = processor::ptr(new processor::hybi(false,m_rng)); + } else { + m_response.add_header("Sec-WebSocket-Version","13, 8, 7"); + + throw(http::exception("Unsupported WebSocket version",http::status_code::BAD_REQUEST)); + } + + m_connection.m_processor->validate_handshake(m_request); + m_origin = m_connection.m_processor->get_origin(m_request); + m_uri = m_connection.m_processor->get_uri(m_request); + + m_endpoint.get_handler()->validate(m_connection.shared_from_this()); + + m_response.set_status(http::status_code::SWITCHING_PROTOCOLS); + } else { + // continue as HTTP? + m_endpoint.get_handler()->http(m_connection.shared_from_this()); + + m_response.set_status(http::status_code::OK); + } + } catch (const http::exception& e) { + m_endpoint.elog().at(log::elevel::ERROR) << e.what() << log::endl; + m_response.set_status(e.m_error_code,e.m_error_msg); + m_response.set_body(e.m_body); + } + + write_response(); + } + void write_response() { + m_response.set_version("HTTP/1.1"); + + if (m_response.status_code() == http::status_code::SWITCHING_PROTOCOLS) { + // websocket response + m_connection.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-11-18"); + + std::string raw = m_response.raw(); + + m_endpoint.alog().at(log::alevel::DEBUG_HANDSHAKE) << raw << log::endl; + + boost::asio::async_write( + m_connection.get_socket(), + boost::asio::buffer(raw), + boost::bind( + &type::handle_write_response, + m_connection.shared_from_this(), + boost::asio::placeholders::error + ) + ); + } + + void handle_write_response(const boost::system::error_code& error) { + // TODO: handshake timer + + if (error) { + m_endpoint.elog().at(log::elevel::ERROR) << "Network error writing handshake respons. code: " << error << log::endl; + + m_connection.terminate(false); + return; + } + + log_open_result(); + + if (m_response.status_code() != http::status_code::SWITCHING_PROTOCOLS) { + if (m_version == -1) { + // if this was not a websocket connection, we have written + // the expected response and the connection can be closed. + } else { + // this was a websocket connection that ended in an error + m_endpoint.elog().at(log::elevel::ERROR) + << "Handshake ended with HTTP error: " + << m_response.status_code() << " " + << m_response.status_msg() << log::endl; + } + m_connection.terminate(true); + return; + } + + m_connection.m_state = session::state::OPEN; + + m_endpoint.get_handler()->on_open(m_connection.shared_from_this()); + + m_connection.handle_read_frame(boost::system::error_code()); + } + + void log_open_result() { + std::stringstream version; + version << "v" << m_version << " "; + + m_endpoint.alog().at(log::alevel::CONNECT) << (m_version == -1 ? "HTTP" : "WebSocket") << " Connection " + << m_connection.get_raw_socket().remote_endpoint() << " " + << (m_version == -1 ? "" : version.str()) + << (get_request_header("User-Agent") == "" ? "NULL" : get_request_header("User-Agent")) + << " " << m_uri.resource << " " << m_response.status_code() + << log::endl; + } private: endpoint& m_endpoint; + connection_type& m_connection; + + int m_version; + uri m_uri; + std::string m_origin; + std::vector m_requested_subprotocols; + std::vector m_requested_extensions; + std::string m_subprotocol; + std::vector m_extensions; http::parser::request m_request; http::parser::response m_response; + blank_rng m_rng; }; - // handler interface callback class - class handler { - virtual void on_action() = 0; - }; + class handler; typedef boost::shared_ptr handler_ptr; @@ -97,7 +333,27 @@ public: typedef endpoint endpoint_type; typedef typename endpoint_traits::connection_ptr connection_ptr; + + // handler interface callback class + class handler { + public: + typedef connection_ptr connection_ptr; + // Required + virtual void validate(connection_ptr connection) = 0; + virtual void on_open(connection_ptr connection) = 0; + virtual void on_close(connection_ptr connection) = 0; + + virtual void on_message(connection_ptr connection,utf8_string_ptr) = 0; + virtual void on_message(connection_ptr connection,binary_string_ptr) = 0; + + // Optional + virtual bool on_ping(connection_ptr connection,binary_string_ptr) {return true;} + virtual void on_pong(connection_ptr connection,binary_string_ptr) {} + virtual void http(connection_ptr connection) {} + virtual void on_fail(connection_ptr connection) {} + }; + server(boost::asio::io_service& m,handler_ptr h) : m_ws_endpoint(static_cast< endpoint_type& >(*this)), m_handler(h), diff --git a/src/sha1/sha1.cpp b/src/sha1/sha1.cpp index fdcbdcc0b4..1c25d46286 100755 --- a/src/sha1/sha1.cpp +++ b/src/sha1/sha1.cpp @@ -40,6 +40,8 @@ #include "sha1.h" +using websocketpp::SHA1; + /* * SHA1 * diff --git a/src/sha1/sha1.h b/src/sha1/sha1.h index c0efa1c944..eae71aaf77 100755 --- a/src/sha1/sha1.h +++ b/src/sha1/sha1.h @@ -24,9 +24,10 @@ #ifndef _SHA1_H_ #define _SHA1_H_ +namespace websocketpp { + class SHA1 { - public: SHA1(); @@ -86,4 +87,6 @@ class SHA1 }; -#endif +} // namespace websocketpp + +#endif // _SHA1_H_ diff --git a/src/sockets/plain.hpp b/src/sockets/plain.hpp index 37d366fbf5..f6576e42ac 100644 --- a/src/sockets/plain.hpp +++ b/src/sockets/plain.hpp @@ -49,6 +49,15 @@ public: template class connection { public: + // should these two be public or protected. If protected, how? + boost::asio::ip::tcp::socket& get_raw_socket() { + return m_socket; + } + + boost::asio::ip::tcp::socket& get_socket() { + return m_socket; + } + protected: connection(plain& e) : m_socket(e.get_io_service()) {} void async_init(socket_init_callback callback) { @@ -56,12 +65,15 @@ public: callback(boost::system::error_code()); } - boost::asio::ip::tcp::socket& get_raw_socket() { - return m_socket; - } - - boost::asio::ip::tcp::socket& get_socket() { - return m_socket; + bool shutdown() { + boost::system::error_code ignored_ec; + m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both,ignored_ec); + + if (ignored_ec) { + return false; + } else { + return true; + } } private: boost::asio::ip::tcp::socket m_socket; diff --git a/src/sockets/ssl.hpp b/src/sockets/ssl.hpp index 82530f61a6..1283506d99 100644 --- a/src/sockets/ssl.hpp +++ b/src/sockets/ssl.hpp @@ -72,6 +72,15 @@ public: template class connection { public: + // should these two be public or protected. If protected, how? + ssl_socket::lowest_layer_type& get_raw_socket() { + return m_socket.lowest_layer(); + } + + ssl_socket& get_socket() { + return m_socket; + } + protected: connection(ssl& e) : m_socket(e.get_io_service(),e.get_context()),m_endpoint(e) {} void async_init(boost::function callback) { @@ -98,12 +107,15 @@ public: callback(error); } - ssl_socket::lowest_layer_type& get_raw_socket() { - return m_socket.lowest_layer(); - } - - ssl_socket& get_socket() { - return m_socket; + bool shutdown() { + boost::system::error_code ignored_ec; + m_socket.shutdown(ignored_ec); + + if (ignored_ec) { + return false; + } else { + return true; + } } private: ssl_socket m_socket; @@ -119,9 +131,9 @@ protected: boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); m_context.set_password_callback(boost::bind(&type::get_password, this)); - m_context.use_certificate_chain_file("/Users/zaphoyd/Documents/ZS/websocketpp/src/ssl/server.pem"); - m_context.use_private_key_file("/Users/zaphoyd/Documents/ZS/websocketpp/src/ssl/server.pem", boost::asio::ssl::context::pem); - m_context.use_tmp_dh_file("/Users/zaphoyd/Documents/ZS/websocketpp/src/ssl/dh512.pem"); + m_context.use_certificate_chain_file("/Users/zaphoyd/Documents/websocketpp/src/ssl/server.pem"); + m_context.use_private_key_file("/Users/zaphoyd/Documents/websocketpp/src/ssl/server.pem", boost::asio::ssl::context::pem); + m_context.use_tmp_dh_file("/Users/zaphoyd/Documents/websocketpp/src/ssl/dh512.pem"); } catch (std::exception& e) { std::cout << e.what() << std::endl; } diff --git a/src/uri.hpp b/src/uri.hpp new file mode 100644 index 0000000000..194347467f --- /dev/null +++ b/src/uri.hpp @@ -0,0 +1,112 @@ +/* + * 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_URI_HPP +#define WEBSOCKETPP_URI_HPP + +#include "common.hpp" + +#include +#include +#include + +namespace websocketpp { + +struct uri { + bool parse(const std::string& uri) { + boost::cmatch what; + static const boost::regex expression("(ws|wss)://([^/:\\[]+|\\[[0-9:]+\\])(:\\d{1,5})?(/[^#]*)?"); + + // TODO: should this split resource into path/query? + + if (boost::regex_match(uri.c_str(), what, expression)) { + if (what[1] == "wss") { + secure = true; + } else { + secure = false; + } + + host = what[2]; + + if (what[3] == "") { + port = (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT); + } else { + unsigned int t_port = atoi(std::string(what[3]).substr(1).c_str()); + + if (t_port > 65535) { + return false; + } + + port = atoi(std::string(what[3]).substr(1).c_str()); + } + + if (what[4] == "") { + resource = "/"; + } else { + resource = what[4]; + } + + return true; + } else { + return false; + } + } + std::string base() { + std::stringstream s; + + s << "ws" << (secure ? "s" : "") << "://" << host; + + if (port != (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT)) { + s << ":" << port; + } + + s << "/"; + return s.str(); + + } + std::string str() { + std::stringstream s; + + s << "ws" << (secure ? "s" : "") << "://" << host; + + if (port != (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT)) { + s << ":" << port; + } + + s << resource; + return s.str(); + } + + bool secure; + std::string host; + uint16_t port; + std::string resource; +}; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_URI_HPP diff --git a/src/websocket_frame.hpp b/src/websocket_frame.hpp index 61d37c3194..45d06a9f51 100644 --- a/src/websocket_frame.hpp +++ b/src/websocket_frame.hpp @@ -28,10 +28,54 @@ #ifndef WEBSOCKET_FRAME_HPP #define WEBSOCKET_FRAME_HPP -#include "network_utilities.hpp" -#include "websocket_constants.hpp" +namespace websocketpp { +namespace frame { + // 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 + } +} // namespace frame +} // namespace websocketpp -#include "websocket_server.hpp" // for server error. this should be gotten rid of +#include "network_utilities.hpp" +#include "processor.hpp" #include "utf8_validator/utf8_validator.hpp" #if defined(WIN32) @@ -67,27 +111,7 @@ private: boost::random::variate_generator > m_gen; } - */ - -// Exception classes -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; -}; + */ template class parser { @@ -212,12 +236,12 @@ public: /*if (s.gcount() == 0) { throw frame_error("consume read zero bytes",FERR_FATAL_SESSION_ERROR); }*/ - } catch (const exception& e) { + } catch (const processor::exception& e) { // After this point all non-close frames must be considered garbage, // including the current one. Reset it and put the reading frame into // a recovery state. if (m_degraded == true) { - throw exception("An error occurred while trying to gracefully recover from a less serious frame error.",error::FATAL_SESSION_ERROR); + throw processor::exception("An error occurred while trying to gracefully recover from a less serious frame error.",processor::error::FATAL_ERROR); } else { reset(); m_state = STATE_RECOVERY; @@ -253,7 +277,7 @@ public: char* get_masking_key() { if (m_state != STATE_READY) { - throw exception("attempted to get masking_key before reading full header"); + throw processor::exception("attempted to get masking_key before reading full header"); } return m_header[get_header_len()-4]; } @@ -307,16 +331,16 @@ public: return frame::opcode::value(m_header[0] & BPB0_OPCODE); } void set_opcode(opcode::value op) { - if (frame::opcode::reserved(op)) { - throw exception("reserved opcode",error::PROTOCOL_VIOLATION); + if (opcode::reserved(op)) { + throw processor::exception("reserved opcode",processor::error::PROTOCOL_VIOLATION); } - if (frame::opcode::invalid(op)) { - throw exception("invalid opcode",error::PROTOCOL_VIOLATION); + if (opcode::invalid(op)) { + throw processor::exception("invalid opcode",processor::error::PROTOCOL_VIOLATION); } if (is_control() && get_basic_size() > limits::PAYLOAD_SIZE_BASIC) { - throw exception("control frames can't have large payloads",error::PROTOCOL_VIOLATION); + throw processor::exception("control frames can't have large payloads",processor::error::PROTOCOL_VIOLATION); } m_header[0] &= (0xFF ^ BPB0_OPCODE); // clear op bits @@ -370,7 +394,7 @@ public: uint32_t codep = 0; validate_utf8(&state,&codep,2); if (state != utf8_validator::UTF8_ACCEPT) { - throw exception("Invalid UTF-8 Data",error::PAYLOAD_VIOLATION); + throw processor::exception("Invalid UTF-8 Data",processor::error::PAYLOAD_VIOLATION); } return std::string(m_payload.begin()+2,m_payload.end()); } else { @@ -394,12 +418,12 @@ public: } void set_payload_helper(size_t s) { if (s > max_payload_size) { - throw exception("requested payload is over implimentation defined limit",error::MESSAGE_TOO_BIG); + throw processor::exception("requested payload is over implimentation defined limit",processor::error::MESSAGE_TOO_BIG); } // limits imposed by the websocket spec if (is_control() && s > limits::PAYLOAD_SIZE_BASIC) { - throw exception("control frames can't have large payloads",error::PROTOCOL_VIOLATION); + throw processor::exception("control frames can't have large payloads",processor::error::PROTOCOL_VIOLATION); } if (s <= limits::PAYLOAD_SIZE_BASIC) { @@ -415,7 +439,7 @@ public: m_header[1] = BASIC_PAYLOAD_64BIT_CODE; *reinterpret_cast(&m_header[BASIC_HEADER_LENGTH]) = htonll(s); } else { - throw exception("payload size limit is 63 bits",error::PROTOCOL_VIOLATION); + throw processor::exception("payload size limit is 63 bits",processor::error::PROTOCOL_VIOLATION); } m_payload.resize(s); @@ -426,13 +450,13 @@ public: if (close::status::invalid(status)) { std::stringstream err; err << "Status code " << status << " is invalid"; - throw exception(err.str()); + throw processor::exception(err.str()); } if (close::status::reserved(status)) { std::stringstream err; err << "Status code " << status << " is reserved"; - throw exception(err.str()); + throw processor::exception(err.str()); } m_payload.resize(2+message.size()); @@ -450,7 +474,7 @@ public: } bool is_control() const { - return (frame::opcode::is_control(get_opcode())); + return (opcode::is_control(get_opcode())); } std::string print_frame() const { @@ -490,14 +514,14 @@ public: // reinterpret the second two bytes as a 16 bit integer in network // byte order. Convert to host byte order and store locally. payload_size = ntohs(*( - reinterpret_cast(&m_header[BASIC_HEADER_LENGTH]) - )); + reinterpret_cast(&m_header[BASIC_HEADER_LENGTH]) + )); if (payload_size < s) { std::stringstream err; err << "payload length not minimally encoded. Using 16 bit form for payload size: " << payload_size; m_bytes_needed = payload_size; - throw exception(err.str(),error::PROTOCOL_VIOLATION); + throw processor::exception(err.str(),processor::error::PROTOCOL_VIOLATION); } mask_index += 2; @@ -505,19 +529,19 @@ public: // reinterpret the second eight bytes as a 64 bit integer in // network byte order. Convert to host byte order and store. payload_size = ntohll(*( - reinterpret_cast(&m_header[BASIC_HEADER_LENGTH]) - )); + reinterpret_cast(&m_header[BASIC_HEADER_LENGTH]) + )); if (payload_size <= limits::PAYLOAD_SIZE_EXTENDED) { m_bytes_needed = payload_size; - throw exception("payload length not minimally encoded", - error::PROTOCOL_VIOLATION); + throw processor::exception("payload length not minimally encoded", + processor::error::PROTOCOL_VIOLATION); } mask_index += 8; } else { // TODO: shouldn't be here how to handle? - throw exception("invalid get_basic_size in process_extended_header"); + throw processor::exception("invalid get_basic_size in process_extended_header"); } if (get_masked() == 0) { @@ -586,29 +610,29 @@ public: using utf8_validator::decode; if (decode(state,codep,m_payload[i]) == utf8_validator::UTF8_REJECT) { - throw exception("Invalid UTF-8 Data",error::PAYLOAD_VIOLATION); + throw processor::exception("Invalid UTF-8 Data",processor::error::PAYLOAD_VIOLATION); } } } void validate_basic_header() const { // check for control frame size if (is_control() && get_basic_size() > limits::PAYLOAD_SIZE_BASIC) { - throw exception("Control Frame is too large",error::PROTOCOL_VIOLATION); + throw processor::exception("Control Frame is too large",processor::error::PROTOCOL_VIOLATION); } // check for reserved bits if (get_rsv1() || get_rsv2() || get_rsv3()) { - throw exception("Reserved bit used",error::PROTOCOL_VIOLATION); + throw processor::exception("Reserved bit used",processor::error::PROTOCOL_VIOLATION); } // check for reserved opcodes if (opcode::reserved(get_opcode())) { - throw exception("Reserved opcode used",error::PROTOCOL_VIOLATION); + throw processor::exception("Reserved opcode used",processor::error::PROTOCOL_VIOLATION); } // check for fragmented control message if (is_control() && !get_fin()) { - throw exception("Fragmented control message",error::PROTOCOL_VIOLATION); + throw processor::exception("Fragmented control message",processor::error::PROTOCOL_VIOLATION); } } @@ -638,106 +662,6 @@ private: rng_policy& m_rng; }; -class hybi_00_parser { -public: - // TODO: not hardcode this - static const uint64_t max_payload_size = 100000000; // 100MB - - hybi_00_parser() : m_state(STATE_READ_TYPE) { - reset(); - } - - bool ready() const { - return m_state == STATE_READY; - } - uint64_t get_bytes_needed() const { - return 1; - } - void reset() { - m_state = STATE_READ_TYPE; - } - - void set_fin(bool fin) {} - void set_opcode(opcode::value op) { - if (op != frame::opcode::TEXT) { - // TODO: what happens when you try to send non-text to a hybi_00 frame - } - } - - // hybi_00 frames are UTF-8 text only. - opcode::value get_opcode() const { - return frame::opcode::TEXT; - } - - void set_payload(const std::string source) { - if (source.size() > max_payload_size) { - throw exception("requested payload is over implimentation defined limit",error::MESSAGE_TOO_BIG); - } - - // TODO: utf8 validation? - - m_payload.resize(source.size()+2); - - m_payload[0] = 0x00; - std::copy(source.begin(),source.end(),m_payload.begin()+1); - m_payload[m_payload.size()-1] = 0xFF; - } - - void set_masked(bool masked) {} - - void process_payload() {} - - bool is_control() const { - return false; - } - - std::vector &get_payload() { - return m_payload; - } - - char* get_header() { - // TODO: this might be a problem - return NULL; - } - - unsigned int get_header_len() const { - return 0; - } - - void validate_utf8(uint32_t* state,uint32_t* codep,size_t offset = 0) const { - for (size_t i = offset; i < m_payload.size(); i++) { - using utf8_validator::decode; - - if (decode(state,codep,m_payload[i]) == utf8_validator::UTF8_REJECT) { - throw exception("Invalid UTF-8 Data",error::PAYLOAD_VIOLATION); - } - } - } - - // Method invariant: One of the following must always be true even in the case - // of exceptions. - // - m_bytes_needed > 0 - // - m-state = STATE_READY - void consume(std::istream &s) { - // read a byte. if it is 0x00 then read payload bytes until 0xFF. - // otherwise it may be another type of frame. Test whether or not - // hybi00 clients actually send them. - - // should do streaming utf8 validation and throw an exception on error. - } -private: - static const uint8_t STATE_READ_TYPE = 1; - static const uint8_t STATE_READ_LENGTH = 2; - static const uint8_t STATE_READ_PAYLOAD = 3; - static const uint8_t STATE_READY = 4; - - uint8_t m_state; - std::vector m_payload; -}; - - - - } } diff --git a/websocketpp.xcodeproj/project.pbxproj b/websocketpp.xcodeproj/project.pbxproj index bdb1a5b02e..91631d1ad7 100644 --- a/websocketpp.xcodeproj/project.pbxproj +++ b/websocketpp.xcodeproj/project.pbxproj @@ -9,11 +9,11 @@ /* Begin PBXBuildFile section */ B613878F145CA61300ED9B19 /* libboost_date_time.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CBE1434AF6A0029A1B1 /* libboost_date_time.dylib */; }; B6138790145CA61C00ED9B19 /* libboost_program_options.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6FE8CEB145A0F1900B32547 /* libboost_program_options.dylib */; }; + B62A5A7214775ECF005F9EB0 /* libwebsocketpp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1C721434A8280029A1B1 /* libwebsocketpp.dylib */; }; B68288871437460E002BA48B /* chat_client_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6828875143745DA002BA48B /* chat_client_handler.cpp */; }; B68288881437460E002BA48B /* chat_client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6828877143745DA002BA48B /* chat_client.cpp */; }; B682888914374617002BA48B /* libwebsocketpp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1C721434A8280029A1B1 /* libwebsocketpp.dylib */; }; B682888B14374623002BA48B /* libboost_system.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B682888A14374623002BA48B /* libboost_system.dylib */; }; - B682888D1437464A002BA48B /* libboost_random.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B682888C1437464A002BA48B /* libboost_random.dylib */; }; B682888F14374689002BA48B /* libboost_thread.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B682888E14374689002BA48B /* libboost_thread.dylib */; }; B6BE76EA144EF53000716A77 /* websocket_endpoint.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6BE76E9144EF53000716A77 /* websocket_endpoint.hpp */; }; B6BE76EB144EF53000716A77 /* websocket_endpoint.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6BE76E9144EF53000716A77 /* websocket_endpoint.hpp */; }; @@ -34,41 +34,28 @@ B6DF1C8D1434AC330029A1B1 /* sha1.h in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C891434AC330029A1B1 /* sha1.h */; }; B6DF1C901434AC3E0029A1B1 /* utf8_validator.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C8F1434AC3E0029A1B1 /* utf8_validator.hpp */; }; B6DF1C911434AC3E0029A1B1 /* utf8_validator.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C8F1434AC3E0029A1B1 /* utf8_validator.hpp */; }; - B6DF1CA01434AC470029A1B1 /* websocket_client_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C921434AC470029A1B1 /* websocket_client_session.cpp */; }; B6DF1CA21434AC470029A1B1 /* websocket_client_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C931434AC470029A1B1 /* websocket_client_session.hpp */; }; B6DF1CA31434AC470029A1B1 /* websocket_client_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C931434AC470029A1B1 /* websocket_client_session.hpp */; }; - B6DF1CA41434AC470029A1B1 /* websocket_client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C941434AC470029A1B1 /* websocket_client.cpp */; }; B6DF1CA61434AC470029A1B1 /* websocket_client.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C951434AC470029A1B1 /* websocket_client.hpp */; }; B6DF1CA71434AC470029A1B1 /* websocket_client.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C951434AC470029A1B1 /* websocket_client.hpp */; }; B6DF1CA81434AC470029A1B1 /* websocket_connection_handler.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C961434AC470029A1B1 /* websocket_connection_handler.hpp */; settings = {ATTRIBUTES = (Public, ); }; }; B6DF1CA91434AC470029A1B1 /* websocket_connection_handler.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C961434AC470029A1B1 /* websocket_connection_handler.hpp */; settings = {ATTRIBUTES = (Public, ); }; }; - B6DF1CAA1434AC470029A1B1 /* websocket_frame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C971434AC470029A1B1 /* websocket_frame.cpp */; }; - B6DF1CAB1434AC470029A1B1 /* websocket_frame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C971434AC470029A1B1 /* websocket_frame.cpp */; }; B6DF1CAC1434AC470029A1B1 /* websocket_frame.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C981434AC470029A1B1 /* websocket_frame.hpp */; }; B6DF1CAD1434AC470029A1B1 /* websocket_frame.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C981434AC470029A1B1 /* websocket_frame.hpp */; }; - B6DF1CAE1434AC470029A1B1 /* websocket_server_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C991434AC470029A1B1 /* websocket_server_session.cpp */; }; B6DF1CB01434AC470029A1B1 /* websocket_server_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9A1434AC470029A1B1 /* websocket_server_session.hpp */; }; B6DF1CB11434AC470029A1B1 /* websocket_server_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9A1434AC470029A1B1 /* websocket_server_session.hpp */; }; - B6DF1CB21434AC470029A1B1 /* websocket_server.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C9B1434AC470029A1B1 /* websocket_server.cpp */; }; - B6DF1CB31434AC470029A1B1 /* websocket_server.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C9B1434AC470029A1B1 /* websocket_server.cpp */; }; B6DF1CB41434AC470029A1B1 /* websocket_server.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9C1434AC470029A1B1 /* websocket_server.hpp */; }; B6DF1CB51434AC470029A1B1 /* websocket_server.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9C1434AC470029A1B1 /* websocket_server.hpp */; }; - B6DF1CB61434AC470029A1B1 /* websocket_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C9D1434AC470029A1B1 /* websocket_session.cpp */; }; - B6DF1CB71434AC470029A1B1 /* websocket_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C9D1434AC470029A1B1 /* websocket_session.cpp */; }; B6DF1CB81434AC470029A1B1 /* websocket_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9E1434AC470029A1B1 /* websocket_session.hpp */; }; B6DF1CB91434AC470029A1B1 /* websocket_session.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9E1434AC470029A1B1 /* websocket_session.hpp */; }; B6DF1CBA1434AC470029A1B1 /* websocketpp.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9F1434AC470029A1B1 /* websocketpp.hpp */; settings = {ATTRIBUTES = (Public, ); }; }; B6DF1CBB1434AC470029A1B1 /* websocketpp.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6DF1C9F1434AC470029A1B1 /* websocketpp.hpp */; settings = {ATTRIBUTES = (Public, ); }; }; - B6DF1CC11434AF6A0029A1B1 /* libboost_date_time.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CBE1434AF6A0029A1B1 /* libboost_date_time.dylib */; }; - B6DF1CC21434AF6A0029A1B1 /* libboost_regex.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CBF1434AF6A0029A1B1 /* libboost_regex.dylib */; }; - B6DF1CC31434AF6A0029A1B1 /* libboost_system.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CC01434AF6A0029A1B1 /* libboost_system.dylib */; }; B6DF1CC41434AF9E0029A1B1 /* network_utilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1C791434AB740029A1B1 /* network_utilities.cpp */; }; B6DF1CDB1435EDCE0029A1B1 /* echo_server.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1CCA1435ED760029A1B1 /* echo_server.cpp */; }; B6DF1CDC1435EDCE0029A1B1 /* echo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6DF1CCB1435ED760029A1B1 /* echo.cpp */; }; B6DF1CDE1435EDF00029A1B1 /* libwebsocketpp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1C721434A8280029A1B1 /* libwebsocketpp.dylib */; }; B6DF1CE21435F1860029A1B1 /* libboost_system.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CE11435F1860029A1B1 /* libboost_system.dylib */; }; B6DF1CE41435F8250029A1B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1CE31435F8250029A1B1 /* Foundation.framework */; }; - B6FE8CEC145A0F1900B32547 /* libboost_program_options.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6FE8CEB145A0F1900B32547 /* libboost_program_options.dylib */; }; B6FE8D06145AFF5F00B32547 /* websocket_constants.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6FE8D05145AFF5F00B32547 /* websocket_constants.hpp */; }; B6FE8D07145AFF5F00B32547 /* websocket_constants.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B6FE8D05145AFF5F00B32547 /* websocket_constants.hpp */; }; B6FE8D181468707200B32547 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = B6FE8D1614686A8500B32547 /* md5.c */; }; @@ -168,6 +155,12 @@ B62A5A581473EBF1005F9EB0 /* plain.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = plain.hpp; path = src/sockets/plain.hpp; sourceTree = ""; }; B62A5A591473EBF1005F9EB0 /* ssl.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ssl.hpp; path = src/sockets/ssl.hpp; sourceTree = ""; }; B62A5A6114755443005F9EB0 /* socket_base.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = socket_base.hpp; path = src/sockets/socket_base.hpp; sourceTree = ""; }; + B62A5A6B147748B2005F9EB0 /* hybi.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = hybi.hpp; sourceTree = ""; }; + B62A5A6C147748B2005F9EB0 /* hybi_legacy.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = hybi_legacy.hpp; sourceTree = ""; }; + B62A5A6D147748B2005F9EB0 /* processor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = processor.hpp; sourceTree = ""; }; + B62A5A6F14774CD5005F9EB0 /* uri.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = uri.hpp; path = src/uri.hpp; sourceTree = ""; }; + B62A5A7014774F08005F9EB0 /* md5.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = md5.hpp; path = src/md5/md5.hpp; sourceTree = ""; }; + B62A5A71147759EA005F9EB0 /* common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = common.hpp; path = src/common.hpp; sourceTree = ""; }; B6828875143745DA002BA48B /* chat_client_handler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = chat_client_handler.cpp; path = examples/chat_client/chat_client_handler.cpp; sourceTree = ""; }; B6828876143745DA002BA48B /* chat_client_handler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = chat_client_handler.hpp; path = examples/chat_client/chat_client_handler.hpp; sourceTree = ""; }; B6828877143745DA002BA48B /* chat_client.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = chat_client.cpp; path = examples/chat_client/chat_client.cpp; sourceTree = ""; }; @@ -261,11 +254,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B6FE8CEC145A0F1900B32547 /* libboost_program_options.dylib in Frameworks */, - B682888D1437464A002BA48B /* libboost_random.dylib in Frameworks */, - B6DF1CC11434AF6A0029A1B1 /* libboost_date_time.dylib in Frameworks */, - B6DF1CC21434AF6A0029A1B1 /* libboost_regex.dylib in Frameworks */, - B6DF1CC31434AF6A0029A1B1 /* libboost_system.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -285,6 +273,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + B62A5A7214775ECF005F9EB0 /* libwebsocketpp.dylib in Frameworks */, B6FE8D6614757D6400B32547 /* libboost_date_time.dylib in Frameworks */, B6FE8D5E14730B2200B32547 /* libssl.dylib in Frameworks */, B6FE8D5C14730B1A00B32547 /* libcrypto.dylib in Frameworks */, @@ -342,6 +331,17 @@ name = sockets; sourceTree = ""; }; + B62A5A6A147748B2005F9EB0 /* processors */ = { + isa = PBXGroup; + children = ( + B62A5A6B147748B2005F9EB0 /* hybi.hpp */, + B62A5A6C147748B2005F9EB0 /* hybi_legacy.hpp */, + B62A5A6D147748B2005F9EB0 /* processor.hpp */, + ); + name = processors; + path = src/processors; + sourceTree = ""; + }; B6CF18121437C370009295BE /* echo_client */ = { isa = PBXGroup; children = ( @@ -396,13 +396,15 @@ B6FE8D30146C721200B32547 /* hybi_processor.hpp */, B6FE8D2E146C2C9500B32547 /* hybi_legacy_processor.hpp */, B62A5A2F1469512A005F9EB0 /* interfaces */, - B6FE8D1414686A6D00B32547 /* md5 */, B61387B51462B34400ED9B19 /* logger */, B61387A4145D847A00ED9B19 /* http */, B6FE8D09145B0F7400B32547 /* rng */, B6FE8D05145AFF5F00B32547 /* websocket_constants.hpp */, + B62A5A6A147748B2005F9EB0 /* processors */, B6FE8D4914730AA600B32547 /* policy.cpp */, + B62A5A71147759EA005F9EB0 /* common.hpp */, B62A5A511473EBB0005F9EB0 /* connection.hpp */, + B62A5A6F14774CD5005F9EB0 /* uri.hpp */, B62A5A521473EBB0005F9EB0 /* endpoint.hpp */, B6DF1C921434AC470029A1B1 /* websocket_client_session.cpp */, B6DF1C931434AC470029A1B1 /* websocket_client_session.hpp */, @@ -420,6 +422,7 @@ B6DF1C9D1434AC470029A1B1 /* websocket_session.cpp */, B6DF1C9E1434AC470029A1B1 /* websocket_session.hpp */, B6DF1C9F1434AC470029A1B1 /* websocketpp.hpp */, + B6FE8D1414686A6D00B32547 /* md5 */, B6DF1C8E1434AC3E0029A1B1 /* utf8_validator */, B6DF1C871434ABF30029A1B1 /* sha1 */, B6DF1C801434ABE20029A1B1 /* base64 */, @@ -538,6 +541,7 @@ children = ( B6FE8D1614686A8500B32547 /* md5.c */, B6FE8D1714686A8500B32547 /* md5.h */, + B62A5A7014774F08005F9EB0 /* md5.hpp */, ); name = md5; sourceTree = ""; @@ -762,12 +766,6 @@ B6DF1C7A1434AB740029A1B1 /* network_utilities.cpp in Sources */, B6DF1C831434ABE20029A1B1 /* base64.cpp in Sources */, B6DF1C8A1434AC330029A1B1 /* sha1.cpp in Sources */, - B6DF1CA01434AC470029A1B1 /* websocket_client_session.cpp in Sources */, - B6DF1CA41434AC470029A1B1 /* websocket_client.cpp in Sources */, - B6DF1CAA1434AC470029A1B1 /* websocket_frame.cpp in Sources */, - B6DF1CAE1434AC470029A1B1 /* websocket_server_session.cpp in Sources */, - B6DF1CB21434AC470029A1B1 /* websocket_server.cpp in Sources */, - B6DF1CB61434AC470029A1B1 /* websocket_session.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -779,9 +777,6 @@ B6DF1CC41434AF9E0029A1B1 /* network_utilities.cpp in Sources */, B6DF1C841434ABE20029A1B1 /* base64.cpp in Sources */, B6DF1C8B1434AC330029A1B1 /* sha1.cpp in Sources */, - B6DF1CAB1434AC470029A1B1 /* websocket_frame.cpp in Sources */, - B6DF1CB31434AC470029A1B1 /* websocket_server.cpp in Sources */, - B6DF1CB71434AC470029A1B1 /* websocket_session.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };