diff --git a/examples/chat_server/chat.hpp b/examples/chat_server/chat.hpp index 7dd97f9a29..2e6865fc9a 100644 --- a/examples/chat_server/chat.hpp +++ b/examples/chat_server/chat.hpp @@ -66,7 +66,7 @@ public: } // someone disconnected from the lobby, remove them - void disconnect(websocketpp::session_ptr client,const std::string &reason) { + void disconnect(websocketpp::session_ptr client,uint16_t status,const std::string &reason) { std::set::iterator it = m_connections.find(client); if (it == m_connections.end()) { diff --git a/examples/echo_server/echo.hpp b/examples/echo_server/echo.hpp index 01e0c6869d..3cc9a768f9 100644 --- a/examples/echo_server/echo.hpp +++ b/examples/echo_server/echo.hpp @@ -47,7 +47,7 @@ public: // an echo server is stateless. The handler has no need to keep track of connected // clients. void connect(websocketpp::session_ptr client) {} - void disconnect(websocketpp::session_ptr client,const std::string &reason) {} + void disconnect(websocketpp::session_ptr client,uint16_t status,const std::string &reason) {} // both text and binary messages are echoed back to the sending client. void message(websocketpp::session_ptr client,const std::string &msg); diff --git a/examples/echo_server/echo_server b/examples/echo_server/echo_server index 42c197d31a..d79f41c27b 100755 Binary files a/examples/echo_server/echo_server and b/examples/echo_server/echo_server differ diff --git a/src/network_utilities.cpp b/src/network_utilities.cpp index cfc547a1b7..a6c870f135 100644 --- a/src/network_utilities.cpp +++ b/src/network_utilities.cpp @@ -50,4 +50,86 @@ uint64_t htonll(uint64_t src) { uint64_t ntohll(uint64_t src) { return htonll(src); +} + +std::string lookup_http_error_string(int code) { + switch (code) { + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Request Entity Too Large"; + case 414: + return "Request-URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 416: + return "Requested Range Not Satisfiable"; + case 417: + return "Expectation Failed"; + case 500: + return "Internal Server Error"; + case 501: + return "Not Implimented"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Timeout"; + case 505: + return "HTTP Version Not Supported"; + default: + return "Unknown"; + } +} + +std::string lookup_ws_close_status_string(uint16_t code) { + switch (code) { + case 1000: + return "Normal closure"; + case 1001: + return "Going away"; + case 1002: + return "Protocol error"; + case 1003: + return "Unacceptable data"; + case 1004: + return "Reserved"; + case 1005: + return "No status received"; + case 1006: + return "Abnormal closure"; + case 1007: + return "Invalid message data"; + case 1008: + return "Policy Violation"; + case 1009: + return "Message too large"; + case 1010: + return "Missing required extensions"; + default: + return "Unknown"; + } } \ No newline at end of file diff --git a/src/network_utilities.hpp b/src/network_utilities.hpp index 85c3a06c38..bd6d0009fe 100644 --- a/src/network_utilities.hpp +++ b/src/network_utilities.hpp @@ -29,6 +29,7 @@ #define NETWORK_UTILITIES_HPP #include +#include // http://www.viva64.com/en/k/0018/ // TODO: impliment stuff from here: @@ -41,4 +42,8 @@ uint64_t htonll(uint64_t src); uint64_t ntohll(uint64_t src); +std::string lookup_http_error_string(int code); +std::string lookup_ws_close_status_string(uint16_t code); + + #endif // NETWORK_UTILITIES_HPP \ No newline at end of file diff --git a/src/websocket_connection_handler.hpp b/src/websocket_connection_handler.hpp index 4ee93aeb97..4ce52ea260 100644 --- a/src/websocket_connection_handler.hpp +++ b/src/websocket_connection_handler.hpp @@ -69,7 +69,7 @@ public: // calls the disconnect method of session // - The connection handler assigned to this client was set to another // handler - virtual void disconnect(session_ptr client,const std::string &reason) = 0; + virtual void disconnect(session_ptr client,uint16_t status,const std::string &reason) = 0; // this will be called when a text message is recieved. Text will be // encoded as UTF-8. diff --git a/src/websocket_frame.cpp b/src/websocket_frame.cpp index fb68f10dae..ae55ea1bfd 100644 --- a/src/websocket_frame.cpp +++ b/src/websocket_frame.cpp @@ -26,6 +26,7 @@ */ #include "websocket_frame.hpp" +#include "websocket_server.hpp" #include #include @@ -160,6 +161,31 @@ size_t frame::get_payload_size() const { return m_payload.size(); } +uint16_t frame::get_close_status() const { + if (get_payload_size() >= 2) { + char val[2]; + + val[0] = m_payload[0]; + val[1] = m_payload[1]; + + uint16_t code = ntohs(*( + reinterpret_cast(&val[0]) + )); + + return code; + } else { + return 1005; // defined in spec as "no status recieved" + } +} + +std::string frame::get_close_msg() const { + if (get_payload_size() > 2) { + return std::string(m_payload.begin()+2,m_payload.end()); + } else { + return std::string(); + } +} + std::vector &frame::get_payload() { return m_payload; } @@ -209,6 +235,28 @@ void frame::set_payload_helper(size_t s) { m_payload.resize(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"); + } + + 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."); + } + + m_payload.resize(2+message.size()); + + char val[2]; + + *reinterpret_cast(&val[0]) = htons(status); + + m_payload[0] = val[0]; + m_payload[1] = val[1]; + + std::copy(message.begin(),message.end(),m_payload.begin()+2); +} + void frame::print_frame() const { /*unsigned int len = get_header_len(); diff --git a/src/websocket_frame.hpp b/src/websocket_frame.hpp index b139261bd9..101f266be4 100644 --- a/src/websocket_frame.hpp +++ b/src/websocket_frame.hpp @@ -106,12 +106,17 @@ public: uint8_t get_basic_size() const; size_t get_payload_size() const; + uint16_t get_close_status() const; + std::string get_close_msg() const; + std::vector &get_payload(); void set_payload(const std::vector source); void set_payload(const std::string source); void set_payload_helper(size_t s); + void set_status(uint16_t status,const std::string message = ""); + bool is_control() const; void print_frame() const; diff --git a/src/websocket_session.cpp b/src/websocket_session.cpp index 9f4f47c5b9..9371547ad0 100644 --- a/src/websocket_session.cpp +++ b/src/websocket_session.cpp @@ -72,7 +72,7 @@ void session::start() { void session::set_handler(connection_handler_ptr new_con) { if (m_local_interface) { - m_local_interface->disconnect(shared_from_this(),"Setting new connection handler"); + m_local_interface->disconnect(shared_from_this(),4000,"Setting new connection handler"); } m_local_interface = new_con; m_local_interface->connect(shared_from_this()); @@ -104,59 +104,6 @@ std::string session::get_origin() const { } } -std::string session::lookup_http_error_string(int code) { - switch (code) { - case 400: - return "Bad Request"; - case 401: - return "Unauthorized"; - case 403: - return "Forbidden"; - case 404: - return "Not Found"; - case 405: - return "Method Not Allowed"; - case 406: - return "Not Acceptable"; - case 407: - return "Proxy Authentication Required"; - case 408: - return "Request Timeout"; - case 409: - return "Conflict"; - case 410: - return "Gone"; - case 411: - return "Length Required"; - case 412: - return "Precondition Failed"; - case 413: - return "Request Entity Too Large"; - case 414: - return "Request-URI Too Long"; - case 415: - return "Unsupported Media Type"; - case 416: - return "Requested Range Not Satisfiable"; - case 417: - return "Expectation Failed"; - case 500: - return "Internal Server Error"; - case 501: - return "Not Implimented"; - case 502: - return "Bad Gateway"; - case 503: - return "Service Unavailable"; - case 504: - return "Gateway Timeout"; - case 505: - return "HTTP Version Not Supported"; - default: - return "Unknown"; - } -} - void session::send(const std::string &msg) { m_write_frame.set_fin(true); m_write_frame.set_opcode(frame::TEXT_FRAME); @@ -175,15 +122,15 @@ void session::send(const std::vector &data) { } // send close frame -void session::disconnect(const std::string &reason) { +void session::disconnect(uint16_t status,const std::string &message) { m_write_frame.set_fin(true); m_write_frame.set_opcode(frame::CONNECTION_CLOSE); - m_write_frame.set_payload(reason); + m_write_frame.set_status(status,message); write_frame(); if (m_local_interface) { - m_local_interface->disconnect(shared_from_this(),reason); + m_local_interface->disconnect(shared_from_this(),status,message); } } @@ -611,25 +558,26 @@ void session::process_continuation() { void session::process_close() { if (m_status == OPEN) { - // send response and set to closed - std::string msg(m_read_frame.get_payload().begin(), - m_read_frame.get_payload().end()); + uint16_t status = m_read_frame.get_close_status(); + std::string message = m_read_frame.get_close_msg(); - std::cout << "Got connection close message, acking and closing the connection. Reason was: " << msg << std::endl; + std::stringstream msg; + msg << "[Connection " << this << "] Got connection close message. Close status:" << status << " (" << lookup_ws_close_status_string(status) << "), close message: " << message; + m_server->access_log(msg.str()); m_status = CLOSED; // send acknowledgement m_write_frame.set_fin(true); m_write_frame.set_opcode(frame::CONNECTION_CLOSE); - m_write_frame.set_payload(""); + //m_write_frame.set_status(status); // ack shouldn't have a payload write_frame(); // let our local interface know that the remote client has // disconnected if (m_local_interface) { - m_local_interface->disconnect(shared_from_this(),msg); + m_local_interface->disconnect(shared_from_this(),status,message); } } else if (m_status == CLOSING) { // this is an ack of my close message @@ -639,9 +587,9 @@ void session::process_close() { // let our local interface know that the remote client has // disconnected and the reason (if any) - if (m_local_interface) { + /*if (m_local_interface) { m_local_interface->disconnect(shared_from_this(),""); - } + }*/ } else { // ignore } @@ -721,12 +669,12 @@ void session::handle_error(std::string msg, const boost::system::error_code& error) { std::stringstream e; - e << msg << " (" << error << ")"; - - std::cerr << e.str() << std::endl; + e << "[Connection " << this << "] " << msg << " (" << error << ")"; + m_server->error_log(e.str()); + if (m_local_interface) { - m_local_interface->disconnect(shared_from_this(),e.str()); + m_local_interface->disconnect(shared_from_this(),1006,e.str()); } m_error = true; diff --git a/src/websocket_session.hpp b/src/websocket_session.hpp index c07ceff675..0be3dbeef9 100644 --- a/src/websocket_session.hpp +++ b/src/websocket_session.hpp @@ -126,7 +126,7 @@ public: void ping(const std::string &msg); void pong(const std::string &msg); - void disconnect(const std::string &reason); + void disconnect(uint16_t status,const std::string &reason); private: // handle_read_handshake reads the HTTP headers of the initial websocket // handshake, parses out the request and headers, and does error checking @@ -191,8 +191,6 @@ private: // prints a diagnostic message and disconnects the local interface void handle_error(std::string msg,const boost::system::error_code& error); - - std::string lookup_http_error_string(int code); private: // Immutable state about the current connection from the handshake std::string m_request;