From ccb34c98b439e523e669d7532f4211425f308648 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 27 Oct 2011 09:42:11 -0500 Subject: [PATCH] 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"