From 37d5f968d0e22941f9976cac125e7ff2a66cb738 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Tue, 4 Oct 2011 08:14:04 -0500 Subject: [PATCH 01/40] adds limit to network read buffer (to prevent DoS attacks), refactors client handshake reading to leave non-handshake data in the read buffer --- src/websocket_client.cpp | 4 +- src/websocket_client_session.cpp | 74 +++++++++++++++----------------- src/websocket_client_session.hpp | 3 +- src/websocket_server.cpp | 4 +- src/websocket_server_session.cpp | 6 ++- src/websocket_server_session.hpp | 3 +- src/websocket_session.cpp | 6 +-- src/websocket_session.hpp | 3 +- 8 files changed, 53 insertions(+), 50 deletions(-) diff --git a/src/websocket_client.cpp b/src/websocket_client.cpp index 69bdd1f89f..ad5446fcb7 100644 --- a/src/websocket_client.cpp +++ b/src/websocket_client.cpp @@ -46,11 +46,13 @@ client::client(boost::asio::io_service& 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_def_con_handler, + m_max_message_size*2 ) ); m_state = CLIENT_STATE_INITIALIZED; diff --git a/src/websocket_client_session.cpp b/src/websocket_client_session.cpp index 456a01e505..21045a5208 100644 --- a/src/websocket_client_session.cpp +++ b/src/websocket_client_session.cpp @@ -47,8 +47,9 @@ using websocketpp::client_session; client_session::client_session (websocketpp::client_ptr c, boost::asio::io_service& io_service, - websocketpp::connection_handler_ptr defc) - : session(io_service,defc),m_client(c) {} + 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 @@ -124,56 +125,49 @@ void client_session::read_handshake() { void client_session::handle_read_handshake(const boost::system::error_code& e, std::size_t bytes_transferred) { // parse server handshake - - - // read handshake and set local state (or pass to write_handshake) - std::ostringstream line; - line << &m_buf; - m_raw_server_handshake += line.str(); - - m_buf << m_raw_server_handshake.substr(bytes_transferred); - - std::stringstream foo; - - foo << "data size: " << m_raw_server_handshake.size() - << " bytes transferred" << bytes_transferred; - - m_client->access_log(foo.str(), ALOG_HANDSHAKE); - m_client->access_log(m_raw_server_handshake,ALOG_HANDSHAKE); - m_client->access_log("SPACER",ALOG_HANDSHAKE); - - std::vector tokens; - std::string::size_type start = 0; + std::istream response_stream(&m_buf); + std::string header; std::string::size_type end; - // Get request and parse headers - end = m_raw_server_handshake.find("\r\n",start); - - while(end != std::string::npos) { - tokens.push_back(m_raw_server_handshake.substr(start, end - start)); - - start = end + 2; - - end = m_raw_server_handshake.find("\r\n",start); + // 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"; } - for (size_t i = 0; i < tokens.size(); i++) { - if (i == 0) { - m_server_http_request = tokens[i]; + // 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 = tokens[i].find(": ",0); + end = header.find(": ",0); - if (end != std::string::npos) { - std::string h = tokens[i].substr(0,end); + if (end != std::string::npos) { + std::string h = header.substr(0,end); if (get_server_header(h) == "") { - m_server_headers[h] = tokens[i].substr(end+2); + m_server_headers[h] = header.substr(end+2); } else { - m_server_headers[h] += ", " + tokens[i].substr(end+2); + 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; @@ -202,7 +196,7 @@ void client_session::handle_read_handshake(const boost::system::error_code& e, 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\""; + err << "Upgrade header was \"" << h << "\" instead of \"websocket\""; throw(handshake_error(err.str(),400)); } diff --git a/src/websocket_client_session.hpp b/src/websocket_client_session.hpp index 510f02cd66..2dd431e272 100644 --- a/src/websocket_client_session.hpp +++ b/src/websocket_client_session.hpp @@ -65,7 +65,8 @@ class client_session : public session { public: client_session (client_ptr c, boost::asio::io_service& io_service, - connection_handler_ptr defc); + connection_handler_ptr defc, + uint64_t buf_size); /*** CLIENT INTERFACE ***/ diff --git a/src/websocket_server.cpp b/src/websocket_server.cpp index c098e77ad3..a3fbdc56c6 100644 --- a/src/websocket_server.cpp +++ b/src/websocket_server.cpp @@ -138,9 +138,11 @@ void server::access_log(std::string msg,uint16_t level) { } void server::start_accept() { + // TODO: sanity check whether the session buffer size bound could be reduced server_session_ptr new_session(new server_session(shared_from_this(), m_io_service, - m_def_con_handler)); + m_def_con_handler, + m_max_message_size*2)); m_acceptor.async_accept( new_session->socket(), diff --git a/src/websocket_server_session.cpp b/src/websocket_server_session.cpp index 1213fb8eca..e0fc73e790 100644 --- a/src/websocket_server_session.cpp +++ b/src/websocket_server_session.cpp @@ -45,8 +45,9 @@ using websocketpp::server_session; server_session::server_session(websocketpp::server_ptr s, boost::asio::io_service& io_service, - websocketpp::connection_handler_ptr defc) - : session(io_service,defc),m_server(s) {} + 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(); @@ -91,6 +92,7 @@ void server_session::select_extension(const std::string& val) { } void server_session::read_handshake() { + // TODO: pass maximum size to m_buf cosntructor to prevent DoS here boost::asio::async_read_until( m_socket, m_buf, diff --git a/src/websocket_server_session.hpp b/src/websocket_server_session.hpp index 0fd93bd8c9..c8b023e8bd 100644 --- a/src/websocket_server_session.hpp +++ b/src/websocket_server_session.hpp @@ -65,7 +65,8 @@ class server_session : public session { public: server_session (server_ptr s, boost::asio::io_service& io_service, - connection_handler_ptr defc); + connection_handler_ptr defc, + uint64_t buf_size); /*** SERVER INTERFACE ***/ diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index 9ebf2a9a8d..daffeae8a9 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -44,7 +44,8 @@ using websocketpp::session; session::session (boost::asio::io_service& io_service, - websocketpp::connection_handler_ptr defc) + websocketpp::connection_handler_ptr defc, + uint64_t buf_size) : m_status(CONNECTING), m_local_close_code(CLOSE_STATUS_NO_STATUS), m_remote_close_code(CLOSE_STATUS_NO_STATUS), @@ -54,8 +55,7 @@ session::session (boost::asio::io_service& io_service, m_socket(io_service), m_io_service(io_service), m_local_interface(defc), - - + m_buf(buf_size), // maximum buffered (unconsumed) bytes from network m_utf8_state(utf8_validator::UTF8_ACCEPT), m_utf8_codepoint(0) {} diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index c6a4847d6a..c9d270a265 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -94,7 +94,8 @@ public: static const uint16_t CLOSE_STATUS_EXTENSION_REQUIRE = 1010; session (boost::asio::io_service& io_service, - connection_handler_ptr defc); + connection_handler_ptr defc, + uint64_t buf_size); tcp::socket& socket(); boost::asio::io_service& io_service(); From 831da9e12b138710e1ad81820a494eae019f6a1c Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 6 Oct 2011 08:51:18 -0500 Subject: [PATCH 02/40] preliminary frame reading changes. NOTE: this revision won't compile --- src/websocket_frame.cpp | 78 +++++++++++++++++++++++++++++---------- src/websocket_frame.hpp | 30 ++++++++++++++- src/websocket_session.cpp | 36 ++++++++++++++++++ 3 files changed, 122 insertions(+), 22 deletions(-) diff --git a/src/websocket_frame.cpp b/src/websocket_frame.cpp index 6be794f501..7fdd35cdcf 100644 --- a/src/websocket_frame.cpp +++ b/src/websocket_frame.cpp @@ -41,6 +41,46 @@ using websocketpp::frame; + +uint8_t frame::get_state() const { + return m_state; +} + +void frame::reset() { + m_state = STATE_BASIC_HEADER; + m_bytes_needed = BASIC_HEADER_LENGTH; +} + +void frame::consume(std::istream &s) { + if (m_state == STATE_BASIC_HEADER) { + s.read(&m_header[BASIC_HEADER_LENGTH-m_bytes_needed],m_bytes_needed); + + m_bytes_needed -= s.gcount(); + + if (m_bytes_needed == 0) { + process_basic_header(); + + // basic header validation + validate_basic_header(); + + if (m_bytes_needed > 0) { + m_state = STATE_EXTENDED_HEADER; + } else { + m_state = STATE_PAYLOAD; + } + } + } else if (m_state == STATE_EXTENDED_HEADER) { + s.read(&m_header[BASIC_HEADER_LENGTH+get_header_len()-m_bytes_needed],m_bytes_needed); + + m_bytes_needed -= s.gcount(); + + if (m_bytes_needed == 0) { + process_extended_header(); + m_state = STATE_PAYLOAD; + } + } +} + char* frame::get_header() { return m_header; } @@ -283,13 +323,9 @@ std::string frame::print_frame() const { return f.str(); } -unsigned int frame::process_basic_header() { - m_extended_header_bytes_needed = 0; +void frame::process_basic_header() { m_payload.empty(); - - m_extended_header_bytes_needed = get_header_len() - BASIC_HEADER_LENGTH; - - return m_extended_header_bytes_needed; + m_bytes_needed = get_header_len() - BASIC_HEADER_LENGTH; } void frame::process_extended_header() { @@ -308,22 +344,26 @@ void frame::process_extended_header() { reinterpret_cast(&m_header[BASIC_HEADER_LENGTH]) )); + if (payload_size < s) { + throw frame_error("payload length not minimally encoded"); + } + mask_index += 2; } else if (s == BASIC_PAYLOAD_64BIT_CODE) { - // reinterpret the second eight bytes as a 16 bit integer in + // 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]) )); + if (payload_size <= PAYLOAD_16BIT_LIMIT) { + throw frame_error("payload length not minimally encoded"); + } + mask_index += 8; } else { // shouldn't be here - throw server_error("invalid get_basic_size in process_extended_header"); - } - - if (payload_size < s) { - throw server_error("payload size error"); + throw frame_error("invalid get_basic_size in process_extended_header"); } if (get_masked() == 0) { @@ -394,32 +434,30 @@ bool frame::validate_utf8(uint32_t* state,uint32_t* codep) const { return true; } -bool frame::validate_basic_header() const { +void frame::validate_basic_header() const { // check for control frame size if (get_basic_size() > BASIC_PAYLOAD_LIMIT && is_control()) { - return false; + throw frame_error("Control Frame is too large"); } // check for reserved opcodes if (get_rsv1() || get_rsv2() || get_rsv3()) { - return false; + throw frame_error("Reserved bit used"); } // check for reserved opcodes opcode op = get_opcode(); if (op > 0x02 && op < 0x08) { - return false; + throw frame_error("Reserved opcode used"); } if (op > 0x0A) { - return false; + throw frame_error("Reserved opcode used"); } // check for fragmented control message if (is_control() && !get_fin()) { - return false; + throw frame_error("Fragmented control message"); } - - return true; } void frame::generate_masking_key() { diff --git a/src/websocket_frame.hpp b/src/websocket_frame.hpp index 33436306a5..0d4e4c6802 100644 --- a/src/websocket_frame.hpp +++ b/src/websocket_frame.hpp @@ -54,6 +54,11 @@ public: static const uint8_t MAX_FRAME_OPCODE = 0x07; + static const uint8_t STATE_BASIC_HEADER = 1; + static const uint8_t STATE_EXTENDED_HEADER = 2; + static const uint8_t STATE_PAYLOAD = 3; + static const uint8_t STATE_READY = 4; + // basic payload byte flags static const uint8_t BPB0_OPCODE = 0x0F; static const uint8_t BPB0_RSV3 = 0x10; @@ -81,6 +86,11 @@ public: memset(m_header,0,MAX_HEADER_LENGTH); } + uint8_t get_state() const; + void reset(); + + void consume(std::istream &s); + // get pointers to underlying buffers char* get_header(); char* get_extended_header(); @@ -126,18 +136,21 @@ public: std::string print_frame() const; // reads basic header, sets and returns m_header_bits_needed - unsigned int process_basic_header(); + void process_basic_header(); void process_extended_header(); void process_payload(); void process_payload2(); // experiment with more efficient masking code. bool validate_utf8(uint32_t* state,uint32_t* codep) const; - bool validate_basic_header() const; + void validate_basic_header() const; void generate_masking_key(); void clear_masking_key(); private: + uint8_t m_state; + uint64_t m_bytes_needed; + char m_header[MAX_HEADER_LENGTH]; std::vector m_payload; @@ -150,6 +163,19 @@ private: m_gen; }; +// Exception classes +class frame_error : public std::exception { +public: + frame_error(const std::string& msg) : m_msg(msg) {} + ~frame_error() throw() {} + + virtual const char* what() const throw() { + return m_msg.c_str(); + } + + std::string m_msg; +}; + } #endif // WEBSOCKET_FRAME_HPP diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index daffeae8a9..98131b2fad 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -202,6 +202,42 @@ void session::pong(const std::string &msg) { write_frame(); } +void session::handle_read_frame(const boost::system::error_code& error) { + // while + // if there are enough bytes to do something: + // do something + // read more + + std::istream s(&m_buf); + + try { + while (m_buf.size() > 0) { + m_read_frame.consume(s); + } + } catch (const frame_error& e) { + std::stringstream err; + err << "Caught frame exception: " << e.what(); + + access_log(e.what(),ALOG_FRAME); + log(err.str(),LOG_ERROR); + + // TODO: close behavior + return; + } + // we have read everything, check if we should read more + + boost::asio::async_read( + m_socket, + m_buf, + boost::asio::transfer_at_least(1), + boost::bind( + &session::handle_read_frame, + shared_from_this(), + boost::asio::placeholders::error + ) + ); +} + void session::read_frame() { boost::asio::async_read( m_socket, From 9180e521037e9b37f189d64cbd225b967b677977 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Fri, 7 Oct 2011 08:26:41 -0500 Subject: [PATCH 03/40] continues frame reading changes fixes a lot of underspecified close behavior. NOTE: this revision won't compile --- src/websocket_frame.cpp | 39 ++++++----- src/websocket_frame.hpp | 13 +++- src/websocket_session.cpp | 132 ++++++++++++++++++++++---------------- src/websocket_session.hpp | 20 +++--- 4 files changed, 122 insertions(+), 82 deletions(-) diff --git a/src/websocket_frame.cpp b/src/websocket_frame.cpp index 7fdd35cdcf..1496ea06a8 100644 --- a/src/websocket_frame.cpp +++ b/src/websocket_frame.cpp @@ -49,6 +49,7 @@ uint8_t frame::get_state() const { void frame::reset() { m_state = STATE_BASIC_HEADER; m_bytes_needed = BASIC_HEADER_LENGTH; + m_payload.empty(); } void frame::consume(std::istream &s) { @@ -78,6 +79,15 @@ void frame::consume(std::istream &s) { process_extended_header(); m_state = STATE_PAYLOAD; } + } else if (m_state == STATE_PAYLOAD) { + s.read(reinterpret_cast(&m_payload[0]),m_bytes_needed); + + m_bytes_needed -= s.gcount(); + + if (m_bytes_needed == 0) { + process_payload(); + m_state = STATE_READY; + } } } @@ -324,13 +334,10 @@ std::string frame::print_frame() const { } void frame::process_basic_header() { - m_payload.empty(); m_bytes_needed = get_header_len() - BASIC_HEADER_LENGTH; } void frame::process_extended_header() { - m_extended_header_bytes_needed = 0; - uint8_t s = get_basic_size(); uint64_t payload_size; int mask_index = BASIC_HEADER_LENGTH; @@ -345,7 +352,8 @@ void frame::process_extended_header() { )); if (payload_size < s) { - throw frame_error("payload length not minimally encoded"); + throw frame_error("payload length not minimally encoded", + FERR_PROTOCOL_VIOLATION); } mask_index += 2; @@ -357,7 +365,8 @@ void frame::process_extended_header() { )); if (payload_size <= PAYLOAD_16BIT_LIMIT) { - throw frame_error("payload length not minimally encoded"); + throw frame_error("payload length not minimally encoded", + FERR_PROTOCOL_VIOLATION); } mask_index += 8; @@ -378,9 +387,11 @@ void frame::process_extended_header() { } if (payload_size > max_payload_size) { + // TODO: frame/message size limits throw server_error("got frame with payload greater than maximum frame buffer size."); } m_payload.resize(payload_size); + m_bytes_needed = payload_size; } void frame::process_payload() { @@ -420,43 +431,39 @@ void frame::process_payload2() { } } -bool frame::validate_utf8(uint32_t* state,uint32_t* codep) const { +void frame::validate_utf8(uint32_t* state,uint32_t* codep) const { for (size_t i = 0; i < m_payload.size(); i++) { using utf8_validator::decode; - //std::cout << "decoding: " << std::hex << m_payload[i] << std::endl; if (decode(state,codep,m_payload[i]) == utf8_validator::UTF8_REJECT) { - // std::cout << "bad byte" << std::endl; - return false; + throw frame_error("Invalid UTF-8 Data",FERR_PAYLOAD_VIOLATION); } } - - return true; } void frame::validate_basic_header() const { // check for control frame size if (get_basic_size() > BASIC_PAYLOAD_LIMIT && is_control()) { - throw frame_error("Control Frame is too large"); + throw frame_error("Control Frame is too large",FERR_PROTOCOL_VIOLATION); } // check for reserved opcodes if (get_rsv1() || get_rsv2() || get_rsv3()) { - throw frame_error("Reserved bit used"); + throw frame_error("Reserved bit used",FERR_PROTOCOL_VIOLATION); } // check for reserved opcodes opcode op = get_opcode(); if (op > 0x02 && op < 0x08) { - throw frame_error("Reserved opcode used"); + throw frame_error("Reserved opcode used",FERR_PROTOCOL_VIOLATION); } if (op > 0x0A) { - throw frame_error("Reserved opcode used"); + throw frame_error("Reserved opcode used",FERR_PROTOCOL_VIOLATION); } // check for fragmented control message if (is_control() && !get_fin()) { - throw frame_error("Fragmented control message"); + throw frame_error("Fragmented control message",FERR_PROTOCOL_VIOLATION); } } diff --git a/src/websocket_frame.hpp b/src/websocket_frame.hpp index 0d4e4c6802..abd88ce30d 100644 --- a/src/websocket_frame.hpp +++ b/src/websocket_frame.hpp @@ -59,6 +59,11 @@ public: static const uint8_t STATE_PAYLOAD = 3; static const uint8_t STATE_READY = 4; + static const uint16_t FERR_FATAL_SESSION_ERROR = 0; // must end session + static const uint16_t FERR_SOFT_SESSION_ERROR = 1; // should log and ignore + static const uint16_t FERR_PROTOCOL_VIOLATION = 2; // must end session + static const uint16_t FERR_PAYLOAD_VIOLATION = 3; // should end session + // basic payload byte flags static const uint8_t BPB0_OPCODE = 0x0F; static const uint8_t BPB0_RSV3 = 0x10; @@ -87,6 +92,7 @@ public: } uint8_t get_state() const; + uint64_t get_bytes_needed() const; void reset(); void consume(std::istream &s); @@ -141,7 +147,7 @@ public: void process_payload(); void process_payload2(); // experiment with more efficient masking code. - bool validate_utf8(uint32_t* state,uint32_t* codep) const; + void validate_utf8(uint32_t* state,uint32_t* codep) const; void validate_basic_header() const; void generate_masking_key(); @@ -166,7 +172,9 @@ private: // Exception classes class frame_error : public std::exception { public: - frame_error(const std::string& msg) : m_msg(msg) {} + frame_error(const std::string& msg, + uint16_t code = frame::FERR_FATAL_SESSION_ERROR) + : m_msg(msg),m_code(code) {} ~frame_error() throw() {} virtual const char* what() const throw() { @@ -174,6 +182,7 @@ public: } std::string m_msg; + uint16_t m_code; }; } diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index 98131b2fad..e1f363377d 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -120,7 +120,7 @@ unsigned int session::get_version() const { } void session::send(const std::string &msg) { - if (m_status != OPEN) { + if (m_state != STATE_OPEN) { log("Tried to send a message from a session that wasn't open",LOG_WARN); return; } @@ -132,7 +132,7 @@ void session::send(const std::string &msg) { } void session::send(const std::vector &data) { - if (m_status != OPEN) { + if (m_state != STATE_OPEN) { log("Tried to send a message from a session that wasn't open",LOG_WARN); return; } @@ -143,19 +143,28 @@ void session::send(const std::vector &data) { write_frame(); } +// end user interface to close the connection void session::close(uint16_t status,const std::string& msg) { + validate_app_close_status(status); + disconnect(status,msg); // TODO: close behavior } // TODO: clean this up, needs to be broken out into more specific methods + +// This method initiates a clean disconnect with the given status code and reason +// it logs an error and is ignored if it is called from a state other than OPEN + +// called by process_close when an initiate close method is received. + void session::disconnect(uint16_t status,const std::string &message) { - if (m_status != OPEN) { + if (m_state != STATE_OPEN) { log("Tried to disconnect a session that wasn't open",LOG_WARN); return; } - m_status = CLOSING; + m_state = STATE_CLOSING; m_close_code = status; m_close_message = message; @@ -179,7 +188,7 @@ void session::disconnect(uint16_t status,const std::string &message) { } void session::ping(const std::string &msg) { - if (m_status != OPEN) { + if (m_state != STATE_OPEN) { log("Tried to send a ping from a session that wasn't open",LOG_WARN); return; } @@ -191,7 +200,7 @@ void session::ping(const std::string &msg) { } void session::pong(const std::string &msg) { - if (m_status != OPEN) { + if (m_state != STATE_OPEN) { log("Tried to send a pong from a session that wasn't open",LOG_WARN); return; } @@ -203,16 +212,28 @@ void session::pong(const std::string &msg) { } void session::handle_read_frame(const boost::system::error_code& error) { - // while - // if there are enough bytes to do something: - // do something - // read more - + if (error) { + handle_error("Error reading extended frame header",error); + // TODO: close behavior + return; + } + + if (m_state != STATE_OPEN && m_state != STATE_CLOSING) { + // stop processing frames. + log("handle_read_frame called in invalid state",LOG_ERROR); + return; + } + std::istream s(&m_buf); try { while (m_buf.size() > 0) { m_read_frame.consume(s); + if (m_read_frame.get_state() == frame::STATE_READY) { + process_frame(); + + // should we break? + } } } catch (const frame_error& e) { std::stringstream err; @@ -221,15 +242,24 @@ void session::handle_read_frame(const boost::system::error_code& error) { access_log(e.what(),ALOG_FRAME); log(err.str(),LOG_ERROR); + disconnect(CLOSE_STATUS_PROTOCOL_ERROR,""); + // TODO: close behavior return; } // we have read everything, check if we should read more + if (m_state != STATE_OPEN && m_state != STATE_CLOSING) { + // stop processing frames. + log("handle_read_frame called in invalid state"); + return; + } + + // read more boost::asio::async_read( m_socket, m_buf, - boost::asio::transfer_at_least(1), + boost::asio::transfer_at_least(m_read_frame.get_bytes_needed()), boost::bind( &session::handle_read_frame, shared_from_this(), @@ -313,16 +343,8 @@ void session::read_payload() { ); } -void session::handle_read_payload (const boost::system::error_code& error) { - if (error) { - handle_error("Error reading payload data frame header",error); - // TODO: close behavior - return; - } - - m_read_frame.process_payload(); - - if (m_status == OPEN) { +void session::process_frame () { + if (m_state == STATE_OPEN) { switch (m_read_frame.get_opcode()) { case frame::CONTINUATION_FRAME: process_continuation(); @@ -347,7 +369,7 @@ void session::handle_read_payload (const boost::system::error_code& error) { // TODO: close behavior break; } - } else if (m_status == CLOSING) { + } else if (m_state == STATE_CLOSING) { if (m_read_frame.get_opcode() == frame::CONNECTION_CLOSE) { process_close(); } else { @@ -377,8 +399,8 @@ void session::handle_read_payload (const boost::system::error_code& error) { // TODO: close behavior return; } - - this->read_frame(); + + m_read_frame.reset(); } void session::handle_write_frame (const boost::system::error_code& error) { @@ -408,22 +430,17 @@ void session::process_pong() { } void session::process_text() { - if (!m_read_frame.validate_utf8(&m_utf8_state,&m_utf8_codepoint)) { - disconnect(CLOSE_STATUS_INVALID_PAYLOAD,"Invalid UTF8 Data"); - // TODO: close behavior - return; - } - + // 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 session::process_binary() { if (m_fragmented) { - handle_error("Got a new message before the previous was finished.", - boost::system::error_code()); - disconnect(CLOSE_STATUS_PROTOCOL_ERROR,""); - // TODO: close behavior - return; + throw frame_error("Got a new message before the previous was finished.", + frame::FERR_PROTOCOL_VIOLATION); } m_current_opcode = m_read_frame.get_opcode(); @@ -439,19 +456,13 @@ void session::process_binary() { void session::process_continuation() { if (!m_fragmented) { - handle_error("Got a continuation frame without an outstanding message.", - boost::system::error_code()); - disconnect(CLOSE_STATUS_PROTOCOL_ERROR,""); - // TODO: close behavior - return; + throw frame_error("Got a continuation frame without an outstanding message.", + frame::FERR_PROTOCOL_VIOLATION); } if (m_current_opcode == frame::TEXT_FRAME) { - if (!m_read_frame.validate_utf8(&m_utf8_state,&m_utf8_codepoint)) { - disconnect(CLOSE_STATUS_INVALID_PAYLOAD,"Invalid UTF8 Data"); - // TODO: close behavior - return; - } + // this will throw an exception if validation fails at any point + m_read_frame.validate_utf8(&m_utf8_state,&m_utf8_codepoint); } extract_payload(); @@ -470,17 +481,16 @@ void session::process_close() { m_remote_close_code = status; m_remote_close_msg = message; - - if (m_status == OPEN) { + if (m_state == STATE_OPEN) { // This is the case where the remote initiated the close. m_closed_by_me = false; // TODO: close behavior disconnect(status,message); - } else if (m_status == CLOSING) { + } else if (m_state == STATE_CLOSING) { // this is an ack of our close message m_closed_by_me = true; } else { - throw "fixme"; + throw frame_error("process_closed called from wrong state"); } m_was_clean = true; @@ -503,10 +513,11 @@ void session::deliver_message() { 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) { - disconnect(CLOSE_STATUS_INVALID_PAYLOAD,"Invalid UTF8 Data"); - // TODO: close behavior - return; + throw frame_error("Invalid UTF-8 Data",FERR_PAYLOAD_VIOLATION); } if (m_fragmented) { @@ -523,7 +534,7 @@ void session::deliver_message() { // 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; - log(err.str(),LOG_ERROR); + throw frame_error(err.str(),frame::FERR_SOFT_SESSION_ERROR); } } @@ -614,3 +625,16 @@ void session::handle_error(std::string msg, m_error = true; } + +// validates status codes that the end application is allowed to use +bool session::validate_app_close_status(uint16_t status) { + if (status == CLOSE_STATUS_NORMAL) { + return true; + } + + if (status >= 4000 && status < 5000) { + return true; + } + + return false; +} diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index c9d270a265..6c1ad3ced3 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -73,14 +73,10 @@ class session : public boost::enable_shared_from_this { public: friend class handshake_error; - enum ws_status { - CONNECTING, - OPEN, - CLOSING, - CLOSED - }; - - typedef enum ws_status status_code; + static const uint8_t STATE_CONNECTING = 0; + static const uint8_t STATE_OPEN = 1; + static const uint8_t STATE_CLOSING = 2; + static const uint8_t STATE_CLOSED = 3; static const uint16_t CLOSE_STATUS_NORMAL = 1000; static const uint16_t CLOSE_STATUS_GOING_AWAY = 1001; @@ -160,13 +156,14 @@ protected: void handle_frame_header(const boost::system::error_code& error); void handle_extended_frame_header(const boost::system::error_code& error); void read_payload(); - void handle_read_payload (const boost::system::error_code& error); + void handle_read_frame (const boost::system::error_code& error); // write m_write_frame out to the socket. void write_frame(); void handle_write_frame (const boost::system::error_code& error); // helper functions for processing each opcode + void process_frame(); void process_ping(); void process_pong(); void process_text(); @@ -194,6 +191,9 @@ protected: // prints a diagnostic message and disconnects the local interface void handle_error(std::string msg,const boost::system::error_code& error); + + // misc helpers + bool validate_app_close_status(uint16_t status); private: std::string get_header(const std::string& key, const header_list& list) const; @@ -220,7 +220,7 @@ protected: std::string m_server_http_string; // Mutable connection state; - status_code m_status; + uint8_t m_state; uint16_t m_close_code; std::string m_close_message; From f0947e4b1d3ed663e462ad1189267b5ef29d1945 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Fri, 7 Oct 2011 22:07:17 -0500 Subject: [PATCH 04/40] frame reading cleanup. NOTE: this revision won't compile --- src/websocket_frame.hpp | 4 ++ src/websocket_session.cpp | 102 +++++++------------------------------- src/websocket_session.hpp | 7 +-- 3 files changed, 24 insertions(+), 89 deletions(-) diff --git a/src/websocket_frame.hpp b/src/websocket_frame.hpp index abd88ce30d..fc37594c6b 100644 --- a/src/websocket_frame.hpp +++ b/src/websocket_frame.hpp @@ -181,6 +181,10 @@ public: return m_msg.c_str(); } + uint16_t code() const throw() { + + } + std::string m_msg; uint16_t m_code; }; diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index e1f363377d..8837642f2a 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -46,7 +46,7 @@ using websocketpp::session; session::session (boost::asio::io_service& io_service, websocketpp::connection_handler_ptr defc, uint64_t buf_size) - : m_status(CONNECTING), + : m_state(STATE_CONNECTING), m_local_close_code(CLOSE_STATUS_NO_STATUS), m_remote_close_code(CLOSE_STATUS_NO_STATUS), m_was_clean(false), @@ -77,7 +77,7 @@ void session::set_handler(websocketpp::connection_handler_ptr new_con) { } const std::string& session::get_subprotocol() const { - if (m_status == CONNECTING) { + if (m_state == STATE_CONNECTING) { log("Subprotocol is not avaliable before the handshake has completed.",LOG_WARN); throw server_error("Subprotocol is not avaliable before the handshake has completed."); } @@ -147,8 +147,7 @@ void session::send(const std::vector &data) { void session::close(uint16_t status,const std::string& msg) { validate_app_close_status(status); - disconnect(status,msg); - // TODO: close behavior + send_close(status,msg); } // TODO: clean this up, needs to be broken out into more specific methods @@ -158,7 +157,7 @@ void session::close(uint16_t status,const std::string& msg) { // called by process_close when an initiate close method is received. -void session::disconnect(uint16_t status,const std::string &message) { +void session::send_close(uint16_t status,const std::string &message) { if (m_state != STATE_OPEN) { log("Tried to disconnect a session that wasn't open",LOG_WARN); return; @@ -242,6 +241,17 @@ void session::handle_read_frame(const boost::system::error_code& error) { access_log(e.what(),ALOG_FRAME); log(err.str(),LOG_ERROR); + if (e.code() == frame::FERR_PROTOCOL_VIOLATION) { + disconnect(CLOSE_STATUS_PROTOCOL_ERROR, e.what()); + } else if (e.code() == frame::FERR_PAYLOAD_VIOLATION) { + disconnect(CLOSE_STATUS_INVALID_PAYLOAD, e.what()); + } else if (e.code() == frame::FERR_SOFT_SESSION_ERROR) { + // ??? + } else { + // Fatal error + disconnect(CLOSE_STATUS_NO_STATUS, ""); + } + disconnect(CLOSE_STATUS_PROTOCOL_ERROR,""); // TODO: close behavior @@ -251,7 +261,7 @@ void session::handle_read_frame(const boost::system::error_code& error) { if (m_state != STATE_OPEN && m_state != STATE_CLOSING) { // stop processing frames. - log("handle_read_frame called in invalid state"); + log("handle_read_frame called in invalid state",LOG_ERROR); return; } @@ -268,81 +278,6 @@ void session::handle_read_frame(const boost::system::error_code& error) { ); } -void session::read_frame() { - boost::asio::async_read( - m_socket, - boost::asio::buffer(m_read_frame.get_header(), - frame::BASIC_HEADER_LENGTH), - boost::bind( - &session::handle_frame_header, - shared_from_this(), - boost::asio::placeholders::error - ) - ); -} - -void session::handle_frame_header(const boost::system::error_code& error) { - if (error) { - handle_error("Error reading basic frame header",error); - // TODO: close behavior - return; - } - log(m_read_frame.print_frame(),LOG_DEBUG); - - uint16_t extended_header_bytes = m_read_frame.process_basic_header(); - - if (!m_read_frame.validate_basic_header()) { - handle_error("Basic header validation failed",boost::system::error_code()); - disconnect(CLOSE_STATUS_PROTOCOL_ERROR,""); - - - // TODO: close behavior - return; - } - - if (extended_header_bytes == 0) { - m_read_frame.process_extended_header(); - read_payload(); - } else { - boost::asio::async_read( - m_socket, - boost::asio::buffer(m_read_frame.get_extended_header(), - extended_header_bytes), - boost::bind( - &session::handle_extended_frame_header, - shared_from_this(), - boost::asio::placeholders::error - ) - ); - } -} - -void session::handle_extended_frame_header( - const boost::system::error_code& error) { - if (error) { - handle_error("Error reading extended frame header",error); - // TODO: close behavior - return; - } - - // this sets up the buffer we are about to read into. - m_read_frame.process_extended_header(); - - this->read_payload(); -} - -void session::read_payload() { - boost::asio::async_read( - m_socket, - boost::asio::buffer(m_read_frame.get_payload()), - boost::bind( - &session::handle_read_payload, - shared_from_this(), - boost::asio::placeholders::error - ) - ); -} - void session::process_frame () { if (m_state == STATE_OPEN) { switch (m_read_frame.get_opcode()) { @@ -494,7 +429,7 @@ void session::process_close() { } m_was_clean = true; - m_status = CLOSED; + m_state = STATE_CLOSED; } void session::deliver_message() { @@ -517,7 +452,8 @@ void session::deliver_message() { // 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_error("Invalid UTF-8 Data",FERR_PAYLOAD_VIOLATION); + throw frame_error("Invalid UTF-8 Data", + frame::FERR_PAYLOAD_VIOLATION); } if (m_fragmented) { diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index 6c1ad3ced3..d4ce03a0bd 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -138,7 +138,6 @@ public: // initiate a connection close void close(uint16_t status,const std::string &reason); - void disconnect(uint16_t status,const std::string& reason); // temp virtual bool is_server() const = 0; @@ -151,11 +150,6 @@ protected: virtual void write_handshake() = 0; virtual void read_handshake() = 0; - // start async read for a websocket frame (2 bytes) to handle_frame_header - void read_frame(); - void handle_frame_header(const boost::system::error_code& error); - void handle_extended_frame_header(const boost::system::error_code& error); - void read_payload(); void handle_read_frame (const boost::system::error_code& error); // write m_write_frame out to the socket. @@ -194,6 +188,7 @@ protected: // misc helpers bool validate_app_close_status(uint16_t status); + void send_close(uint16_t status,const std::string& reason); private: std::string get_header(const std::string& key, const header_list& list) const; From 43bee764a9755d7b4905678a2747833ba00112be Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Mon, 10 Oct 2011 18:13:49 -0500 Subject: [PATCH 05/40] default logging changes --- src/websocket_server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/websocket_server.cpp b/src/websocket_server.cpp index a3fbdc56c6..4303e9610b 100644 --- a/src/websocket_server.cpp +++ b/src/websocket_server.cpp @@ -37,8 +37,8 @@ using websocketpp::server; server::server(boost::asio::io_service& io_service, const tcp::endpoint& endpoint, websocketpp::connection_handler_ptr defc) - : m_elog_level(LOG_ALL), - m_alog_level(ALOG_ALL), + : m_elog_level(LOG_OFF), + m_alog_level(ALOG_OFF), m_max_message_size(DEFAULT_MAX_MESSAGE_SIZE), m_io_service(io_service), m_acceptor(io_service, endpoint), From eec0390618e9f7235a784963a72fcade8af72715 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Mon, 10 Oct 2011 18:14:48 -0500 Subject: [PATCH 06/40] default logging settings change --- src/websocket_client.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/websocket_client.cpp b/src/websocket_client.cpp index ad5446fcb7..c23637a750 100644 --- a/src/websocket_client.cpp +++ b/src/websocket_client.cpp @@ -37,8 +37,8 @@ using boost::asio::ip::tcp; client::client(boost::asio::io_service& io_service, websocketpp::connection_handler_ptr defc) - : m_elog_level(LOG_ALL), - m_alog_level(ALOG_ALL), + : 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), @@ -182,6 +182,10 @@ void client::access_log(std::string msg,uint16_t level) { void client::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 { From 1ff8d333a5b12acbfdb85f1d2c792bbfe9fa1443 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Mon, 10 Oct 2011 18:35:38 -0500 Subject: [PATCH 07/40] completes frame reading changes. fixes 37, fixes 32 --- examples/echo_client/echo_client.cpp | 28 ++- examples/echo_client/echo_client_handler.cpp | 4 +- examples/echo_client/echo_client_handler.hpp | 2 +- examples/echo_server/echo_server.cpp | 3 + src/websocket_client_session.cpp | 13 +- src/websocket_frame.cpp | 171 +++++++++---- src/websocket_frame.hpp | 14 +- src/websocket_server_session.cpp | 8 +- src/websocket_session.cpp | 241 +++++++++++-------- src/websocket_session.hpp | 79 +++--- 10 files changed, 375 insertions(+), 188 deletions(-) diff --git a/examples/echo_client/echo_client.cpp b/examples/echo_client/echo_client.cpp index e0f05de88e..713201804c 100644 --- a/examples/echo_client/echo_client.cpp +++ b/examples/echo_client/echo_client.cpp @@ -56,9 +56,35 @@ int main(int argc, char* argv[]) { client->set_header("User Agent","WebSocket++/2011-09-25"); client->connect("ws://localhost:9001/getCaseCount"); - io_service.run(); + std::cout << "case count: " << c->m_case_count; + + for (int i = 1; i <= c->m_case_count; i++) { + io_service.reset(); + //boost::asio::io_service ios; + + //client.reset(); + //client = websocketpp::client_ptr(new websocketpp::client(io_service,c)); + + std::cout << "foo: " << i << std::endl; + //websocketpp::client_ptr client2(new websocketpp::client(io_service,c)); + + client->set_alog_level(websocketpp::ALOG_OFF); + client->set_elog_level(websocketpp::LOG_OFF); + + client->init(); + client->set_header("User Agent","WebSocket++/2011-09-25"); + + + std::stringstream foo; + + foo << "ws://localhost:9001/runCase?case=" << i << "&agent=\"WebSocket++Snapshot/2011-10-08\""; + + client->connect(foo.str()); + io_service.run(); + } + std::cout << "done" << std::endl; } catch (std::exception& e) { diff --git a/examples/echo_client/echo_client_handler.cpp b/examples/echo_client/echo_client_handler.cpp index ab43350161..ce8dc6408c 100644 --- a/examples/echo_client/echo_client_handler.cpp +++ b/examples/echo_client/echo_client_handler.cpp @@ -42,8 +42,8 @@ void echo_client_handler::on_close(session_ptr s,uint16_t status,const std::stri void echo_client_handler::on_message(session_ptr s,const std::string &msg) { if (s->get_resource() == "/getCaseCount") { - std::cout << "msg |" << msg.substr(1,msg.size()-2) << "|" << std::endl; - m_case_count = atoi(msg.substr(1,msg.size()-2).c_str()); + std::cout << "Detected " << msg << " test cases." << std::endl; + m_case_count = atoi(msg.c_str()); } else { s->send(msg); } diff --git a/examples/echo_client/echo_client_handler.hpp b/examples/echo_client/echo_client_handler.hpp index a2c3c385c3..795939d183 100644 --- a/examples/echo_client/echo_client_handler.hpp +++ b/examples/echo_client/echo_client_handler.hpp @@ -70,7 +70,7 @@ public: // ignore messages void on_message(session_ptr s,const std::vector &data); -private: + int m_case_count; }; diff --git a/examples/echo_server/echo_server.cpp b/examples/echo_server/echo_server.cpp index 021c2656de..c8b616412e 100644 --- a/examples/echo_server/echo_server.cpp +++ b/examples/echo_server/echo_server.cpp @@ -61,6 +61,9 @@ int main(int argc, char* argv[]) { ); // setup server settings + server->set_alog_level(websocketpp::ALOG_OFF); + server->set_elog_level(websocketpp::LOG_OFF); + server->add_host(host); server->add_host(full_host); diff --git a/src/websocket_client_session.cpp b/src/websocket_client_session.cpp index 21045a5208..8532afdde6 100644 --- a/src/websocket_client_session.cpp +++ b/src/websocket_client_session.cpp @@ -124,6 +124,13 @@ void client_session::read_handshake() { 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; @@ -255,7 +262,7 @@ void client_session::handle_read_handshake(const boost::system::error_code& e, log_open_result(); - m_status = OPEN; + m_state = STATE_OPEN; if (m_local_interface) { m_local_interface->on_open(shared_from_this()); @@ -326,8 +333,8 @@ void client_session::write_handshake() { void client_session::handle_write_handshake(const boost::system::error_code& error) { if (error) { - handle_error("Error writing handshake",error); - // TODO: close behavior + log_error("Error writing handshake",error); + drop_tcp(); return; } diff --git a/src/websocket_frame.cpp b/src/websocket_frame.cpp index 1496ea06a8..63fcaf45b5 100644 --- a/src/websocket_frame.cpp +++ b/src/websocket_frame.cpp @@ -46,47 +46,114 @@ uint8_t frame::get_state() const { return m_state; } +uint64_t frame::get_bytes_needed() const { + return m_bytes_needed; +} + void frame::reset() { m_state = STATE_BASIC_HEADER; m_bytes_needed = BASIC_HEADER_LENGTH; + m_degraded = false; m_payload.empty(); + memset(m_header,0,MAX_HEADER_LENGTH); } +// 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 frame::consume(std::istream &s) { - if (m_state == STATE_BASIC_HEADER) { - s.read(&m_header[BASIC_HEADER_LENGTH-m_bytes_needed],m_bytes_needed); + try { + switch (m_state) { + case STATE_BASIC_HEADER: + s.read(&m_header[BASIC_HEADER_LENGTH-m_bytes_needed],m_bytes_needed); - m_bytes_needed -= s.gcount(); - - if (m_bytes_needed == 0) { - process_basic_header(); - - // basic header validation - validate_basic_header(); - - if (m_bytes_needed > 0) { - m_state = STATE_EXTENDED_HEADER; - } else { - m_state = STATE_PAYLOAD; - } + m_bytes_needed -= s.gcount(); + + if (m_bytes_needed == 0) { + process_basic_header(); + + validate_basic_header(); + + if (m_bytes_needed > 0) { + m_state = STATE_EXTENDED_HEADER; + } else { + process_extended_header(); + + if (m_bytes_needed == 0) { + m_state = STATE_READY; + process_payload(); + + } else { + m_state = STATE_PAYLOAD; + } + } + } + break; + case STATE_EXTENDED_HEADER: + s.read(&m_header[get_header_len()-m_bytes_needed],m_bytes_needed); + + m_bytes_needed -= s.gcount(); + + if (m_bytes_needed == 0) { + process_extended_header(); + if (m_bytes_needed == 0) { + m_state = STATE_READY; + process_payload(); + } else { + m_state = STATE_PAYLOAD; + } + } + break; + case STATE_PAYLOAD: + s.read(reinterpret_cast(&m_payload[m_payload.size()-m_bytes_needed]), + m_bytes_needed); + + m_bytes_needed -= s.gcount(); + + if (m_bytes_needed == 0) { + m_state = STATE_READY; + process_payload(); + } + break; + case STATE_RECOVERY: + // Recovery state discards all bytes that are not the first byte + // of a close frame. + do { + s.read(reinterpret_cast(&m_header[0]),1); + + //std::cout << std::hex << int(static_cast(m_header[0])) << " "; + + if (int(static_cast(m_header[0])) == 0x88) { + //(BPB0_FIN && CONNECTION_CLOSE) + m_bytes_needed--; + m_state = STATE_BASIC_HEADER; + break; + } + } while (s.gcount() > 0); + + //std::cout << std::endl; + + break; + default: + break; } - } else if (m_state == STATE_EXTENDED_HEADER) { - s.read(&m_header[BASIC_HEADER_LENGTH+get_header_len()-m_bytes_needed],m_bytes_needed); - m_bytes_needed -= s.gcount(); - - if (m_bytes_needed == 0) { - process_extended_header(); - m_state = STATE_PAYLOAD; - } - } else if (m_state == STATE_PAYLOAD) { - s.read(reinterpret_cast(&m_payload[0]),m_bytes_needed); - - m_bytes_needed -= s.gcount(); - - if (m_bytes_needed == 0) { - process_payload(); - m_state = STATE_READY; + /*if (s.gcount() == 0) { + throw frame_error("consume read zero bytes",FERR_FATAL_SESSION_ERROR); + }*/ + } catch (const frame_error& 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 frame_error("An error occurred while trying to gracefully recover from a less serious frame error.",FERR_FATAL_SESSION_ERROR); + } else { + reset(); + m_state = STATE_RECOVERY; + m_degraded = true; + + throw e; } } } @@ -116,8 +183,8 @@ unsigned int frame::get_header_len() const { } char* frame::get_masking_key() { - if (m_extended_header_bytes_needed > 0) { - throw "attempted to get masking_key before reading full header"; + if (m_state != STATE_READY) { + throw frame_error("attempted to get masking_key before reading full header"); } return m_masking_key; } @@ -178,12 +245,12 @@ frame::opcode frame::get_opcode() const { void frame::set_opcode(frame::opcode op) { if (op > 0x0F) { - throw "invalid opcode"; + throw frame_error("invalid opcode",FERR_PROTOCOL_VIOLATION); } if (get_basic_size() > BASIC_PAYLOAD_LIMIT && is_control()) { - throw "control frames can't have large payloads"; + throw frame_error("control frames can't have large payloads",FERR_PROTOCOL_VIOLATION); } m_header[0] &= (0xFF ^ BPB0_OPCODE); // clear op bits @@ -209,9 +276,9 @@ uint8_t frame::get_basic_size() const { } size_t frame::get_payload_size() const { - if (m_extended_header_bytes_needed > 0) { + if (m_state != STATE_READY && m_state != STATE_PAYLOAD) { // problem - throw "attempted to get payload size before reading full header"; + throw frame_error("attempted to get payload size before reading full header"); } return m_payload.size(); @@ -263,13 +330,13 @@ bool frame::is_control() const { void frame::set_payload_helper(size_t s) { if (s > max_payload_size) { - throw "requested payload is over implimentation defined limit"; + throw frame_error("requested payload is over implimentation defined limit",FERR_MSG_TOO_BIG); } // limits imposed by the websocket spec if (s > BASIC_PAYLOAD_LIMIT && get_opcode() > MAX_FRAME_OPCODE) { - throw "control frames can't have large payloads"; + throw frame_error("control frames can't have large payloads",FERR_PROTOCOL_VIOLATION); } if (s <= BASIC_PAYLOAD_LIMIT) { @@ -285,7 +352,7 @@ void frame::set_payload_helper(size_t s) { m_header[1] = BASIC_PAYLOAD_64BIT_CODE; *reinterpret_cast(&m_header[BASIC_HEADER_LENGTH]) = htonll(s); } else { - throw "payload size limit is 63 bits"; + throw frame_error("payload size limit is 63 bits",FERR_PROTOCOL_VIOLATION); } m_payload.resize(s); @@ -294,11 +361,11 @@ void frame::set_payload_helper(size_t s) { void frame::set_status(uint16_t status,const std::string message) { // check for valid statuses if (status < 1000 || status > 4999) { - throw server_error("Status codes must be in the range 1000-4999"); + throw frame_error("Status codes must be in the range 1000-4999"); } if (status == 1005 || status == 1006) { - throw server_error("Status codes 1005 and 1006 are reserved for internal use and cannot be written to a frame."); + throw frame_error("Status codes 1005 and 1006 are reserved for internal use and cannot be written to a frame."); } m_payload.resize(2+message.size()); @@ -326,9 +393,13 @@ std::string frame::print_frame() const { f << std::hex << (unsigned short)m_header[i] << " "; } // print message - std::vector::const_iterator it; - for (it = m_payload.begin(); it != m_payload.end(); it++) { - f << *it; + if (m_payload.size() > 50) { + f << "[payload of " << m_payload.size() << " bytes]"; + } else { + std::vector::const_iterator it; + for (it = m_payload.begin(); it != m_payload.end(); it++) { + f << *it; + } } return f.str(); } @@ -352,7 +423,10 @@ void frame::process_extended_header() { )); if (payload_size < s) { - throw frame_error("payload length not minimally encoded", + std::stringstream err; + err << "payload length not minimally encoded. Using 16 bit form for payload size: " << payload_size; + m_bytes_needed = payload_size; + throw frame_error(err.str(), FERR_PROTOCOL_VIOLATION); } @@ -365,6 +439,7 @@ void frame::process_extended_header() { )); if (payload_size <= PAYLOAD_16BIT_LIMIT) { + m_bytes_needed = payload_size; throw frame_error("payload length not minimally encoded", FERR_PROTOCOL_VIOLATION); } @@ -471,9 +546,7 @@ void frame::generate_masking_key() { //throw "masking key generation not implimented"; int32_t key = m_gen(); - - std::cout << "genkey: " << key << std::endl; - + //m_masking_key[0] = reinterpret_cast(&key)[0]; //m_masking_key[1] = reinterpret_cast(&key)[1]; //m_masking_key[2] = reinterpret_cast(&key)[2]; diff --git a/src/websocket_frame.hpp b/src/websocket_frame.hpp index fc37594c6b..9e4a7014df 100644 --- a/src/websocket_frame.hpp +++ b/src/websocket_frame.hpp @@ -58,11 +58,14 @@ public: static const uint8_t STATE_EXTENDED_HEADER = 2; static const uint8_t STATE_PAYLOAD = 3; static const uint8_t STATE_READY = 4; + static const uint8_t STATE_RECOVERY = 5; - static const uint16_t FERR_FATAL_SESSION_ERROR = 0; // must end session + static const uint16_t FERR_FATAL_SESSION_ERROR = 0; // force session end static const uint16_t FERR_SOFT_SESSION_ERROR = 1; // should log and ignore static const uint16_t FERR_PROTOCOL_VIOLATION = 2; // must end session static const uint16_t FERR_PAYLOAD_VIOLATION = 3; // should end session + static const uint16_t FERR_INTERNAL_SERVER_ERROR = 4; // cleanly end session + static const uint16_t FERR_MSG_TOO_BIG = 5; // basic payload byte flags static const uint8_t BPB0_OPCODE = 0x0F; @@ -86,9 +89,8 @@ public: // create an empty frame for writing into frame() : m_gen(m_rng, - boost::random::uniform_int_distribution<>(INT32_MIN,INT32_MAX)) { - // not sure if these are necessary with c++ but putting in just in case - memset(m_header,0,MAX_HEADER_LENGTH); + boost::random::uniform_int_distribution<>(INT32_MIN,INT32_MAX)),m_degraded(false) { + reset(); } uint8_t get_state() const; @@ -156,12 +158,12 @@ public: private: uint8_t m_state; uint64_t m_bytes_needed; + bool m_degraded; char m_header[MAX_HEADER_LENGTH]; std::vector m_payload; char m_masking_key[4]; - unsigned int m_extended_header_bytes_needed; boost::random::random_device m_rng; boost::random::variate_generatoron_open(shared_from_this()); diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index 8837642f2a..1174a36f86 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -47,6 +47,7 @@ session::session (boost::asio::io_service& io_service, websocketpp::connection_handler_ptr defc, uint64_t buf_size) : 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), @@ -165,9 +166,6 @@ void session::send_close(uint16_t status,const std::string &message) { m_state = STATE_CLOSING; - m_close_code = status; - m_close_message = message; - m_local_close_code = status; m_local_close_msg = message; @@ -177,8 +175,8 @@ void session::send_close(uint16_t status,const std::string &message) { if (status == CLOSE_STATUS_NO_STATUS) { m_write_frame.set_status(CLOSE_STATUS_NORMAL,""); } else if (status == CLOSE_STATUS_ABNORMAL_CLOSE) { - // unknown internal error, don't set a status? use protocol error? - log("Tried to disconnect with status ABNORMAL_CLOSE",LOG_DEBUG); + // Internal implimentation error. There is no good close code for this. + m_write_frame.set_status(CLOSE_STATUS_POLICY_VIOLATION,message); } else { m_write_frame.set_status(status,message); } @@ -210,75 +208,136 @@ void session::pong(const std::string &msg) { write_frame(); } +void session::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 session::handle_read_frame(const boost::system::error_code& error) { - if (error) { - handle_error("Error reading extended frame header",error); - // TODO: close behavior + if (m_state != STATE_OPEN && m_state != STATE_CLOSING) { + log("handle_read_frame called in invalid state",LOG_ERROR); return; } - if (m_state != STATE_OPEN && m_state != STATE_CLOSING) { - // stop processing frames. - log("handle_read_frame called in invalid state",LOG_ERROR); - 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); - try { - while (m_buf.size() > 0) { - m_read_frame.consume(s); - if (m_read_frame.get_state() == frame::STATE_READY) { - process_frame(); - - // should we break? + while (m_buf.size() > 0 && m_state != STATE_CLOSED) { + try { + if (m_read_frame.get_bytes_needed() == 0) { + throw frame_error("have bytes that no frame needs",frame::FERR_FATAL_SESSION_ERROR); } + + // Consume will read bytes from s + // will throw a frame_error on error. + + std::stringstream err; + + 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.get_state() == frame::STATE_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. + err.str(""); + err << "processing frame " << m_buf.size(); + log(err.str(),LOG_DEBUG); + process_frame(); + } + } catch (const frame_error& e) { + std::stringstream err; + err << "Caught frame exception: " << e.what(); + + access_log(e.what(),ALOG_FRAME); + log(err.str(),LOG_ERROR); + + // process different types of frame errors + // + if (e.code() == frame::FERR_PROTOCOL_VIOLATION) { + send_close(CLOSE_STATUS_PROTOCOL_ERROR, e.what()); + } else if (e.code() == frame::FERR_PAYLOAD_VIOLATION) { + send_close(CLOSE_STATUS_INVALID_PAYLOAD, e.what()); + } else if (e.code() == frame::FERR_INTERNAL_SERVER_ERROR) { + send_close(CLOSE_STATUS_ABNORMAL_CLOSE, e.what()); + } else if (e.code() == frame::FERR_SOFT_SESSION_ERROR) { + // ignore and continue processing frames + continue; + } else { + // Fatal error, forcibly end connection immediately. + drop_tcp(true); + } + + break; } - } catch (const frame_error& e) { - std::stringstream err; - err << "Caught frame exception: " << e.what(); - - access_log(e.what(),ALOG_FRAME); - log(err.str(),LOG_ERROR); - - if (e.code() == frame::FERR_PROTOCOL_VIOLATION) { - disconnect(CLOSE_STATUS_PROTOCOL_ERROR, e.what()); - } else if (e.code() == frame::FERR_PAYLOAD_VIOLATION) { - disconnect(CLOSE_STATUS_INVALID_PAYLOAD, e.what()); - } else if (e.code() == frame::FERR_SOFT_SESSION_ERROR) { - // ??? - } else { - // Fatal error - disconnect(CLOSE_STATUS_NO_STATUS, ""); - } - - disconnect(CLOSE_STATUS_PROTOCOL_ERROR,""); - - // TODO: close behavior - return; } + + 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) { - // stop processing frames. + if ((m_state == STATE_OPEN || m_state == STATE_CLOSING) && m_read_frame.get_bytes_needed() > 0) { + std::stringstream msg; + msg << "starting async read for " << m_read_frame.get_bytes_needed() << " bytes."; + + log(msg.str(),LOG_DEBUG); + + // 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, + shared_from_this(), + boost::asio::placeholders::error + ) + ); + } else if (m_state == STATE_CLOSED) { + log_close_result(); + + if (m_local_interface) { + m_local_interface->on_close(shared_from_this(),m_remote_close_code,m_remote_close_msg); + } + } else { log("handle_read_frame called in invalid state",LOG_ERROR); - return; } - - // read more - 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, - shared_from_this(), - boost::asio::placeholders::error - ) - ); } void session::process_frame () { + log("process_frame",LOG_DEBUG); + if (m_state == STATE_OPEN) { switch (m_read_frame.get_opcode()) { case frame::CONTINUATION_FRAME: @@ -291,6 +350,7 @@ void session::process_frame () { process_binary(); break; case frame::CONNECTION_CLOSE: + log("process_close",LOG_DEBUG); process_close(); break; case frame::PING: @@ -300,8 +360,8 @@ void session::process_frame () { process_pong(); break; default: - disconnect(CLOSE_STATUS_PROTOCOL_ERROR,"Invalid Opcode"); - // TODO: close behavior + throw frame_error("Invalid Opcode", + frame::FERR_PROTOCOL_VIOLATION); break; } } else if (m_state == STATE_CLOSING) { @@ -309,42 +369,24 @@ void session::process_frame () { process_close(); } else { // Ignore all other frames in closing state + log("ignoring this frame",LOG_DEBUG); } } else { // Recieved message before or after connection was opened/closed - // TODO: close behavior - return; + throw frame_error("process_frame called from invalid state"); } - // check if there was an error processing this frame and fail the connection - if (m_error) { - log("Connection has been closed uncleanly",LOG_ERROR); - // TODO: close behavior - return; - } - - if (m_status == CLOSED) { - log_close_result(); - - if (m_local_interface) { - m_local_interface->on_close(shared_from_this(), - m_close_code, - m_close_message); - } - // TODO: close behavior - return; - } - m_read_frame.reset(); } void session::handle_write_frame (const boost::system::error_code& error) { if (error) { - handle_error("Error writing frame data",error); - // TODO: close behavior + log_error("Error writing frame data",error); + drop_tcp(false); } - //std::cout << "Successfully wrote frame." << std::endl; + access_log("handle_write_frame complete",ALOG_FRAME); + m_writing = false; } void session::process_ping() { @@ -417,11 +459,13 @@ void session::process_close() { m_remote_close_msg = message; if (m_state == STATE_OPEN) { + log("process_close sending ack",LOG_DEBUG); // This is the case where the remote initiated the close. m_closed_by_me = false; - // TODO: close behavior - disconnect(status,message); + // send acknowledgement + send_close(status,message); } else if (m_state == STATE_CLOSING) { + log("process_close got ack",LOG_DEBUG); // this is an ack of our close message m_closed_by_me = true; } else { @@ -501,7 +545,9 @@ void session::write_frame() { ); log("Write Frame: "+m_write_frame.print_frame(),LOG_DEBUG); - + + m_writing = true; + boost::asio::async_write( m_socket, data, @@ -547,19 +593,13 @@ void session::log_open_result() { access_log(msg.str(),ALOG_HANDSHAKE); } -void session::handle_error(std::string msg, - const boost::system::error_code& error) { - std::stringstream e; +// this is called when an async asio call encounters an error +void session::log_error(std::string msg,const boost::system::error_code& e) { + std::stringstream err; - e << "[Connection " << this << "] " << msg << " (" << error << ")"; + err << "[Connection " << this << "] " << msg << " (" << e << ")"; - log(e.str(),LOG_ERROR); - - if (m_local_interface) { - m_local_interface->on_close(shared_from_this(),1006,e.str()); - } - - m_error = true; + log(err.str(),LOG_ERROR); } // validates status codes that the end application is allowed to use @@ -574,3 +614,12 @@ bool session::validate_app_close_status(uint16_t status) { return false; } + +void session::drop_tcp(bool dropped_by_me) { + if (m_socket.is_open()) { + m_socket.shutdown(tcp::socket::shutdown_both); + m_socket.close(); + } + m_dropped_by_me = dropped_by_me; + m_state = STATE_CLOSED; +} diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index d4ce03a0bd..03a74d56fb 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -25,6 +25,42 @@ * */ +/* + + 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 + */ + #ifndef WEBSOCKET_SESSION_HPP #define WEBSOCKET_SESSION_HPP @@ -150,6 +186,7 @@ protected: virtual void write_handshake() = 0; virtual void read_handshake() = 0; + void read_frame(); void handle_read_frame (const boost::system::error_code& error); // write m_write_frame out to the socket. @@ -182,13 +219,12 @@ protected: void log_close_result(); void log_open_result(); - - // prints a diagnostic message and disconnects the local interface - void handle_error(std::string msg,const boost::system::error_code& error); + void log_error(std::string msg,const boost::system::error_code& e); // misc helpers bool validate_app_close_status(uint16_t status); void send_close(uint16_t status,const std::string& reason); + void drop_tcp(bool dropped_by_me = true); private: std::string get_header(const std::string& key, const header_list& list) const; @@ -215,18 +251,17 @@ protected: std::string m_server_http_string; // Mutable connection state; - uint8_t m_state; - uint16_t m_close_code; - std::string m_close_message; + uint8_t m_state; + bool m_writing; // Close state - uint16_t m_local_close_code; - std::string m_local_close_msg; - uint16_t m_remote_close_code; - std::string m_remote_close_msg; - bool m_was_clean; - bool m_closed_by_me; - bool m_dropped_by_me; + uint16_t m_local_close_code; + std::string m_local_close_msg; + uint16_t 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; @@ -234,7 +269,7 @@ protected: connection_handler_ptr m_local_interface; // Buffers - boost::asio::streambuf m_buf; + boost::asio::streambuf m_buf; // current message state uint32_t m_utf8_state; @@ -244,11 +279,11 @@ protected: frame::opcode m_current_opcode; // current frame state - frame m_read_frame; + frame m_read_frame; // unorganized - frame m_write_frame; - bool m_error; + frame m_write_frame; + bool m_error; }; // Exception classes @@ -273,13 +308,3 @@ public: } #endif // WEBSOCKET_SESSION_HPP - - - -// better debug printing system -// set acceptible origin and host headers -// case sensitive header values? e.g. websocket - - -// double check bugs in autobahn (sending wrong localhost:9000 header) not -// checking masking in the 9.x tests From c4da789430dac3eaa03df22d939ff43e04082f55 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Fri, 14 Oct 2011 08:59:03 -0500 Subject: [PATCH 08/40] begins work on timeouts --- examples/echo_server/echo_server.cpp | 4 ++-- src/websocket_server.cpp | 4 ++-- src/websocket_server_session.cpp | 11 ++++++++++- src/websocket_session.cpp | 19 +++++++++++++++++++ src/websocket_session.hpp | 3 +++ websocketpp.xcodeproj/project.pbxproj | 3 ++- 6 files changed, 38 insertions(+), 6 deletions(-) diff --git a/examples/echo_server/echo_server.cpp b/examples/echo_server/echo_server.cpp index c8b616412e..4a5e21b1c7 100644 --- a/examples/echo_server/echo_server.cpp +++ b/examples/echo_server/echo_server.cpp @@ -61,8 +61,8 @@ int main(int argc, char* argv[]) { ); // setup server settings - server->set_alog_level(websocketpp::ALOG_OFF); - server->set_elog_level(websocketpp::LOG_OFF); + //server->set_alog_level(websocketpp::ALOG_OFF); + //server->set_elog_level(websocketpp::LOG_OFF); server->add_host(host); server->add_host(full_host); diff --git a/src/websocket_server.cpp b/src/websocket_server.cpp index 4303e9610b..a3fbdc56c6 100644 --- a/src/websocket_server.cpp +++ b/src/websocket_server.cpp @@ -37,8 +37,8 @@ using websocketpp::server; server::server(boost::asio::io_service& io_service, const tcp::endpoint& endpoint, websocketpp::connection_handler_ptr defc) - : m_elog_level(LOG_OFF), - m_alog_level(ALOG_OFF), + : m_elog_level(LOG_ALL), + m_alog_level(ALOG_ALL), m_max_message_size(DEFAULT_MAX_MESSAGE_SIZE), m_io_service(io_service), m_acceptor(io_service, endpoint), diff --git a/src/websocket_server_session.cpp b/src/websocket_server_session.cpp index a02e3edac3..8e41a4c312 100644 --- a/src/websocket_server_session.cpp +++ b/src/websocket_server_session.cpp @@ -92,7 +92,16 @@ void server_session::select_extension(const std::string& val) { } void server_session::read_handshake() { - // TODO: pass maximum size to m_buf cosntructor to prevent DoS here + m_timer.expires_from_now(boost::posix_time::seconds(5)); + + m_timer.async_wait( + boost::bind( + &session::handle_timer_expired, + this, + boost::asio::placeholders::error + ) + ); + boost::asio::async_read_until( m_socket, m_buf, diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index 1174a36f86..ca56838b03 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -56,6 +56,7 @@ session::session (boost::asio::io_service& io_service, m_socket(io_service), m_io_service(io_service), 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) {} @@ -389,6 +390,24 @@ void session::handle_write_frame (const boost::system::error_code& error) { m_writing = false; } + +void session::handle_timer_expired (const boost::system::error_code& error) { + if (error) { + if (error == boost::asio::error::operation_aborted) { + log("timer was aborted",LOG_DEBUG); + //drop_tcp(false); + } else { + log("timer ended with error",LOG_DEBUG); + } + return; + } + + log("timer ended without error",LOG_DEBUG); + + +} + + void session::process_ping() { access_log("Ping",ALOG_MISC_CONTROL); // TODO: on_ping diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index 03a74d56fb..ab576aaee6 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -193,6 +193,8 @@ protected: void write_frame(); void handle_write_frame (const boost::system::error_code& error); + void handle_timer_expired(const boost::system::error_code& error); + // helper functions for processing each opcode void process_frame(); void process_ping(); @@ -267,6 +269,7 @@ protected: tcp::socket m_socket; boost::asio::io_service& m_io_service; connection_handler_ptr m_local_interface; + boost::asio::deadline_timer m_timer; // Buffers boost::asio::streambuf m_buf; diff --git a/websocketpp.xcodeproj/project.pbxproj b/websocketpp.xcodeproj/project.pbxproj index 46d48c15f3..c44ea01195 100644 --- a/websocketpp.xcodeproj/project.pbxproj +++ b/websocketpp.xcodeproj/project.pbxproj @@ -496,7 +496,7 @@ B6DF1C471434A5940029A1B1 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0410; + LastUpgradeCheck = 0420; }; buildConfigurationList = B6DF1C4A1434A5940029A1B1 /* Build configuration list for PBXProject "websocketpp" */; compatibilityVersion = "Xcode 3.2"; @@ -835,6 +835,7 @@ B6CF18251437C397009295BE /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; B6DF1C4A1434A5940029A1B1 /* Build configuration list for PBXProject "websocketpp" */ = { isa = XCConfigurationList; From 63fe20e2a706095c2cd19443e2a496be57282de1 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Tue, 18 Oct 2011 11:29:00 -0500 Subject: [PATCH 09/40] documentation --- .gitignore | 3 ++- readme.txt | 53 +++++++++++++++++++++++++++++++++++++++ src/websocket_session.hpp | 11 ++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 48d6c63c0c..5c1b2fbd4f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ test/basic/tests libwebsocketpp.dylib.0.1.0 websocketpp.xcodeproj/xcuserdata/* -websocketpp.xcodeproj/project.xcworkspace/xcuserdata/* \ No newline at end of file +websocketpp.xcodeproj/project.xcworkspace/xcuserdata/* +policy_based_notes.hpp diff --git a/readme.txt b/readme.txt index 8570367bc7..8fbeeb9d81 100644 --- a/readme.txt +++ b/readme.txt @@ -105,3 +105,56 @@ Acknowledgements - Autobahn test suite - testing by Keith Brisson + + +API spec notes + + +6.59 + + +Server API +websocketpp.hpp + +create a websocketpp::server_ptr initialized to a new websocketpp::server object + +the server constructor will need three things. +- A boost::asio::io_service object to use to manage its async operations +- A boost::asio::ip::tcp::endpoint to listen to for new connections +- An object that impliments the websocketpp::connection_handler interface to provide callback functions (See Handler API) + +After construction the server object will be in the initialization state. At this time you can set up server config options either via calling individual set option commands or by loading them in a batch from a config file. + +The only required option is that at least one host value must be set. Incoming websocket connections must specify a host value that they wish to connect to and if the server object does not have that host value in it's list of canonical hosts it will reject the connection. + +[note about settings that can be changed live?] + +Once the server has been configured the way you want, call the start_accept() method. This will add the first async call to your io_service. If your io_service was already running, the server will start accepting connections immediately. If not you will need to call io_service.run() to start it. + +Client API +include websocketpp.hpp + +create a websocketpp::client_ptr initialized to a new websocketpp::client object + +the client constructor will need: +- A boost::asio::io_service object to use to manage its async operations +- An object that impliments the websocketpp::connection_handler interface to privde callback functions (See Handler API) + +After construction, the client object will be in the initialization state. At this time you can set up client config options either via calling individual set options commands or by loading them in a batch from a config file. + +Opening a new connection: +Per the websocket spec, a client can only have one connection in the connecting state at a time. Client method new_session() will create a new session and return a shared pointer to it. After this point new_session will throw an exception if you attempt to call it again before the most recently created session has either successfully connected or failed to connect. new_session() +- call websocketpp::client::new_session(). This will return a session_ptr + + +Handler API + + + + +Session API + + + + + diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index ab576aaee6..facff2a2ae 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -59,6 +59,17 @@ 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? + */ #ifndef WEBSOCKET_SESSION_HPP From dd4db981d537b2e4ba2e09ae614fc48785fd1a72 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Tue, 18 Oct 2011 17:23:41 -0500 Subject: [PATCH 10/40] more docs --- readme.txt | 120 ++++++++++++++++++++++++-- src/websocket_client.hpp | 25 ------ src/websocket_connection_handler.hpp | 63 +++++++++----- src/websocket_server.hpp | 25 ------ websocketpp.xcodeproj/project.pbxproj | 10 +++ 5 files changed, 162 insertions(+), 81 deletions(-) diff --git a/readme.txt b/readme.txt index 8fbeeb9d81..087106ffd2 100644 --- a/readme.txt +++ b/readme.txt @@ -110,10 +110,8 @@ Acknowledgements API spec notes -6.59 - -Server API +## Server API ## websocketpp.hpp create a websocketpp::server_ptr initialized to a new websocketpp::server object @@ -131,7 +129,9 @@ The only required option is that at least one host value must be set. Incoming w Once the server has been configured the way you want, call the start_accept() method. This will add the first async call to your io_service. If your io_service was already running, the server will start accepting connections immediately. If not you will need to call io_service.run() to start it. -Client API +Once the server has started it will accept new connections. A new session object will be created for each connection accepted. The session will perform the websocket handshake and if it is successful begin reading frames. The session will continue reading frames until an error occurs or a connection close frame is seen. The session will notify the handler that it was initilized with (see Handler API) as necessary. The Session API defines how a handler (or other part of the end application) can interact with the session (to get information about the session, send messages back to the client, etc) + +## Client API ## include websocketpp.hpp create a websocketpp::client_ptr initialized to a new websocketpp::client object @@ -147,14 +147,118 @@ Per the websocket spec, a client can only have one connection in the connecting - call websocketpp::client::new_session(). This will return a session_ptr -Handler API +## Handler API ## +The handler API defines the interface that a websocketpp session will use to communicate information about the session state and new messages to your application. + +A client or server must be initialized with a default handler that will be used for all sessions. The default handler may pass a session off to another handler as necessary. + +A handler must impliment the following methods: +- validate(session_ptr) +- on_fail(session_ptr) +- on_open(session_ptr) +- on_close(session_ptr) +- on_message(session_ptr,const std::vector &) +- on_message(session_ptr,const std::string &) + +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 member function. + +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. + +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. + +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. + +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. + +TODO: Notes about thread safety + + +## Session API ## +The Session API allows a handler to look up information about a session as well as interact with that session (send messages, close the connection, etc) + +Session pointers are returned with every handler callback as well as every call to websocketpp::client::connect. + + +Handler Interface: +- set_handler(connection_handler_ptr) + +Handshake Interface: +For the client these methods are valid after the server's handshake has been received. This is guaranteed to be the case by the time `on_open` is called. +For the server these methods are valid after the client's handshake has been received. This is guaranteed to be the case by the time `validate` is called. + +- const std::string& get_subprotocol() const; +- const std::string& get_resource() const; +- const std::string& get_origin() const; +- std::string get_client_header(const std::string&) const; +- std::string get_server_header(const std::string&) const; +- const std::vector& get_extensions() const; +- unsigned int get_version() const; + +Frame Interface +- void send(const std::string &); +- void send(const std::vector &); +- void ping(const std::string &); +- void pong(const std::string &); + +These methods are valid only for open connections. They will throw an exception if called from any other state. + +WebSocket++ does not queue messages. As such only one send operation can be occuring at once. +TODO: failure behavior. OPTIONS: +- send will throw a `session_busy` exception if busy +- send will return true/false +- a callback could be defined letting the handler know that it is safe to write again. + +Session Interface +- void close(uint16_t status,const std::string &reason); +- bool is_server() const; -Session API - - +-------------------------------- +screwing around with a policy based refactoring +-------------------------------- +template +class endpoint : public WebSocketRole, Logger { +public: + endpoint(connection_handler_ptr); + + size_t get_connected_client_count() const; + + void set_endpoint(const tcp::endpoint& endpoint); // asio::bind + +private: + std::list m_connections; + connection_handler_ptr m_handler; + + boost::asio::io_service m_io_service; + tcp::acceptor m_acceptor; +} +class server_interface { +public: + void add_host(const std::string &host); + void remove_host(const std::string &host); + bool validate_host(const std::string &host) const; + + void set_max_message_size(uint64_t size); + bool validate_message_size(uint64_t size) const; + + void start() { + // start_accept() + // io_service.run() + } +private: + void start_accept(); + void handle_accept(session_ptr session, const boost::system::error_code&) + + std::set m_hosts; + uint64_t m_max_message_size; +} +class client_interface { + +} diff --git a/src/websocket_client.hpp b/src/websocket_client.hpp index 89bbe27395..0ef1437178 100644 --- a/src/websocket_client.hpp +++ b/src/websocket_client.hpp @@ -61,31 +61,6 @@ private: class client : public boost::enable_shared_from_this { public: - // 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; - */ static const uint16_t CLIENT_STATE_NULL = 0; static const uint16_t CLIENT_STATE_INITIALIZED = 1; static const uint16_t CLIENT_STATE_CONNECTING = 2; diff --git a/src/websocket_connection_handler.hpp b/src/websocket_connection_handler.hpp index 5a1778e274..9c48a46358 100644 --- a/src/websocket_connection_handler.hpp +++ b/src/websocket_connection_handler.hpp @@ -49,39 +49,56 @@ public: // 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 client) = 0; - + virtual void validate(session_ptr session) = 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(session_ptr session) = 0; - // this will be called once the connected websocket is avaliable for - // writing messages. client may be a new websocket session or an existing - // session that was recently passed to this handler. - virtual void on_open(session_ptr client) = 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(session_ptr session) = 0; - // this will be called when the connected websocket is no longer avaliable - // for writing messages. This occurs under the following conditions: - // - Disconnect message recieved from the remote endpoint - // - Someone (usually this object) calls the disconnect method of session - // - A disconnect acknowledgement is recieved (in case another object - // calls the disconnect method of session - // - The connection handler assigned to this client was set to another - // handler - virtual void on_close(session_ptr client,uint16_t status,const std::string &reason) = 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; - // this will be called when a text message is recieved. Text will be - // encoded as UTF-8. - virtual void on_message(session_ptr client,const std::string &msg) = 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; - // this will be called when a binary message is recieved. Argument is a - // vector of the raw bytes in the message body. - virtual void on_message(session_ptr client, - const std::vector &data) = 0; }; diff --git a/src/websocket_server.hpp b/src/websocket_server.hpp index a1415b63ed..d294c13505 100644 --- a/src/websocket_server.hpp +++ b/src/websocket_server.hpp @@ -61,31 +61,6 @@ private: class server : public boost::enable_shared_from_this { public: - // 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; -*/ server(boost::asio::io_service& io_service, const tcp::endpoint& endpoint, connection_handler_ptr defc); diff --git a/websocketpp.xcodeproj/project.pbxproj b/websocketpp.xcodeproj/project.pbxproj index c44ea01195..70da3c369f 100644 --- a/websocketpp.xcodeproj/project.pbxproj +++ b/websocketpp.xcodeproj/project.pbxproj @@ -163,6 +163,7 @@ B6DF1CD11435ED910029A1B1 /* echo_server */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = echo_server; sourceTree = BUILT_PRODUCTS_DIR; }; B6DF1CE11435F1860029A1B1 /* libboost_system.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_system.dylib; path = usr/local/lib/libboost_system.dylib; sourceTree = SDKROOT; }; B6DF1CE31435F8250029A1B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + B6FE8CE2144DE17F00B32547 /* readme.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = readme.txt; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -236,6 +237,7 @@ B682888C1437464A002BA48B /* libboost_random.dylib */, B682888A14374623002BA48B /* libboost_system.dylib */, B6DF1CE31435F8250029A1B1 /* Foundation.framework */, + B6FE8CE4144DE18900B32547 /* documentation */, B6DF1CC61435ED380029A1B1 /* examples */, B6DF1CC51435ECE40029A1B1 /* libraries */, B6DF1C7F1434ABB70029A1B1 /* src */, @@ -359,6 +361,14 @@ name = chat_server; sourceTree = ""; }; + B6FE8CE4144DE18900B32547 /* documentation */ = { + isa = PBXGroup; + children = ( + B6FE8CE2144DE17F00B32547 /* readme.txt */, + ); + name = documentation; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ From 333fd6101e4d3150b3f0b988072447010931eb1e Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 20 Oct 2011 07:09:45 -0500 Subject: [PATCH 11/40] api sketching --- readme.txt | 35 +++++++++++++++++ src/websocket_endpoint.hpp | 55 +++++++++++++++++++++++++++ websocketpp.xcodeproj/project.pbxproj | 6 +++ 3 files changed, 96 insertions(+) create mode 100644 src/websocket_endpoint.hpp diff --git a/readme.txt b/readme.txt index 087106ffd2..6badd7dca1 100644 --- a/readme.txt +++ b/readme.txt @@ -260,5 +260,40 @@ private: } class client_interface { +public: + session_ptr connect(const std::string &url); +protected: + +private: + +} + + + +template +class session : public HandshakePolicy { +public: + session(); +private: } + +namespace websocketpp { +namespace handshake { + +/* a handshake policy must define: +void on_connect(); +bool is_server() const; + +*/ + +class server { + +} + +class client { + +} + +} // handshake +} // websocketpp \ No newline at end of file diff --git a/src/websocket_endpoint.hpp b/src/websocket_endpoint.hpp new file mode 100644 index 0000000000..410ec0f000 --- /dev/null +++ b/src/websocket_endpoint.hpp @@ -0,0 +1,55 @@ +/* + * 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: + virtual bool is_server() = 0; + // log + // access_log +}; + + + +} +#endif // WEBSOCKET_ENDPOINT_HPP diff --git a/websocketpp.xcodeproj/project.pbxproj b/websocketpp.xcodeproj/project.pbxproj index 70da3c369f..ac6f6b3ddc 100644 --- a/websocketpp.xcodeproj/project.pbxproj +++ b/websocketpp.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 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 */; }; B6CF18281437C3B1009295BE /* echo_client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6CF18131437C370009295BE /* echo_client.cpp */; }; B6CF18291437C3B1009295BE /* echo_client_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6CF18141437C370009295BE /* echo_client_handler.cpp */; }; B6CF182A1437C3BD009295BE /* libwebsocketpp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B6DF1C721434A8280029A1B1 /* libwebsocketpp.dylib */; }; @@ -124,6 +126,7 @@ B682888A14374623002BA48B /* libboost_system.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_system.dylib; path = usr/local/lib/libboost_system.dylib; sourceTree = SDKROOT; }; B682888C1437464A002BA48B /* libboost_random.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_random.dylib; path = usr/local/lib/libboost_random.dylib; sourceTree = SDKROOT; }; B682888E14374689002BA48B /* libboost_thread.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_thread.dylib; path = usr/local/lib/libboost_thread.dylib; sourceTree = SDKROOT; }; + B6BE76E9144EF53000716A77 /* websocket_endpoint.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = websocket_endpoint.hpp; path = src/websocket_endpoint.hpp; sourceTree = ""; }; B6CF18131437C370009295BE /* echo_client.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = echo_client.cpp; sourceTree = ""; }; B6CF18141437C370009295BE /* echo_client_handler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = echo_client_handler.cpp; sourceTree = ""; }; B6CF18151437C370009295BE /* echo_client_handler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = echo_client_handler.hpp; sourceTree = ""; }; @@ -265,6 +268,7 @@ B6DF1C941434AC470029A1B1 /* websocket_client.cpp */, B6DF1C951434AC470029A1B1 /* websocket_client.hpp */, B6DF1C961434AC470029A1B1 /* websocket_connection_handler.hpp */, + B6BE76E9144EF53000716A77 /* websocket_endpoint.hpp */, B6DF1C971434AC470029A1B1 /* websocket_frame.cpp */, B6DF1C981434AC470029A1B1 /* websocket_frame.hpp */, B6DF1C991434AC470029A1B1 /* websocket_server_session.cpp */, @@ -388,6 +392,7 @@ B6DF1CB01434AC470029A1B1 /* websocket_server_session.hpp in Headers */, B6DF1CB41434AC470029A1B1 /* websocket_server.hpp in Headers */, B6DF1CB81434AC470029A1B1 /* websocket_session.hpp in Headers */, + B6BE76EA144EF53000716A77 /* websocket_endpoint.hpp in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -407,6 +412,7 @@ B6DF1CB11434AC470029A1B1 /* websocket_server_session.hpp in Headers */, B6DF1CB51434AC470029A1B1 /* websocket_server.hpp in Headers */, B6DF1CB91434AC470029A1B1 /* websocket_session.hpp in Headers */, + B6BE76EB144EF53000716A77 /* websocket_endpoint.hpp in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; From 41f60162ec61fdda37f8633336784631f3b033a6 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 20 Oct 2011 08:45:10 -0500 Subject: [PATCH 12/40] preliminary work on various timeouts, adjusts the handler API to be more consistant --- examples/echo_server/echo.hpp | 3 ++- src/websocket_server_session.cpp | 7 +++++-- src/websocket_server_session.hpp | 1 + src/websocket_session.cpp | 18 +++++++++++++++++- src/websocket_session.hpp | 1 + 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/examples/echo_server/echo.hpp b/examples/echo_server/echo.hpp index 3416f8b0b2..c2c989a6a8 100644 --- a/examples/echo_server/echo.hpp +++ b/examples/echo_server/echo.hpp @@ -48,8 +48,9 @@ public: // an echo server is stateless. // The handler has no need to keep track of connected clients. + void on_fail(session_ptr client) {} void on_open(session_ptr client) {} - void on_close(session_ptr client,uint16_t status,const std::string &reason) {} + void on_close(session_ptr client) {} // both text and binary messages are echoed back to the sending client. void on_message(session_ptr client,const std::string &msg); diff --git a/src/websocket_server_session.cpp b/src/websocket_server_session.cpp index 8e41a4c312..d8f0aa6c38 100644 --- a/src/websocket_server_session.cpp +++ b/src/websocket_server_session.cpp @@ -96,8 +96,8 @@ void server_session::read_handshake() { m_timer.async_wait( boost::bind( - &session::handle_timer_expired, - this, + &session::handle_handshake_expired, + shared_from_this(), boost::asio::placeholders::error ) ); @@ -342,6 +342,9 @@ void server_session::handle_write_handshake(const boost::system::error_code& err m_state = STATE_OPEN; + // stop the handshake timer + m_timer.cancel(); + if (m_local_interface) { m_local_interface->on_open(shared_from_this()); } diff --git a/src/websocket_server_session.hpp b/src/websocket_server_session.hpp index c8b023e8bd..538edaba72 100644 --- a/src/websocket_server_session.hpp +++ b/src/websocket_server_session.hpp @@ -103,6 +103,7 @@ protected: std::size_t bytes_transferred); + private: protected: diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index ca56838b03..a2fb733f79 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -329,7 +329,8 @@ void session::handle_read_frame(const boost::system::error_code& error) { log_close_result(); if (m_local_interface) { - m_local_interface->on_close(shared_from_this(),m_remote_close_code,m_remote_close_msg); + // TODO: make sure close code/msg are properly set. + m_local_interface->on_close(shared_from_this()); } } else { log("handle_read_frame called in invalid state",LOG_ERROR); @@ -407,6 +408,21 @@ void session::handle_timer_expired (const boost::system::error_code& error) { } +void session::handle_handshake_expired (const boost::system::error_code& error) { + if (error) { + if (error == boost::asio::error::operation_aborted) { + log("timer was aborted",LOG_DEBUG); + //drop_tcp(false); + } else { + log("Unexpected handshake timer error.",LOG_DEBUG); + drop_tcp(false); + } + return; + } + + log("Handshake timed out",LOG_DEBUG); + drop_tcp(false); +} void session::process_ping() { access_log("Ping",ALOG_MISC_CONTROL); diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index facff2a2ae..b91b9ed38a 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -205,6 +205,7 @@ protected: void handle_write_frame (const boost::system::error_code& error); void handle_timer_expired(const boost::system::error_code& error); + void handle_handshake_expired(const boost::system::error_code& error); // helper functions for processing each opcode void process_frame(); From 1d7cb1bee32496f564c559254d8437471f4269ec Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 20 Oct 2011 19:51:39 -0500 Subject: [PATCH 13/40] updates to connection handler --- src/websocket_connection_handler.hpp | 26 ++++++++++++++++---------- src/websocket_endpoint.hpp | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/websocket_connection_handler.hpp b/src/websocket_connection_handler.hpp index 9c48a46358..f3d93e43bf 100644 --- a/src/websocket_connection_handler.hpp +++ b/src/websocket_connection_handler.hpp @@ -58,16 +58,7 @@ public: // 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) = 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(session_ptr session) = 0; + 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 @@ -99,6 +90,21 @@ public: // 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) {} }; diff --git a/src/websocket_endpoint.hpp b/src/websocket_endpoint.hpp index 410ec0f000..4f80bf992f 100644 --- a/src/websocket_endpoint.hpp +++ b/src/websocket_endpoint.hpp @@ -42,7 +42,7 @@ namespace websocketpp { namespace websocketpp { -class endpoint { +class endpoint : public boost::enable_shared_from_this { public: virtual bool is_server() = 0; // log From 95e6a1f6a9fb3231af42a2b04e18aa3180828510 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 15:58:17 +0200 Subject: [PATCH 14/40] moved to version specific dir --- windows/{ => vcpp2008}/.gitignore | 0 windows/{ => vcpp2008}/examples/chatclient.vcproj | 0 windows/{ => vcpp2008}/examples/chatserver.vcproj | 0 windows/{ => vcpp2008}/examples/echoserver.vcproj | 0 windows/{ => vcpp2008}/stdint.h | 0 windows/{ => vcpp2008}/websocketpp.sln | 0 windows/{ => vcpp2008}/websocketpp.vcproj | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename windows/{ => vcpp2008}/.gitignore (100%) rename windows/{ => vcpp2008}/examples/chatclient.vcproj (100%) rename windows/{ => vcpp2008}/examples/chatserver.vcproj (100%) rename windows/{ => vcpp2008}/examples/echoserver.vcproj (100%) rename windows/{ => vcpp2008}/stdint.h (100%) rename windows/{ => vcpp2008}/websocketpp.sln (100%) rename windows/{ => vcpp2008}/websocketpp.vcproj (100%) diff --git a/windows/.gitignore b/windows/vcpp2008/.gitignore similarity index 100% rename from windows/.gitignore rename to windows/vcpp2008/.gitignore diff --git a/windows/examples/chatclient.vcproj b/windows/vcpp2008/examples/chatclient.vcproj similarity index 100% rename from windows/examples/chatclient.vcproj rename to windows/vcpp2008/examples/chatclient.vcproj diff --git a/windows/examples/chatserver.vcproj b/windows/vcpp2008/examples/chatserver.vcproj similarity index 100% rename from windows/examples/chatserver.vcproj rename to windows/vcpp2008/examples/chatserver.vcproj diff --git a/windows/examples/echoserver.vcproj b/windows/vcpp2008/examples/echoserver.vcproj similarity index 100% rename from windows/examples/echoserver.vcproj rename to windows/vcpp2008/examples/echoserver.vcproj diff --git a/windows/stdint.h b/windows/vcpp2008/stdint.h similarity index 100% rename from windows/stdint.h rename to windows/vcpp2008/stdint.h diff --git a/windows/websocketpp.sln b/windows/vcpp2008/websocketpp.sln similarity index 100% rename from windows/websocketpp.sln rename to windows/vcpp2008/websocketpp.sln diff --git a/windows/websocketpp.vcproj b/windows/vcpp2008/websocketpp.vcproj similarity index 100% rename from windows/websocketpp.vcproj rename to windows/vcpp2008/websocketpp.vcproj From 53fa78f0beeb7f2612d5384e2b5d7ba465e94145 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 16:00:54 +0200 Subject: [PATCH 15/40] add copy of vcpp2008 for 2010 adjustments --- windows/vcpp2010/.gitignore | 9 + windows/vcpp2010/examples/chatclient.vcproj | 201 +++++++++++++++ windows/vcpp2010/examples/chatserver.vcproj | 201 +++++++++++++++ windows/vcpp2010/examples/echoserver.vcproj | 202 +++++++++++++++ windows/vcpp2010/stdint.h | 44 ++++ windows/vcpp2010/websocketpp.sln | 54 ++++ windows/vcpp2010/websocketpp.vcproj | 271 ++++++++++++++++++++ 7 files changed, 982 insertions(+) create mode 100644 windows/vcpp2010/.gitignore create mode 100644 windows/vcpp2010/examples/chatclient.vcproj create mode 100644 windows/vcpp2010/examples/chatserver.vcproj create mode 100644 windows/vcpp2010/examples/echoserver.vcproj create mode 100644 windows/vcpp2010/stdint.h create mode 100644 windows/vcpp2010/websocketpp.sln create mode 100644 windows/vcpp2010/websocketpp.vcproj diff --git a/windows/vcpp2010/.gitignore b/windows/vcpp2010/.gitignore new file mode 100644 index 0000000000..f885bfa69a --- /dev/null +++ b/windows/vcpp2010/.gitignore @@ -0,0 +1,9 @@ +*.user +*.ncb +*.suo +Debug +Release +examples/*.user +examples/*.ncb +examples/Debug +examples/Release diff --git a/windows/vcpp2010/examples/chatclient.vcproj b/windows/vcpp2010/examples/chatclient.vcproj new file mode 100644 index 0000000000..4919d9f806 --- /dev/null +++ b/windows/vcpp2010/examples/chatclient.vcproj @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/windows/vcpp2010/examples/chatserver.vcproj b/windows/vcpp2010/examples/chatserver.vcproj new file mode 100644 index 0000000000..a0080e75a8 --- /dev/null +++ b/windows/vcpp2010/examples/chatserver.vcproj @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/windows/vcpp2010/examples/echoserver.vcproj b/windows/vcpp2010/examples/echoserver.vcproj new file mode 100644 index 0000000000..f0d6c88bea --- /dev/null +++ b/windows/vcpp2010/examples/echoserver.vcproj @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/windows/vcpp2010/stdint.h b/windows/vcpp2010/stdint.h new file mode 100644 index 0000000000..b68541271b --- /dev/null +++ b/windows/vcpp2010/stdint.h @@ -0,0 +1,44 @@ +/* + * 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 STDINT_WIN32_H +#define STDINT_WIN32_H + +/* This file emulates enough of stdint.h on Windows. */ +#define INT32_MIN (-2147483647i32 - 1) /* minimum signed 32 bit value */ +#define INT32_MAX 2147483647i32 /* maximum signed 32 bit value */ + +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned short uint16_t; +typedef short int16_t; +typedef unsigned int uint32_t; +typedef int int32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#endif diff --git a/windows/vcpp2010/websocketpp.sln b/windows/vcpp2010/websocketpp.sln new file mode 100644 index 0000000000..2c657c2785 --- /dev/null +++ b/windows/vcpp2010/websocketpp.sln @@ -0,0 +1,54 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "websocketpp", "websocketpp.vcproj", "{1C0FD04E-5ACA-4031-B3D1-320A5360C9D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{14E490FC-930E-40EE-B14A-84E2D98DEC9F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echoserver", "examples\echoserver.vcproj", "{B569A272-D7D3-404B-B5FB-9187C0EB9F48}" + ProjectSection(ProjectDependencies) = postProject + {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} = {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chatserver", "examples\chatserver.vcproj", "{2AFECE48-86DE-47D0-9263-DC0D203AA62D}" + ProjectSection(ProjectDependencies) = postProject + {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} = {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chatclient", "examples\chatclient.vcproj", "{116BFEDA-AF8E-4B3F-8508-ACC5EE89F905}" + ProjectSection(ProjectDependencies) = postProject + {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} = {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0}.Debug|Win32.ActiveCfg = Debug|Win32 + {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0}.Debug|Win32.Build.0 = Debug|Win32 + {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0}.Release|Win32.ActiveCfg = Release|Win32 + {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0}.Release|Win32.Build.0 = Release|Win32 + {B569A272-D7D3-404B-B5FB-9187C0EB9F48}.Debug|Win32.ActiveCfg = Debug|Win32 + {B569A272-D7D3-404B-B5FB-9187C0EB9F48}.Debug|Win32.Build.0 = Debug|Win32 + {B569A272-D7D3-404B-B5FB-9187C0EB9F48}.Release|Win32.ActiveCfg = Release|Win32 + {B569A272-D7D3-404B-B5FB-9187C0EB9F48}.Release|Win32.Build.0 = Release|Win32 + {2AFECE48-86DE-47D0-9263-DC0D203AA62D}.Debug|Win32.ActiveCfg = Debug|Win32 + {2AFECE48-86DE-47D0-9263-DC0D203AA62D}.Debug|Win32.Build.0 = Debug|Win32 + {2AFECE48-86DE-47D0-9263-DC0D203AA62D}.Release|Win32.ActiveCfg = Release|Win32 + {2AFECE48-86DE-47D0-9263-DC0D203AA62D}.Release|Win32.Build.0 = Release|Win32 + {116BFEDA-AF8E-4B3F-8508-ACC5EE89F905}.Debug|Win32.ActiveCfg = Debug|Win32 + {116BFEDA-AF8E-4B3F-8508-ACC5EE89F905}.Debug|Win32.Build.0 = Debug|Win32 + {116BFEDA-AF8E-4B3F-8508-ACC5EE89F905}.Release|Win32.ActiveCfg = Release|Win32 + {116BFEDA-AF8E-4B3F-8508-ACC5EE89F905}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B569A272-D7D3-404B-B5FB-9187C0EB9F48} = {14E490FC-930E-40EE-B14A-84E2D98DEC9F} + {2AFECE48-86DE-47D0-9263-DC0D203AA62D} = {14E490FC-930E-40EE-B14A-84E2D98DEC9F} + {116BFEDA-AF8E-4B3F-8508-ACC5EE89F905} = {14E490FC-930E-40EE-B14A-84E2D98DEC9F} + EndGlobalSection +EndGlobal diff --git a/windows/vcpp2010/websocketpp.vcproj b/windows/vcpp2010/websocketpp.vcproj new file mode 100644 index 0000000000..deea893c45 --- /dev/null +++ b/windows/vcpp2010/websocketpp.vcproj @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From fc59c8038a1382fc2222e32c579ecc3828900301 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 16:15:13 +0200 Subject: [PATCH 16/40] fix relative paths --- windows/vcpp2008/examples/chatclient.vcproj | 14 +++---- windows/vcpp2008/examples/chatserver.vcproj | 14 +++---- windows/vcpp2008/examples/echoserver.vcproj | 14 +++---- windows/vcpp2008/websocketpp.vcproj | 46 ++++++++++----------- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/windows/vcpp2008/examples/chatclient.vcproj b/windows/vcpp2008/examples/chatclient.vcproj index 4919d9f806..929efff01e 100644 --- a/windows/vcpp2008/examples/chatclient.vcproj +++ b/windows/vcpp2008/examples/chatclient.vcproj @@ -41,7 +41,7 @@ @@ -191,7 +191,7 @@ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > diff --git a/windows/vcpp2008/examples/chatserver.vcproj b/windows/vcpp2008/examples/chatserver.vcproj index a0080e75a8..d16f5eb89c 100644 --- a/windows/vcpp2008/examples/chatserver.vcproj +++ b/windows/vcpp2008/examples/chatserver.vcproj @@ -41,7 +41,7 @@ @@ -191,7 +191,7 @@ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > diff --git a/windows/vcpp2008/examples/echoserver.vcproj b/windows/vcpp2008/examples/echoserver.vcproj index f0d6c88bea..f2fc495064 100644 --- a/windows/vcpp2008/examples/echoserver.vcproj +++ b/windows/vcpp2008/examples/echoserver.vcproj @@ -41,7 +41,7 @@ @@ -192,7 +192,7 @@ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > diff --git a/windows/vcpp2008/websocketpp.vcproj b/windows/vcpp2008/websocketpp.vcproj index deea893c45..eb63530692 100644 --- a/windows/vcpp2008/websocketpp.vcproj +++ b/windows/vcpp2008/websocketpp.vcproj @@ -41,7 +41,7 @@ @@ -194,7 +194,7 @@ Name="sha" > @@ -205,46 +205,46 @@ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > @@ -252,7 +252,7 @@ Name="sha" > @@ -260,7 +260,7 @@ Name="utf8_validator" > From bdb7780f32ef6782fa2030ec957c184e8d3c5da4 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 16:20:38 +0200 Subject: [PATCH 17/40] remove old project files --- windows/vcpp2010/examples/chatclient.vcproj | 201 --------------- windows/vcpp2010/examples/chatserver.vcproj | 201 --------------- windows/vcpp2010/examples/echoserver.vcproj | 202 --------------- windows/vcpp2010/websocketpp.vcproj | 271 -------------------- 4 files changed, 875 deletions(-) delete mode 100644 windows/vcpp2010/examples/chatclient.vcproj delete mode 100644 windows/vcpp2010/examples/chatserver.vcproj delete mode 100644 windows/vcpp2010/examples/echoserver.vcproj delete mode 100644 windows/vcpp2010/websocketpp.vcproj diff --git a/windows/vcpp2010/examples/chatclient.vcproj b/windows/vcpp2010/examples/chatclient.vcproj deleted file mode 100644 index 4919d9f806..0000000000 --- a/windows/vcpp2010/examples/chatclient.vcproj +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/windows/vcpp2010/examples/chatserver.vcproj b/windows/vcpp2010/examples/chatserver.vcproj deleted file mode 100644 index a0080e75a8..0000000000 --- a/windows/vcpp2010/examples/chatserver.vcproj +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/windows/vcpp2010/examples/echoserver.vcproj b/windows/vcpp2010/examples/echoserver.vcproj deleted file mode 100644 index f0d6c88bea..0000000000 --- a/windows/vcpp2010/examples/echoserver.vcproj +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/windows/vcpp2010/websocketpp.vcproj b/windows/vcpp2010/websocketpp.vcproj deleted file mode 100644 index deea893c45..0000000000 --- a/windows/vcpp2010/websocketpp.vcproj +++ /dev/null @@ -1,271 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 7d60e093cd939812e5167c3dabf345960d7f1742 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 16:22:14 +0200 Subject: [PATCH 18/40] ignore vs2010 stuff --- windows/vcpp2010/.gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/windows/vcpp2010/.gitignore b/windows/vcpp2010/.gitignore index f885bfa69a..62b99499b0 100644 --- a/windows/vcpp2010/.gitignore +++ b/windows/vcpp2010/.gitignore @@ -1,9 +1,17 @@ +# files to be ignored: +# http://stackoverflow.com/questions/2538149/what-should-be-contained-in-a-global-subversion-ignore-pattern-for-visual-studio +# http://stackoverflow.com/questions/3922660/which-visual-c-file-types-should-be-committed-to-version-control +# http://msdn.microsoft.com/en-us/library/3awe4781%28v=VS.100%29.aspx + *.user *.ncb *.suo +*.sdf +*.opensdf Debug Release examples/*.user examples/*.ncb examples/Debug examples/Release +ipch/* From 8cb026725d9b22dc00348eb59e17676f6592de95 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 16:22:43 +0200 Subject: [PATCH 19/40] remove stdint which conflicts with vs2010 --- windows/vcpp2010/stdint.h | 44 --------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 windows/vcpp2010/stdint.h diff --git a/windows/vcpp2010/stdint.h b/windows/vcpp2010/stdint.h deleted file mode 100644 index b68541271b..0000000000 --- a/windows/vcpp2010/stdint.h +++ /dev/null @@ -1,44 +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 STDINT_WIN32_H -#define STDINT_WIN32_H - -/* This file emulates enough of stdint.h on Windows. */ -#define INT32_MIN (-2147483647i32 - 1) /* minimum signed 32 bit value */ -#define INT32_MAX 2147483647i32 /* maximum signed 32 bit value */ - -typedef unsigned char uint8_t; -typedef signed char int8_t; -typedef unsigned short uint16_t; -typedef short int16_t; -typedef unsigned int uint32_t; -typedef int int32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; - -#endif From 1e4efbb1f3ba718e9678a36a9478426feb637ea2 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 16:22:57 +0200 Subject: [PATCH 20/40] add build readme --- windows/vcpp2010/readme.txt | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 windows/vcpp2010/readme.txt diff --git a/windows/vcpp2010/readme.txt b/windows/vcpp2010/readme.txt new file mode 100644 index 0000000000..d32437e126 --- /dev/null +++ b/windows/vcpp2010/readme.txt @@ -0,0 +1,35 @@ +Build Boost +=========== + +Prerequisites: Visual Sutdio C ++ 2010 Express (or higher) + +Download Boost from http://www.boost.org/ + +Unzip boost_1_47_0.zip to C:\boost_1_47_0 + +Open a "Visual Studio Command Prompt (2010)" (not a regular cmd.exe!). + +cd C:\boost_1_47_0 + +bootstrap + +.\b2 runtime-link=static + +Now set a system environment variable: + +BOOSTROOT = C:\boost_1_47_0 + + +Background: + + - http://www.boost.org/doc/libs/1_47_0/more/getting_started/windows.html + - http://www.boost.org/doc/libs/1_47_0/more/getting_started/windows.html#library-naming + - http://stackoverflow.com/questions/2035287/static-runtime-library-linking-for-visual-c-express-2008 + + +Build websocket++ +================= + +Open websocketpp.sln in VS. + +Build Solution (F7). From 50281bcee8aaeb2d080a2de9fdcedbd29d0525eb Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 16:23:21 +0200 Subject: [PATCH 21/40] add VS2010 build/project files --- windows/vcpp2010/examples/chatclient.vcxproj | 108 +++++++++++++++++ .../examples/chatclient.vcxproj.filters | 26 +++++ windows/vcpp2010/examples/chatserver.vcxproj | 108 +++++++++++++++++ .../examples/chatserver.vcxproj.filters | 26 +++++ windows/vcpp2010/examples/echoserver.vcxproj | 109 ++++++++++++++++++ .../examples/echoserver.vcxproj.filters | 26 +++++ windows/vcpp2010/websocketpp.sln | 28 +---- windows/vcpp2010/websocketpp.vcxproj | 105 +++++++++++++++++ windows/vcpp2010/websocketpp.vcxproj.filters | 95 +++++++++++++++ 9 files changed, 609 insertions(+), 22 deletions(-) create mode 100644 windows/vcpp2010/examples/chatclient.vcxproj create mode 100644 windows/vcpp2010/examples/chatclient.vcxproj.filters create mode 100644 windows/vcpp2010/examples/chatserver.vcxproj create mode 100644 windows/vcpp2010/examples/chatserver.vcxproj.filters create mode 100644 windows/vcpp2010/examples/echoserver.vcxproj create mode 100644 windows/vcpp2010/examples/echoserver.vcxproj.filters create mode 100644 windows/vcpp2010/websocketpp.vcxproj create mode 100644 windows/vcpp2010/websocketpp.vcxproj.filters diff --git a/windows/vcpp2010/examples/chatclient.vcxproj b/windows/vcpp2010/examples/chatclient.vcxproj new file mode 100644 index 0000000000..53fd0b9f4d --- /dev/null +++ b/windows/vcpp2010/examples/chatclient.vcxproj @@ -0,0 +1,108 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {116BFEDA-AF8E-4B3F-8508-ACC5EE89F905} + chatclient + Win32Proj + + + + Application + Unicode + true + + + Application + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + + Disabled + $(BOOSTROOT);..;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreaded + + + Level3 + ProgramDatabase + + + ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + true + Console + MachineX86 + + + + + MaxSpeed + true + $(BOOSTROOT);..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + MultiThreaded + false + true + StreamingSIMDExtensions2 + + + Level3 + + + + + ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + false + Console + true + true + MachineX86 + + + + + + + + + + + + {1c0fd04e-5aca-4031-b3d1-320a5360c9d0} + false + + + + + + \ No newline at end of file diff --git a/windows/vcpp2010/examples/chatclient.vcxproj.filters b/windows/vcpp2010/examples/chatclient.vcxproj.filters new file mode 100644 index 0000000000..34e66c830b --- /dev/null +++ b/windows/vcpp2010/examples/chatclient.vcxproj.filters @@ -0,0 +1,26 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/windows/vcpp2010/examples/chatserver.vcxproj b/windows/vcpp2010/examples/chatserver.vcxproj new file mode 100644 index 0000000000..d760b9c662 --- /dev/null +++ b/windows/vcpp2010/examples/chatserver.vcxproj @@ -0,0 +1,108 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {2AFECE48-86DE-47D0-9263-DC0D203AA62D} + chatserver + Win32Proj + + + + Application + Unicode + true + + + Application + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + + Disabled + $(BOOSTROOT);..;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreaded + + + Level3 + ProgramDatabase + + + ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + true + Console + MachineX86 + + + + + MaxSpeed + true + $(BOOSTROOT);..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + MultiThreaded + false + true + StreamingSIMDExtensions2 + + + Level3 + + + + + ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + false + Console + true + true + MachineX86 + + + + + + + + + + + + {1c0fd04e-5aca-4031-b3d1-320a5360c9d0} + false + + + + + + \ No newline at end of file diff --git a/windows/vcpp2010/examples/chatserver.vcxproj.filters b/windows/vcpp2010/examples/chatserver.vcxproj.filters new file mode 100644 index 0000000000..e2ea4bd9dd --- /dev/null +++ b/windows/vcpp2010/examples/chatserver.vcxproj.filters @@ -0,0 +1,26 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/windows/vcpp2010/examples/echoserver.vcxproj b/windows/vcpp2010/examples/echoserver.vcxproj new file mode 100644 index 0000000000..9627b339e7 --- /dev/null +++ b/windows/vcpp2010/examples/echoserver.vcxproj @@ -0,0 +1,109 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B569A272-D7D3-404B-B5FB-9187C0EB9F48} + examples + Win32Proj + + + + Application + Unicode + true + + + Application + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + + Disabled + $(BOOSTROOT);..;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreaded + StreamingSIMDExtensions2 + + + Level3 + ProgramDatabase + + + ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + true + Console + MachineX86 + + + + + MaxSpeed + true + ..\..\..\boost_1_47_0;..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + MultiThreaded + false + true + StreamingSIMDExtensions2 + + + Level3 + + + + + ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + false + Console + true + true + MachineX86 + + + + + + + + + + + + {1c0fd04e-5aca-4031-b3d1-320a5360c9d0} + false + + + + + + \ No newline at end of file diff --git a/windows/vcpp2010/examples/echoserver.vcxproj.filters b/windows/vcpp2010/examples/echoserver.vcxproj.filters new file mode 100644 index 0000000000..a62d813e87 --- /dev/null +++ b/windows/vcpp2010/examples/echoserver.vcxproj.filters @@ -0,0 +1,26 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/windows/vcpp2010/websocketpp.sln b/windows/vcpp2010/websocketpp.sln index 2c657c2785..a2ca1b86e3 100644 --- a/windows/vcpp2010/websocketpp.sln +++ b/windows/vcpp2010/websocketpp.sln @@ -1,24 +1,13 @@  -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "websocketpp", "websocketpp.vcproj", "{1C0FD04E-5ACA-4031-B3D1-320A5360C9D0}" +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "websocketpp", "websocketpp.vcxproj", "{1C0FD04E-5ACA-4031-B3D1-320A5360C9D0}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{14E490FC-930E-40EE-B14A-84E2D98DEC9F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echoserver", "examples\echoserver.vcxproj", "{B569A272-D7D3-404B-B5FB-9187C0EB9F48}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echoserver", "examples\echoserver.vcproj", "{B569A272-D7D3-404B-B5FB-9187C0EB9F48}" - ProjectSection(ProjectDependencies) = postProject - {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} = {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} - EndProjectSection +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chatserver", "examples\chatserver.vcxproj", "{2AFECE48-86DE-47D0-9263-DC0D203AA62D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chatserver", "examples\chatserver.vcproj", "{2AFECE48-86DE-47D0-9263-DC0D203AA62D}" - ProjectSection(ProjectDependencies) = postProject - {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} = {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chatclient", "examples\chatclient.vcproj", "{116BFEDA-AF8E-4B3F-8508-ACC5EE89F905}" - ProjectSection(ProjectDependencies) = postProject - {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} = {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} - EndProjectSection +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chatclient", "examples\chatclient.vcxproj", "{116BFEDA-AF8E-4B3F-8508-ACC5EE89F905}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -46,9 +35,4 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {B569A272-D7D3-404B-B5FB-9187C0EB9F48} = {14E490FC-930E-40EE-B14A-84E2D98DEC9F} - {2AFECE48-86DE-47D0-9263-DC0D203AA62D} = {14E490FC-930E-40EE-B14A-84E2D98DEC9F} - {116BFEDA-AF8E-4B3F-8508-ACC5EE89F905} = {14E490FC-930E-40EE-B14A-84E2D98DEC9F} - EndGlobalSection EndGlobal diff --git a/windows/vcpp2010/websocketpp.vcxproj b/windows/vcpp2010/websocketpp.vcxproj new file mode 100644 index 0000000000..0ba1ca8384 --- /dev/null +++ b/windows/vcpp2010/websocketpp.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} + websocketpp + Win32Proj + + + + StaticLibrary + Unicode + true + + + StaticLibrary + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + + Disabled + $(BOOSTROOT);.;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreaded + StreamingSIMDExtensions2 + + + Level3 + EditAndContinue + + + + + MaxSpeed + true + Speed + $(BOOSTROOT);.;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + MultiThreaded + false + true + StreamingSIMDExtensions2 + + + Level3 + ProgramDatabase + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/vcpp2010/websocketpp.vcxproj.filters b/windows/vcpp2010/websocketpp.vcxproj.filters new file mode 100644 index 0000000000..ace00626f3 --- /dev/null +++ b/windows/vcpp2010/websocketpp.vcxproj.filters @@ -0,0 +1,95 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {79820f1e-61d9-46bc-9eb2-a52c20f62fa4} + + + {b6f4bad9-f45a-495f-93f2-f4b1605b9ac4} + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {d7e1c798-4217-4c1e-a897-90bfc59d83ad} + + + {84b75a09-d8c9-4327-806d-c4f54698e4c5} + + + {c849b58d-df43-4574-ae85-92ac66333899} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\base64 + + + Source Files\sha + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\base64 + + + Header Files\sha + + + Header Files\utf8_validator + + + From 2c47fe17a4eeb2b1a66ca21ed6a2f82641db6b25 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 16:42:14 +0200 Subject: [PATCH 22/40] fix vs2010 build --- windows/vcpp2010/examples/chatclient.vcxproj | 6 +++--- windows/vcpp2010/examples/chatserver.vcxproj | 6 +++--- windows/vcpp2010/examples/echoserver.vcxproj | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/windows/vcpp2010/examples/chatclient.vcxproj b/windows/vcpp2010/examples/chatclient.vcxproj index 53fd0b9f4d..1cdf6b82f2 100644 --- a/windows/vcpp2010/examples/chatclient.vcxproj +++ b/windows/vcpp2010/examples/chatclient.vcxproj @@ -51,14 +51,14 @@ WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) true EnableFastChecks - MultiThreaded + MultiThreadedDebug Level3 ProgramDatabase - ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + $(BOOSTROOT)\stage\lib;%(AdditionalLibraryDirectories) true Console MachineX86 @@ -81,7 +81,7 @@ - ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + $(BOOSTROOT)\stage\lib;%(AdditionalLibraryDirectories) false Console true diff --git a/windows/vcpp2010/examples/chatserver.vcxproj b/windows/vcpp2010/examples/chatserver.vcxproj index d760b9c662..93c19831b8 100644 --- a/windows/vcpp2010/examples/chatserver.vcxproj +++ b/windows/vcpp2010/examples/chatserver.vcxproj @@ -51,14 +51,14 @@ WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) true EnableFastChecks - MultiThreaded + MultiThreadedDebug Level3 ProgramDatabase - ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + $(BOOSTROOT)\stage\lib;%(AdditionalLibraryDirectories) true Console MachineX86 @@ -81,7 +81,7 @@ - ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + $(BOOSTROOT)\stage\lib;%(AdditionalLibraryDirectories) false Console true diff --git a/windows/vcpp2010/examples/echoserver.vcxproj b/windows/vcpp2010/examples/echoserver.vcxproj index 9627b339e7..c632264918 100644 --- a/windows/vcpp2010/examples/echoserver.vcxproj +++ b/windows/vcpp2010/examples/echoserver.vcxproj @@ -51,7 +51,7 @@ WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) true EnableFastChecks - MultiThreaded + MultiThreadedDebug StreamingSIMDExtensions2 @@ -59,7 +59,7 @@ ProgramDatabase - ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + $(BOOSTROOT)\stage\lib;%(AdditionalLibraryDirectories) true Console MachineX86 @@ -82,7 +82,7 @@ - ..\..\..\boost_1_47_0\stage\lib;%(AdditionalLibraryDirectories) + $(BOOSTROOT)\stage\lib;%(AdditionalLibraryDirectories) false Console true From cc088644017b6eb2a46cef21acebc47d62a04e83 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 17:05:37 +0200 Subject: [PATCH 23/40] adjust chat server/client for new connection_handler ifc --- examples/chat_client/chat_client_handler.cpp | 2 +- examples/chat_client/chat_client_handler.hpp | 2 +- examples/chat_server/chat.cpp | 2 +- examples/chat_server/chat.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/chat_client/chat_client_handler.cpp b/examples/chat_client/chat_client_handler.cpp index 38f8ff80ee..e16a5f4717 100644 --- a/examples/chat_client/chat_client_handler.cpp +++ b/examples/chat_client/chat_client_handler.cpp @@ -39,7 +39,7 @@ void chat_client_handler::on_open(session_ptr s) { std::cout << "Successfully connected" << std::endl; } -void chat_client_handler::on_close(session_ptr s,uint16_t status,const std::string &reason) { +void chat_client_handler::on_close(session_ptr s) { // not sure if anything needs to happen here either. m_session = client_session_ptr(); diff --git a/examples/chat_client/chat_client_handler.hpp b/examples/chat_client/chat_client_handler.hpp index fb90a6e7aa..0b1f0e0676 100644 --- a/examples/chat_client/chat_client_handler.hpp +++ b/examples/chat_client/chat_client_handler.hpp @@ -63,7 +63,7 @@ public: void on_open(session_ptr s); // connection to chat room closed - void on_close(session_ptr s,uint16_t status,const std::string &reason); + void on_close(session_ptr s); // got a new message from server void on_message(session_ptr s,const std::string &msg); diff --git a/examples/chat_server/chat.cpp b/examples/chat_server/chat.cpp index d346281f99..9b629b45fb 100644 --- a/examples/chat_server/chat.cpp +++ b/examples/chat_server/chat.cpp @@ -59,7 +59,7 @@ void chat_server_handler::on_open(session_ptr client) { send_to_all(encode_message("server",m_connections[client]+" has joined the chat.")); } -void chat_server_handler::on_close(session_ptr client,uint16_t status,const std::string &reason) { +void chat_server_handler::on_close(session_ptr client) { std::map::iterator it = m_connections.find(client); if (it == m_connections.end()) { diff --git a/examples/chat_server/chat.hpp b/examples/chat_server/chat.hpp index 3eb9b721ce..0ef21c325c 100644 --- a/examples/chat_server/chat.hpp +++ b/examples/chat_server/chat.hpp @@ -59,7 +59,7 @@ public: void on_open(websocketpp::session_ptr client); // someone disconnected from the lobby, remove them - void on_close(websocketpp::session_ptr client,uint16_t status,const std::string &reason); + void on_close(websocketpp::session_ptr client); void on_message(websocketpp::session_ptr client,const std::string &msg); From 4274292858fda708c1a941c89e159b72738ba379 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 17:10:11 +0200 Subject: [PATCH 24/40] temporariy hack to work around access issue --- src/websocket_session.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index b91b9ed38a..1d291f45b3 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -193,7 +193,7 @@ public: virtual void handle_write_handshake(const boost::system::error_code& e) = 0; virtual void handle_read_handshake(const boost::system::error_code& e, std::size_t bytes_transferred) = 0; -protected: +public: //protected: virtual void write_handshake() = 0; virtual void read_handshake() = 0; From d4860de04f872c04dd88c8a7dd6659c2ec941506 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Wed, 26 Oct 2011 17:10:26 +0200 Subject: [PATCH 25/40] fix build config for release --- windows/vcpp2010/examples/echoserver.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/vcpp2010/examples/echoserver.vcxproj b/windows/vcpp2010/examples/echoserver.vcxproj index c632264918..38bf907bca 100644 --- a/windows/vcpp2010/examples/echoserver.vcxproj +++ b/windows/vcpp2010/examples/echoserver.vcxproj @@ -69,7 +69,7 @@ MaxSpeed true - ..\..\..\boost_1_47_0;..;%(AdditionalIncludeDirectories) + $(BOOSTROOT);..;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) MultiThreaded false From 46df7bac658dba6d99066350234ac852072feae3 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Wed, 26 Oct 2011 11:07:02 -0500 Subject: [PATCH 26/40] updates examples for new on_close api --- examples/chat_client/chat_client_handler.cpp | 2 +- examples/chat_client/chat_client_handler.hpp | 2 +- examples/echo_client/echo_client_handler.cpp | 2 +- examples/echo_client/echo_client_handler.hpp | 5 +---- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/chat_client/chat_client_handler.cpp b/examples/chat_client/chat_client_handler.cpp index 38f8ff80ee..e16a5f4717 100644 --- a/examples/chat_client/chat_client_handler.cpp +++ b/examples/chat_client/chat_client_handler.cpp @@ -39,7 +39,7 @@ void chat_client_handler::on_open(session_ptr s) { std::cout << "Successfully connected" << std::endl; } -void chat_client_handler::on_close(session_ptr s,uint16_t status,const std::string &reason) { +void chat_client_handler::on_close(session_ptr s) { // not sure if anything needs to happen here either. m_session = client_session_ptr(); diff --git a/examples/chat_client/chat_client_handler.hpp b/examples/chat_client/chat_client_handler.hpp index fb90a6e7aa..0b1f0e0676 100644 --- a/examples/chat_client/chat_client_handler.hpp +++ b/examples/chat_client/chat_client_handler.hpp @@ -63,7 +63,7 @@ public: void on_open(session_ptr s); // connection to chat room closed - void on_close(session_ptr s,uint16_t status,const std::string &reason); + void on_close(session_ptr s); // got a new message from server void on_message(session_ptr s,const std::string &msg); diff --git a/examples/echo_client/echo_client_handler.cpp b/examples/echo_client/echo_client_handler.cpp index ce8dc6408c..88d08513b6 100644 --- a/examples/echo_client/echo_client_handler.cpp +++ b/examples/echo_client/echo_client_handler.cpp @@ -36,7 +36,7 @@ void echo_client_handler::on_open(session_ptr s) { std::cout << "Successfully connected: " << s->get_resource() << std::endl; } -void echo_client_handler::on_close(session_ptr s,uint16_t status,const std::string &reason) { +void echo_client_handler::on_close(session_ptr s) { std::cout << "client was disconnected" << std::endl; } diff --git a/examples/echo_client/echo_client_handler.hpp b/examples/echo_client/echo_client_handler.hpp index 795939d183..f011c56265 100644 --- a/examples/echo_client/echo_client_handler.hpp +++ b/examples/echo_client/echo_client_handler.hpp @@ -56,14 +56,11 @@ public: echo_client_handler() : m_case_count(0) {} virtual ~echo_client_handler() {} - // ignored for clients? - void validate(session_ptr s) {} - // connection to chat room complete void on_open(session_ptr s); // connection to chat room closed - void on_close(session_ptr s,uint16_t status,const std::string &reason); + void on_close(session_ptr sn); // got a new message from server void on_message(session_ptr s,const std::string &msg); From 48cd643ee8c2f725cd9f22e0f3224458bffd7993 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Wed, 26 Oct 2011 14:41:16 -0500 Subject: [PATCH 27/40] close handling timers --- src/websocket_session.cpp | 40 +++++++++++++++++++++++++++++++++++++++ src/websocket_session.hpp | 1 + 2 files changed, 41 insertions(+) diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index a2fb733f79..52ac50bb71 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -167,6 +167,16 @@ void session::send_close(uint16_t status,const std::string &message) { m_state = STATE_CLOSING; + m_timer.expires_from_now(boost::posix_time::milliseconds(1000)); + + m_timer.async_wait( + boost::bind( + &session::handle_close_expired, + shared_from_this(), + boost::asio::placeholders::error + ) + ); + m_local_close_code = status; m_local_close_msg = message; @@ -280,6 +290,13 @@ void session::handle_read_frame(const boost::system::error_code& error) { access_log(e.what(),ALOG_FRAME); log(err.str(),LOG_ERROR); + // 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.get_state() == frame::STATE_READY) { + m_read_frame.reset(); + } + // process different types of frame errors // if (e.code() == frame::FERR_PROTOCOL_VIOLATION) { @@ -293,6 +310,7 @@ void session::handle_read_frame(const boost::system::error_code& error) { continue; } else { // Fatal error, forcibly end connection immediately. + log("Dropping TCP due to unrecoverable exception",LOG_DEBUG); drop_tcp(true); } @@ -332,6 +350,8 @@ void session::handle_read_frame(const boost::system::error_code& error) { // TODO: make sure close code/msg are properly set. m_local_interface->on_close(shared_from_this()); } + + m_timer.cancel(); } else { log("handle_read_frame called in invalid state",LOG_ERROR); } @@ -424,6 +444,24 @@ void session::handle_handshake_expired (const boost::system::error_code& error) drop_tcp(false); } +void session::handle_close_expired (const boost::system::error_code& error) { + if (error) { + if (error == boost::asio::error::operation_aborted) { + log("timer was aborted",LOG_DEBUG); + //drop_tcp(false); + } else { + log("Unexpected close timer error.",LOG_DEBUG); + drop_tcp(false); + } + return; + } + + if (m_state != STATE_CLOSED) { + log("close timed out",LOG_DEBUG); + drop_tcp(false); + } +} + void session::process_ping() { access_log("Ping",ALOG_MISC_CONTROL); // TODO: on_ping @@ -651,10 +689,12 @@ bool session::validate_app_close_status(uint16_t status) { } void session::drop_tcp(bool dropped_by_me) { + m_timer.cancel(); if (m_socket.is_open()) { m_socket.shutdown(tcp::socket::shutdown_both); m_socket.close(); } m_dropped_by_me = dropped_by_me; m_state = STATE_CLOSED; + } diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index b91b9ed38a..180ef38d98 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -206,6 +206,7 @@ protected: void handle_timer_expired(const boost::system::error_code& error); void handle_handshake_expired(const boost::system::error_code& error); + void handle_close_expired(const boost::system::error_code& error); // helper functions for processing each opcode void process_frame(); From f3769e041a9cc97c67c1cd1129b16a51e21ef3bd Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Wed, 26 Oct 2011 14:43:12 -0500 Subject: [PATCH 28/40] error timers --- src/websocket_session.cpp | 33 +++++++++++++++++++++++++++------ src/websocket_session.hpp | 1 + 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index 52ac50bb71..aa09d23aed 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -281,6 +281,7 @@ void session::handle_read_frame(const boost::system::error_code& error) { err.str(""); err << "processing frame " << m_buf.size(); log(err.str(),LOG_DEBUG); + m_timer.cancel(); process_frame(); } } catch (const frame_error& e) { @@ -430,18 +431,36 @@ void session::handle_timer_expired (const boost::system::error_code& error) { void session::handle_handshake_expired (const boost::system::error_code& error) { if (error) { - if (error == boost::asio::error::operation_aborted) { - log("timer was aborted",LOG_DEBUG); - //drop_tcp(false); - } else { + if (error != boost::asio::error::operation_aborted) { log("Unexpected handshake timer error.",LOG_DEBUG); - drop_tcp(false); + drop_tcp(true); } return; } log("Handshake timed out",LOG_DEBUG); - drop_tcp(false); + drop_tcp(true); +} + +// 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 session::handle_error_timer_expired (const boost::system::error_code& error) { + if (error) { + if (error == boost::asio::error::operation_aborted) { + log("error timer was aborted",LOG_DEBUG); + //drop_tcp(false); + } else { + log("error timer ended with error",LOG_DEBUG); + drop_tcp(true); + } + return; + } + + log("error timer ended without error",LOG_DEBUG); + drop_tcp(true); } void session::handle_close_expired (const boost::system::error_code& error) { @@ -555,6 +574,7 @@ void session::deliver_message() { } if (m_current_opcode == frame::BINARY_FRAME) { + //log("Dispatching Binary Message",LOG_DEBUG); if (m_fragmented) { m_local_interface->on_message(shared_from_this(),m_current_message); } else { @@ -582,6 +602,7 @@ void session::deliver_message() { ); } + //log("Dispatching Text Message",LOG_DEBUG); m_local_interface->on_message(shared_from_this(),msg); } else { // Not sure if this should be a fatal error or not diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index 180ef38d98..0369ac717b 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -207,6 +207,7 @@ protected: void handle_timer_expired(const boost::system::error_code& error); void handle_handshake_expired(const boost::system::error_code& error); void handle_close_expired(const boost::system::error_code& error); + void handle_error_timer_expired (const boost::system::error_code& error); // helper functions for processing each opcode void process_frame(); From af2fa698220f5a88372752f6dd76c5691dd2236e Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Wed, 26 Oct 2011 16:47:20 -0500 Subject: [PATCH 29/40] catches socket exception when other end closes the connection right as we are about to --- src/websocket_session.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index aa09d23aed..b7cc8fd836 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -711,9 +711,18 @@ bool session::validate_app_close_status(uint16_t status) { void session::drop_tcp(bool dropped_by_me) { m_timer.cancel(); - if (m_socket.is_open()) { - m_socket.shutdown(tcp::socket::shutdown_both); - m_socket.close(); + 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; From ccb34c98b439e523e669d7532f4211425f308648 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 27 Oct 2011 09:42:11 -0500 Subject: [PATCH 30/40] fixes close status handling --- examples/echo_client/echo_client.cpp | 8 +++--- src/websocket_frame.cpp | 35 +++++++++++++++++++------- src/websocket_frame.hpp | 4 +-- src/websocket_session.cpp | 36 ++++++++++++++++----------- src/websocket_session.hpp | 1 + src/websocketpp.hpp | 37 +++++++++++++++++++++++++++- 6 files changed, 91 insertions(+), 30 deletions(-) diff --git a/examples/echo_client/echo_client.cpp b/examples/echo_client/echo_client.cpp index 713201804c..fc5f96057c 100644 --- a/examples/echo_client/echo_client.cpp +++ b/examples/echo_client/echo_client.cpp @@ -53,12 +53,12 @@ int main(int argc, char* argv[]) { websocketpp::client_ptr client(new websocketpp::client(io_service,c)); client->init(); - client->set_header("User Agent","WebSocket++/2011-09-25"); + client->set_header("User Agent","WebSocket++/2011-10-27"); client->connect("ws://localhost:9001/getCaseCount"); io_service.run(); - std::cout << "case count: " << c->m_case_count; + std::cout << "case count: " << c->m_case_count << std::endl; for (int i = 1; i <= c->m_case_count; i++) { io_service.reset(); @@ -74,12 +74,12 @@ int main(int argc, char* argv[]) { client->set_elog_level(websocketpp::LOG_OFF); client->init(); - client->set_header("User Agent","WebSocket++/2011-09-25"); + client->set_header("User Agent","WebSocket++/2011-10-27"); std::stringstream foo; - foo << "ws://localhost:9001/runCase?case=" << i << "&agent=\"WebSocket++Snapshot/2011-10-08\""; + foo << "ws://localhost:9001/runCase?case=" << i << "&agent=\"WebSocket++Snapshot/2011-10-27\""; client->connect(foo.str()); io_service.run(); diff --git a/src/websocket_frame.cpp b/src/websocket_frame.cpp index 63fcaf45b5..114cdbd012 100644 --- a/src/websocket_frame.cpp +++ b/src/websocket_frame.cpp @@ -285,7 +285,9 @@ size_t frame::get_payload_size() const { } uint16_t frame::get_close_status() const { - if (get_payload_size() >= 2) { + if (get_payload_size() == 0) { + return close::status::NO_STATUS; + } else if (get_payload_size() >= 2) { char val[2]; val[0] = m_payload[0]; @@ -295,14 +297,25 @@ uint16_t frame::get_close_status() const { reinterpret_cast(&val[0]) )); - return code; + if (close::status::invalid(code)) { + return close::status::PROTOCOL_ERROR; + } else { + return code; + } } else { - return 1005; // defined in spec as "no status recieved" + return close::status::PROTOCOL_ERROR; } } std::string frame::get_close_msg() const { if (get_payload_size() > 2) { + uint32_t state = utf8_validator::UTF8_ACCEPT; + uint32_t codep = 0; + validate_utf8(&state,&codep,2); + if (state != utf8_validator::UTF8_ACCEPT) { + throw frame_error("Invalid UTF-8 Data", + frame::FERR_PAYLOAD_VIOLATION); + } return std::string(m_payload.begin()+2,m_payload.end()); } else { return std::string(); @@ -360,12 +373,16 @@ void frame::set_payload_helper(size_t s) { void frame::set_status(uint16_t status,const std::string message) { // check for valid statuses - if (status < 1000 || status > 4999) { - throw frame_error("Status codes must be in the range 1000-4999"); + if (close::status::invalid(status)) { + std::stringstream err; + err << "Status code " << status << " is invalid"; + throw frame_error(err.str()); } - if (status == 1005 || status == 1006) { - throw frame_error("Status codes 1005 and 1006 are reserved for internal use and cannot be written to a frame."); + if (close::status::reserved(status)) { + std::stringstream err; + err << "Status code " << status << " is reserved"; + throw frame_error(err.str()); } m_payload.resize(2+message.size()); @@ -506,8 +523,8 @@ void frame::process_payload2() { } } -void frame::validate_utf8(uint32_t* state,uint32_t* codep) const { - for (size_t i = 0; i < m_payload.size(); i++) { +void frame::validate_utf8(uint32_t* state,uint32_t* codep, size_t offset) 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) { diff --git a/src/websocket_frame.hpp b/src/websocket_frame.hpp index 9e4a7014df..ab74ef76b6 100644 --- a/src/websocket_frame.hpp +++ b/src/websocket_frame.hpp @@ -130,7 +130,7 @@ public: uint16_t get_close_status() const; std::string get_close_msg() const; - + std::vector &get_payload(); void set_payload(const std::vector source); @@ -149,7 +149,7 @@ public: void process_payload(); void process_payload2(); // experiment with more efficient masking code. - void validate_utf8(uint32_t* state,uint32_t* codep) const; + void validate_utf8(uint32_t* state,uint32_t* codep,size_t offset = 0) const; void validate_basic_header() const; void generate_masking_key(); diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index b7cc8fd836..f43f8abdaa 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -183,15 +183,20 @@ void session::send_close(uint16_t status,const std::string &message) { m_write_frame.set_fin(true); m_write_frame.set_opcode(frame::CONNECTION_CLOSE); - 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,message); - } else { - m_write_frame.set_status(status,message); + // echo close value unless there is a good reason not to. + try { + 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,message); + } else { + m_write_frame.set_status(status,message); + } + } catch (const frame_error& e) { + m_write_frame.set_status(close::status::PROTOCOL_ERROR,e.what()); } - + write_frame(); } @@ -544,18 +549,21 @@ void session::process_continuation() { } void session::process_close() { - uint16_t status = m_read_frame.get_close_status(); - std::string message = m_read_frame.get_close_msg(); - - m_remote_close_code = status; - m_remote_close_msg = message; + 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) { log("process_close sending ack",LOG_DEBUG); // This is the case where the remote initiated the close. m_closed_by_me = false; // send acknowledgement - send_close(status,message); + + // 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) { log("process_close got ack",LOG_DEBUG); // this is an ack of our close message diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index 0369ac717b..40bc7bc269 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -135,6 +135,7 @@ public: static const uint16_t CLOSE_STATUS_POLICY_VIOLATION = 1008; static const uint16_t CLOSE_STATUS_MESSAGE_TOO_BIG = 1009; static const uint16_t CLOSE_STATUS_EXTENSION_REQUIRE = 1010; + static const uint16_t CLOSE_STATUS_MAXIMUM = 1011; session (boost::asio::io_service& io_service, connection_handler_ptr defc, diff --git a/src/websocketpp.hpp b/src/websocketpp.hpp index 0b8322ec7b..a40855df35 100644 --- a/src/websocketpp.hpp +++ b/src/websocketpp.hpp @@ -59,7 +59,42 @@ namespace websocketpp { static const uint16_t ALOG_CONTROL = ALOG_CONNECT & ALOG_DISCONNECT & ALOG_MISC_CONTROL; - static const uint16_t ALOG_ALL = 0xFFFF; + static const uint16_t ALOG_ALL = 0xFFFF; + + + 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(uint16_t s) { + return ((s >= RSV_START && s <= RSV_END) || + s == RSV_ADHOC_1); + } + + inline bool invalid(uint16_t s) { + return ((s <= INVALID_END || s >= INVALID_START) || + s == NO_STATUS || + s == ABNORMAL_CLOSE); + } + } + } + } #include "websocket_session.hpp" From 5f35df4e6e0943e5f22baa5106dcddbf4585f47c Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 27 Oct 2011 10:06:21 -0500 Subject: [PATCH 31/40] streamlined bad close code behavior --- src/websocket_frame.cpp | 6 +----- src/websocket_session.cpp | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/websocket_frame.cpp b/src/websocket_frame.cpp index 114cdbd012..c630f05007 100644 --- a/src/websocket_frame.cpp +++ b/src/websocket_frame.cpp @@ -297,11 +297,7 @@ uint16_t frame::get_close_status() const { reinterpret_cast(&val[0]) )); - if (close::status::invalid(code)) { - return close::status::PROTOCOL_ERROR; - } else { - return code; - } + return code; } else { return close::status::PROTOCOL_ERROR; } diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index f43f8abdaa..a8119b4728 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -184,17 +184,17 @@ void session::send_close(uint16_t status,const std::string &message) { m_write_frame.set_opcode(frame::CONNECTION_CLOSE); // echo close value unless there is a good reason not to. - try { - 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,message); - } else { - m_write_frame.set_status(status,message); - } - } catch (const frame_error& e) { - m_write_frame.set_status(close::status::PROTOCOL_ERROR,e.what()); + 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,message); + } 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,message); } write_frame(); From ad8726b06dd67817c644a897d9623cae966760d3 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Thu, 27 Oct 2011 17:58:50 +0200 Subject: [PATCH 32/40] fix for relative include paths --- examples/echo_client/echo_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/echo_client/echo_client.cpp b/examples/echo_client/echo_client.cpp index fc5f96057c..0d649e9121 100644 --- a/examples/echo_client/echo_client.cpp +++ b/examples/echo_client/echo_client.cpp @@ -27,7 +27,7 @@ #include "echo_client_handler.hpp" -#include +#include "../../src/websocketpp.hpp" #include #include From 445d65ffac4ba9b90ad906a27cf292b2e6e97ab1 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Thu, 27 Oct 2011 17:59:09 +0200 Subject: [PATCH 33/40] add echo client project --- windows/vcpp2010/examples/echoclient.vcxproj | 99 +++++++++++++++++++ .../examples/echoclient.vcxproj.filters | 30 ++++++ windows/vcpp2010/websocketpp.sln | 9 ++ 3 files changed, 138 insertions(+) create mode 100644 windows/vcpp2010/examples/echoclient.vcxproj create mode 100644 windows/vcpp2010/examples/echoclient.vcxproj.filters diff --git a/windows/vcpp2010/examples/echoclient.vcxproj b/windows/vcpp2010/examples/echoclient.vcxproj new file mode 100644 index 0000000000..cff2202597 --- /dev/null +++ b/windows/vcpp2010/examples/echoclient.vcxproj @@ -0,0 +1,99 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {D7C36001-002F-4B8E-B4C0-A04E2F9522D0} + Win32Proj + echoclient + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + $(BOOSTROOT);..;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + true + $(BOOSTROOT)\stage\lib;%(AdditionalLibraryDirectories) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;NOCOMM;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + $(BOOSTROOT);..;%(AdditionalIncludeDirectories) + MultiThreaded + false + StreamingSIMDExtensions2 + + + Console + true + true + true + $(BOOSTROOT)\stage\lib;%(AdditionalLibraryDirectories) + + + + + + + + + + + + {1c0fd04e-5aca-4031-b3d1-320a5360c9d0} + + + + + + \ No newline at end of file diff --git a/windows/vcpp2010/examples/echoclient.vcxproj.filters b/windows/vcpp2010/examples/echoclient.vcxproj.filters new file mode 100644 index 0000000000..765ace9c46 --- /dev/null +++ b/windows/vcpp2010/examples/echoclient.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/windows/vcpp2010/websocketpp.sln b/windows/vcpp2010/websocketpp.sln index a2ca1b86e3..4139952bfa 100644 --- a/windows/vcpp2010/websocketpp.sln +++ b/windows/vcpp2010/websocketpp.sln @@ -9,6 +9,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chatserver", "examples\chat EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chatclient", "examples\chatclient.vcxproj", "{116BFEDA-AF8E-4B3F-8508-ACC5EE89F905}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echoclient", "examples\echoclient.vcxproj", "{D7C36001-002F-4B8E-B4C0-A04E2F9522D0}" + ProjectSection(ProjectDependencies) = postProject + {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} = {1C0FD04E-5ACA-4031-B3D1-320A5360C9D0} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -31,6 +36,10 @@ Global {116BFEDA-AF8E-4B3F-8508-ACC5EE89F905}.Debug|Win32.Build.0 = Debug|Win32 {116BFEDA-AF8E-4B3F-8508-ACC5EE89F905}.Release|Win32.ActiveCfg = Release|Win32 {116BFEDA-AF8E-4B3F-8508-ACC5EE89F905}.Release|Win32.Build.0 = Release|Win32 + {D7C36001-002F-4B8E-B4C0-A04E2F9522D0}.Debug|Win32.ActiveCfg = Debug|Win32 + {D7C36001-002F-4B8E-B4C0-A04E2F9522D0}.Debug|Win32.Build.0 = Debug|Win32 + {D7C36001-002F-4B8E-B4C0-A04E2F9522D0}.Release|Win32.ActiveCfg = Release|Win32 + {D7C36001-002F-4B8E-B4C0-A04E2F9522D0}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From dbcfa44d78257b6d6dc2f11d327d93109badadf9 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 27 Oct 2011 11:44:26 -0500 Subject: [PATCH 34/40] fixes echo_client, absolute vs relative include, adds some debug info --- examples/echo_client/echo_client.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/echo_client/echo_client.cpp b/examples/echo_client/echo_client.cpp index fc5f96057c..c5f9cad852 100644 --- a/examples/echo_client/echo_client.cpp +++ b/examples/echo_client/echo_client.cpp @@ -27,7 +27,7 @@ #include "echo_client_handler.hpp" -#include +#include "../../src/websocketpp.hpp" #include #include @@ -61,28 +61,28 @@ int main(int argc, char* argv[]) { std::cout << "case count: " << c->m_case_count << std::endl; for (int i = 1; i <= c->m_case_count; i++) { + std::cout << "Resetting io_service" << std::endl; io_service.reset(); - //boost::asio::io_service ios; - - //client.reset(); - //client = websocketpp::client_ptr(new websocketpp::client(io_service,c)); - - std::cout << "foo: " << i << std::endl; - //websocketpp::client_ptr client2(new websocketpp::client(io_service,c)); client->set_alog_level(websocketpp::ALOG_OFF); client->set_elog_level(websocketpp::LOG_OFF); + std::cout << "Initializing client" << std::endl; client->init(); client->set_header("User Agent","WebSocket++/2011-10-27"); - std::stringstream foo; + std::stringstream url; - foo << "ws://localhost:9001/runCase?case=" << i << "&agent=\"WebSocket++Snapshot/2011-10-27\""; + url << "ws://localhost:9001/runCase?case=" << i << "&agent=\"WebSocket++Snapshot/2011-10-27\""; - client->connect(foo.str()); + std::cout << "Parsing URL and resolving DNS" << std::endl; + client->connect(url.str()); + + std::cout << "Starting io_service for test " << i << std::endl; io_service.run(); + + std::cout << "test " << i << " complete" << std::endl; } std::cout << "done" << std::endl; From 3b57b8697396b8124a04116eb24640a3683fdb26 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 27 Oct 2011 11:55:49 -0500 Subject: [PATCH 35/40] adds times to echo client debug --- examples/echo_client/echo_client.cpp | 11 ++++++----- examples/echo_client/echo_client_handler.cpp | 5 +++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/echo_client/echo_client.cpp b/examples/echo_client/echo_client.cpp index c5f9cad852..5d9fcff200 100644 --- a/examples/echo_client/echo_client.cpp +++ b/examples/echo_client/echo_client.cpp @@ -30,6 +30,7 @@ #include "../../src/websocketpp.hpp" #include #include +#include #include @@ -61,13 +62,13 @@ int main(int argc, char* argv[]) { std::cout << "case count: " << c->m_case_count << std::endl; for (int i = 1; i <= c->m_case_count; i++) { - std::cout << "Resetting io_service" << std::endl; + std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " Resetting io_service" << std::endl; io_service.reset(); client->set_alog_level(websocketpp::ALOG_OFF); client->set_elog_level(websocketpp::LOG_OFF); - std::cout << "Initializing client" << std::endl; + std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " Initializing client (constructing socket)" << std::endl; client->init(); client->set_header("User Agent","WebSocket++/2011-10-27"); @@ -76,13 +77,13 @@ int main(int argc, char* argv[]) { url << "ws://localhost:9001/runCase?case=" << i << "&agent=\"WebSocket++Snapshot/2011-10-27\""; - std::cout << "Parsing URL and resolving DNS" << std::endl; + std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " Parsing URL and resolving DNS" << std::endl; client->connect(url.str()); - std::cout << "Starting io_service for test " << i << std::endl; + std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " Starting io_service for test " << i << std::endl; io_service.run(); - std::cout << "test " << i << " complete" << std::endl; + std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " test " << i << " complete" << std::endl; } std::cout << "done" << std::endl; diff --git a/examples/echo_client/echo_client_handler.cpp b/examples/echo_client/echo_client_handler.cpp index 88d08513b6..2fd72272db 100644 --- a/examples/echo_client/echo_client_handler.cpp +++ b/examples/echo_client/echo_client_handler.cpp @@ -28,16 +28,17 @@ #include "echo_client_handler.hpp" #include +#include using websocketecho::echo_client_handler; using websocketpp::client_session_ptr; void echo_client_handler::on_open(session_ptr s) { - std::cout << "Successfully connected: " << s->get_resource() << std::endl; + std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " Successfully connected (handshake complete): " << s->get_resource() << std::endl; } void echo_client_handler::on_close(session_ptr s) { - std::cout << "client was disconnected" << std::endl; + std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " client was disconnected (WS state is now CLOSED)" << std::endl; } void echo_client_handler::on_message(session_ptr s,const std::string &msg) { From 80037e4d9fa96815258939625c6f178ced7d5f4a Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 27 Oct 2011 12:05:50 -0500 Subject: [PATCH 36/40] debug statement --- src/websocket_client.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/websocket_client.cpp b/src/websocket_client.cpp index c23637a750..1e08d9d6bd 100644 --- a/src/websocket_client.cpp +++ b/src/websocket_client.cpp @@ -186,6 +186,8 @@ void client::handle_connect(const boost::system::error_code& error) { 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 { From 62a97aad6ba26f4fb084b2f3fefa2083db785034 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 27 Oct 2011 17:26:37 -0500 Subject: [PATCH 37/40] starts work on program options --- src/websocket_frame.hpp | 7 ++ src/websocket_server.cpp | 16 +++- src/websocket_server.hpp | 111 ++++++++++++++------------ websocketpp.xcodeproj/project.pbxproj | 4 + 4 files changed, 85 insertions(+), 53 deletions(-) diff --git a/src/websocket_frame.hpp b/src/websocket_frame.hpp index ab74ef76b6..9c719f38b2 100644 --- a/src/websocket_frame.hpp +++ b/src/websocket_frame.hpp @@ -39,6 +39,13 @@ namespace websocketpp { +/* policies to abstract out + + - random number generation + - utf8 validation + + */ + class frame { public: enum opcode_s { diff --git a/src/websocket_server.cpp b/src/websocket_server.cpp index a3fbdc56c6..c0040e9031 100644 --- a/src/websocket_server.cpp +++ b/src/websocket_server.cpp @@ -30,6 +30,8 @@ #include #include +#include + #include using websocketpp::server; @@ -42,7 +44,19 @@ server::server(boost::asio::io_service& io_service, m_max_message_size(DEFAULT_MAX_MESSAGE_SIZE), m_io_service(io_service), m_acceptor(io_service, endpoint), - m_def_con_handler(defc) {} + m_def_con_handler(defc), + m_desc("test") { + m_desc.add_options() + ("help", "produce help message") + ("intval",po::value(), "set compression level") + ; + +} + +void server::parse_command_line(int ac, char* av[]) { + po::store(po::parse_command_line(ac,av, m_desc),m_vm); + po::notify(m_vm); +} void server::add_host(std::string host) { m_hosts.insert(host); diff --git a/src/websocket_server.hpp b/src/websocket_server.hpp index d294c13505..dc9b4f03cb 100644 --- a/src/websocket_server.hpp +++ b/src/websocket_server.hpp @@ -30,6 +30,8 @@ #include #include +#include +namespace po = boost::program_options; #include @@ -60,61 +62,66 @@ private: }; class server : public boost::enable_shared_from_this { - public: - server(boost::asio::io_service& io_service, - const tcp::endpoint& endpoint, - connection_handler_ptr defc); - - // creates a new session object and connects the next websocket - // connection to it. - void start_accept(); - - // INTERFACE FOR LOCAL APPLICATIONS +public: + server(boost::asio::io_service& io_service, + const tcp::endpoint& endpoint, + connection_handler_ptr defc); + + // creates a new session object and connects the next websocket + // connection to it. + void start_accept(); + + // INTERFACE FOR LOCAL APPLICATIONS - // Add or remove a host string (host:port) to the list of acceptable - // hosts to accept websocket connections from. Additions/deletions here - // only affect new connections. - void add_host(std::string host); - void remove_host(std::string host); - - void set_max_message_size(uint64_t 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); - void set_elog_level(uint16_t level); - - bool test_alog_level(uint16_t level); - void set_alog_level(uint16_t level); - void unset_alog_level(uint16_t level); + // Add or remove a host string (host:port) to the list of acceptable + // hosts to accept websocket connections from. Additions/deletions here + // only affect new connections. + void add_host(std::string host); + void remove_host(std::string host); + + void set_max_message_size(uint64_t 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); + void set_elog_level(uint16_t level); + + bool test_alog_level(uint16_t level); + void set_alog_level(uint16_t level); + void unset_alog_level(uint16_t level); + + void parse_command_line(int ac, char* av[]); + + // INTERFACE FOR SESSIONS - // INTERFACE FOR SESSIONS + // Check if this server will respond to this host. + bool validate_host(std::string host); + + // Check if message size is within server's acceptable parameters + bool validate_message_size(uint64_t val); + + // write to the server's logs + void log(std::string msg,uint16_t level = LOG_ERROR); + void access_log(std::string msg,uint16_t level); +private: + // if no errors starts the session's read loop and returns to the + // start_accept phase. + void handle_accept(server_session_ptr session, + const boost::system::error_code& error); + +private: + uint16_t m_elog_level; + uint16_t m_alog_level; - // Check if this server will respond to this host. - bool validate_host(std::string host); - - // Check if message size is within server's acceptable parameters - bool validate_message_size(uint64_t val); - - // write to the server's logs - void log(std::string msg,uint16_t level = LOG_ERROR); - void access_log(std::string msg,uint16_t level); - private: - // if no errors starts the session's read loop and returns to the - // start_accept phase. - void handle_accept(server_session_ptr session, - const boost::system::error_code& error); - - private: - uint16_t m_elog_level; - uint16_t m_alog_level; - - std::set m_hosts; - uint64_t m_max_message_size; - boost::asio::io_service& m_io_service; - tcp::acceptor m_acceptor; - connection_handler_ptr m_def_con_handler; + std::set m_hosts; + uint64_t m_max_message_size; + boost::asio::io_service& m_io_service; + tcp::acceptor m_acceptor; + connection_handler_ptr m_def_con_handler; + + po::options_description m_desc; + po::variables_map m_vm; }; } diff --git a/websocketpp.xcodeproj/project.pbxproj b/websocketpp.xcodeproj/project.pbxproj index ac6f6b3ddc..ae545d4e8b 100644 --- a/websocketpp.xcodeproj/project.pbxproj +++ b/websocketpp.xcodeproj/project.pbxproj @@ -69,6 +69,7 @@ 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 */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -167,6 +168,7 @@ B6DF1CE11435F1860029A1B1 /* libboost_system.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_system.dylib; path = usr/local/lib/libboost_system.dylib; sourceTree = SDKROOT; }; B6DF1CE31435F8250029A1B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; B6FE8CE2144DE17F00B32547 /* readme.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = readme.txt; sourceTree = ""; }; + B6FE8CEB145A0F1900B32547 /* libboost_program_options.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_program_options.dylib; path = usr/local/lib/libboost_program_options.dylib; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -200,6 +202,7 @@ 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 */, @@ -235,6 +238,7 @@ B6DF1C451434A5940029A1B1 = { isa = PBXGroup; children = ( + B6FE8CEB145A0F1900B32547 /* libboost_program_options.dylib */, B6CF182B1437C3CA009295BE /* libboost_system.dylib */, B682888E14374689002BA48B /* libboost_thread.dylib */, B682888C1437464A002BA48B /* libboost_random.dylib */, From dd5e620bd2aa2adebb968701fe342483d6409122 Mon Sep 17 00:00:00 2001 From: Tobias Oberstein Date: Fri, 28 Oct 2011 12:56:34 +0200 Subject: [PATCH 38/40] disable incremental builds for all projects and configurations --- windows/vcpp2010/examples/chatclient.vcxproj | 2 +- windows/vcpp2010/examples/chatserver.vcxproj | 2 +- windows/vcpp2010/examples/echoclient.vcxproj | 2 +- windows/vcpp2010/examples/echoserver.vcxproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/windows/vcpp2010/examples/chatclient.vcxproj b/windows/vcpp2010/examples/chatclient.vcxproj index 1cdf6b82f2..5a07f036a5 100644 --- a/windows/vcpp2010/examples/chatclient.vcxproj +++ b/windows/vcpp2010/examples/chatclient.vcxproj @@ -39,7 +39,7 @@ <_ProjectFileVersion>10.0.30319.1 $(SolutionDir)$(Configuration)\ $(Configuration)\ - true + false $(SolutionDir)$(Configuration)\ $(Configuration)\ false diff --git a/windows/vcpp2010/examples/chatserver.vcxproj b/windows/vcpp2010/examples/chatserver.vcxproj index 93c19831b8..bfdec866f8 100644 --- a/windows/vcpp2010/examples/chatserver.vcxproj +++ b/windows/vcpp2010/examples/chatserver.vcxproj @@ -39,7 +39,7 @@ <_ProjectFileVersion>10.0.30319.1 $(SolutionDir)$(Configuration)\ $(Configuration)\ - true + false $(SolutionDir)$(Configuration)\ $(Configuration)\ false diff --git a/windows/vcpp2010/examples/echoclient.vcxproj b/windows/vcpp2010/examples/echoclient.vcxproj index cff2202597..281a304a64 100644 --- a/windows/vcpp2010/examples/echoclient.vcxproj +++ b/windows/vcpp2010/examples/echoclient.vcxproj @@ -38,7 +38,7 @@ - true + false false diff --git a/windows/vcpp2010/examples/echoserver.vcxproj b/windows/vcpp2010/examples/echoserver.vcxproj index 38bf907bca..eeace56e03 100644 --- a/windows/vcpp2010/examples/echoserver.vcxproj +++ b/windows/vcpp2010/examples/echoserver.vcxproj @@ -39,9 +39,9 @@ <_ProjectFileVersion>10.0.30319.1 $(SolutionDir)$(Configuration)\ $(Configuration)\ - true $(SolutionDir)$(Configuration)\ $(Configuration)\ + false false From 536ded2512bf06a79e4de7514721c4e66dc90390 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Fri, 28 Oct 2011 09:26:54 -0500 Subject: [PATCH 39/40] continues work on program options, updates makefiles for examples --- examples/chat_server/Makefile | 2 +- examples/echo_client/Makefile | 2 +- examples/echo_client/echo_client.cpp | 6 ----- examples/echo_client/echo_client_handler.cpp | 4 ++-- examples/echo_server/Makefile | 2 +- examples/echo_server/echo_server.cpp | 4 ++++ src/websocket_server.cpp | 23 ++++++++++++++++---- websocketpp.xcodeproj/project.pbxproj | 14 ++++++++++++ 8 files changed, 42 insertions(+), 15 deletions(-) diff --git a/examples/chat_server/Makefile b/examples/chat_server/Makefile index 882386ceee..9906162064 100644 --- a/examples/chat_server/Makefile +++ b/examples/chat_server/Makefile @@ -7,7 +7,7 @@ SHARED ?= "1" ifeq ($(SHARED), 1) LDFLAGS := $(LDFLAGS) -lboost_system -lboost_date_time -lwebsocketpp else - LDFLAGS := $(LDFLAGS) -lboost_system -lboost_date_time -lboost_regex -lboost_random ../../libwebsocketpp.a + LDFLAGS := $(LDFLAGS) -lboost_system -lboost_date_time -lboost_regex -lboost_random -lboost_program_options ../../libwebsocketpp.a endif chat_server: chat_server.o chat.o diff --git a/examples/echo_client/Makefile b/examples/echo_client/Makefile index 2be6a3b967..901343976e 100644 --- a/examples/echo_client/Makefile +++ b/examples/echo_client/Makefile @@ -7,7 +7,7 @@ SHARED ?= "1" ifeq ($(SHARED), 1) LDFLAGS := $(LDFLAGS) -lboost_system -lboost_thread -lwebsocketpp else - LDFLAGS := $(LDFLAGS) -lboost_system -lboost_thread -lboost_date_time -lboost_regex -lboost_random ../../libwebsocketpp.a + LDFLAGS := $(LDFLAGS) -lboost_system -lboost_thread -lboost_date_time -lboost_regex -lboost_random -lboost_program_options ../../libwebsocketpp.a endif echo_client: echo_client.o echo_client_handler.o diff --git a/examples/echo_client/echo_client.cpp b/examples/echo_client/echo_client.cpp index 5d9fcff200..62174ca986 100644 --- a/examples/echo_client/echo_client.cpp +++ b/examples/echo_client/echo_client.cpp @@ -62,13 +62,11 @@ int main(int argc, char* argv[]) { std::cout << "case count: " << c->m_case_count << std::endl; for (int i = 1; i <= c->m_case_count; i++) { - std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " Resetting io_service" << std::endl; io_service.reset(); client->set_alog_level(websocketpp::ALOG_OFF); client->set_elog_level(websocketpp::LOG_OFF); - std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " Initializing client (constructing socket)" << std::endl; client->init(); client->set_header("User Agent","WebSocket++/2011-10-27"); @@ -77,13 +75,9 @@ int main(int argc, char* argv[]) { url << "ws://localhost:9001/runCase?case=" << i << "&agent=\"WebSocket++Snapshot/2011-10-27\""; - std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " Parsing URL and resolving DNS" << std::endl; client->connect(url.str()); - std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " Starting io_service for test " << i << std::endl; io_service.run(); - - std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " test " << i << " complete" << std::endl; } std::cout << "done" << std::endl; diff --git a/examples/echo_client/echo_client_handler.cpp b/examples/echo_client/echo_client_handler.cpp index 2fd72272db..b91b4d69f1 100644 --- a/examples/echo_client/echo_client_handler.cpp +++ b/examples/echo_client/echo_client_handler.cpp @@ -34,11 +34,11 @@ using websocketecho::echo_client_handler; using websocketpp::client_session_ptr; void echo_client_handler::on_open(session_ptr s) { - std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " Successfully connected (handshake complete): " << s->get_resource() << std::endl; + std::cout << " Successfully connected (handshake complete): " << s->get_resource() << std::endl; } void echo_client_handler::on_close(session_ptr s) { - std::cout << boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << " client was disconnected (WS state is now CLOSED)" << std::endl; + std::cout << " client was disconnected (WS state is now CLOSED)" << std::endl; } void echo_client_handler::on_message(session_ptr s,const std::string &msg) { diff --git a/examples/echo_server/Makefile b/examples/echo_server/Makefile index df12f2a59d..ab1c38d55d 100644 --- a/examples/echo_server/Makefile +++ b/examples/echo_server/Makefile @@ -7,7 +7,7 @@ SHARED ?= "1" ifeq ($(SHARED), 1) LDFLAGS := $(LDFLAGS) -lboost_system -lboost_date_time -lwebsocketpp else - LDFLAGS := $(LDFLAGS) -lboost_system -lboost_date_time -lboost_regex -lboost_random ../../libwebsocketpp.a + LDFLAGS := $(LDFLAGS) -lboost_system -lboost_date_time -lboost_regex -lboost_random -lboost_program_options ../../libwebsocketpp.a endif echo_server: echo_server.o echo.o diff --git a/examples/echo_server/echo_server.cpp b/examples/echo_server/echo_server.cpp index 4a5e21b1c7..25ab4b5a50 100644 --- a/examples/echo_server/echo_server.cpp +++ b/examples/echo_server/echo_server.cpp @@ -60,6 +60,10 @@ int main(int argc, char* argv[]) { new websocketpp::server(io_service,endpoint,echo_handler) ); + //server->parse_command_line(argc, argv); + + + // setup server settings //server->set_alog_level(websocketpp::ALOG_OFF); //server->set_elog_level(websocketpp::LOG_OFF); diff --git a/src/websocket_server.cpp b/src/websocket_server.cpp index c0040e9031..52ecce784b 100644 --- a/src/websocket_server.cpp +++ b/src/websocket_server.cpp @@ -45,10 +45,11 @@ server::server(boost::asio::io_service& io_service, m_io_service(io_service), m_acceptor(io_service, endpoint), m_def_con_handler(defc), - m_desc("test") { - m_desc.add_options() - ("help", "produce help message") - ("intval",po::value(), "set compression level") + 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") ; } @@ -56,6 +57,20 @@ server::server(boost::asio::io_service& io_service, void server::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(); + + const std::vector< std::string > &foo = m_vm["host"].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; } void server::add_host(std::string host) { diff --git a/websocketpp.xcodeproj/project.pbxproj b/websocketpp.xcodeproj/project.pbxproj index ae545d4e8b..3bfb7e15dc 100644 --- a/websocketpp.xcodeproj/project.pbxproj +++ b/websocketpp.xcodeproj/project.pbxproj @@ -120,6 +120,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + B6138760145AD09700ED9B19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; name = Makefile; path = examples/echo_server/Makefile; sourceTree = ""; }; + B6138762145AD0A500ED9B19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; name = Makefile; path = examples/echo_client/Makefile; sourceTree = ""; }; + B6138763145AD1F700ED9B19 /* chat_client.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = chat_client.html; path = examples/chat_server/chat_client.html; sourceTree = ""; }; + B6138764145AD1F700ED9B19 /* chat_server.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = chat_server.cpp; path = examples/chat_server/chat_server.cpp; sourceTree = ""; }; + B6138765145AD1F700ED9B19 /* chat.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = chat.cpp; path = examples/chat_server/chat.cpp; sourceTree = ""; }; + B6138766145AD1F700ED9B19 /* chat.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = chat.hpp; path = examples/chat_server/chat.hpp; sourceTree = ""; }; + B6138767145AD1F700ED9B19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; name = Makefile; path = examples/chat_server/Makefile; 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 = ""; }; @@ -348,6 +355,7 @@ B6DF1CCA1435ED760029A1B1 /* echo_server.cpp */, B6DF1CCB1435ED760029A1B1 /* echo.cpp */, B6DF1CCC1435ED760029A1B1 /* echo.hpp */, + B6138760145AD09700ED9B19 /* Makefile */, ); name = echo_server; sourceTree = ""; @@ -358,6 +366,7 @@ B6828875143745DA002BA48B /* chat_client_handler.cpp */, B6828876143745DA002BA48B /* chat_client_handler.hpp */, B6828877143745DA002BA48B /* chat_client.cpp */, + B6138762145AD0A500ED9B19 /* Makefile */, ); name = chat_client; sourceTree = ""; @@ -365,6 +374,11 @@ B6DF1CC91435ED460029A1B1 /* chat_server */ = { isa = PBXGroup; children = ( + B6138763145AD1F700ED9B19 /* chat_client.html */, + B6138764145AD1F700ED9B19 /* chat_server.cpp */, + B6138765145AD1F700ED9B19 /* chat.cpp */, + B6138766145AD1F700ED9B19 /* chat.hpp */, + B6138767145AD1F700ED9B19 /* Makefile */, ); name = chat_server; sourceTree = ""; From 329c6b4d206cfbb3c82123a050ef3dc67049978e Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Fri, 28 Oct 2011 09:36:34 -0500 Subject: [PATCH 40/40] Xcode update --- websocketpp.xcodeproj/project.pbxproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/websocketpp.xcodeproj/project.pbxproj b/websocketpp.xcodeproj/project.pbxproj index 46d48c15f3..c44ea01195 100644 --- a/websocketpp.xcodeproj/project.pbxproj +++ b/websocketpp.xcodeproj/project.pbxproj @@ -496,7 +496,7 @@ B6DF1C471434A5940029A1B1 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0410; + LastUpgradeCheck = 0420; }; buildConfigurationList = B6DF1C4A1434A5940029A1B1 /* Build configuration list for PBXProject "websocketpp" */; compatibilityVersion = "Xcode 3.2"; @@ -835,6 +835,7 @@ B6CF18251437C397009295BE /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; B6DF1C4A1434A5940029A1B1 /* Build configuration list for PBXProject "websocketpp" */ = { isa = XCConfigurationList;