From 1e97e3dcf5001682ed43cb33bfb6f4ac0d49c98a Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Sun, 7 Apr 2013 12:18:03 -0500 Subject: [PATCH] adds connection client methods for adding subprotocols to the handshake request --- readme.txt | 3 +- test/roles/client.cpp | 60 +++++++++++++++++++++++++++- websocketpp/connection.hpp | 37 ++++++++++++++++- websocketpp/error.hpp | 17 +++++++- websocketpp/impl/connection_impl.hpp | 35 ++++++++++++++++ 5 files changed, 145 insertions(+), 7 deletions(-) diff --git a/readme.txt b/readme.txt index 448842e9fd..b23a77f4a8 100644 --- a/readme.txt +++ b/readme.txt @@ -36,7 +36,7 @@ Implimented, needs more testing - exception/error handling - Logging - Client role -- Server subprotocol negotiation +- Subprotocol negotiation Implimented, API not finalized - open_handler @@ -46,7 +46,6 @@ Implimented, API not finalized Needs work: - PowerPC support -- Client subprotocol negotiation - Extension support - permessage_compress extension - Visual Studio / Windows support diff --git a/test/roles/client.cpp b/test/roles/client.cpp index 045d1aa11c..2dcbc18065 100644 --- a/test/roles/client.cpp +++ b/test/roles/client.cpp @@ -133,7 +133,63 @@ BOOST_AUTO_TEST_CASE( connect_con ) { channel2 >> *con; } - - +BOOST_AUTO_TEST_CASE( select_subprotocol ) { + client c; + websocketpp::lib::error_code ec; + using websocketpp::error::make_error_code; + + connection_ptr con = c.get_connection("ws://localhost/", ec); + + BOOST_CHECK( con ); + + con->select_subprotocol("foo",ec); + BOOST_CHECK_EQUAL( ec , make_error_code(websocketpp::error::server_only) ); + BOOST_CHECK_THROW( con->select_subprotocol("foo") , websocketpp::lib::error_code ); +} + +BOOST_AUTO_TEST_CASE( add_subprotocols_invalid ) { + client c; + websocketpp::lib::error_code ec; + using websocketpp::error::make_error_code; + + connection_ptr con = c.get_connection("ws://localhost/", ec); + BOOST_CHECK( con ); + + con->add_subprotocol("",ec); + BOOST_CHECK_EQUAL( ec , make_error_code(websocketpp::error::invalid_subprotocol) ); + BOOST_CHECK_THROW( con->add_subprotocol("") , websocketpp::lib::error_code ); + + con->add_subprotocol("foo,bar",ec); + BOOST_CHECK_EQUAL( ec , make_error_code(websocketpp::error::invalid_subprotocol) ); + BOOST_CHECK_THROW( con->add_subprotocol("foo,bar") , websocketpp::lib::error_code ); +} + +BOOST_AUTO_TEST_CASE( add_subprotocols ) { + client c; + websocketpp::lib::error_code ec; + std::stringstream out; + std::string o; + + c.register_ostream(&out); + + connection_ptr con = c.get_connection("ws://localhost/", ec); + BOOST_CHECK( con ); + + con->add_subprotocol("foo",ec); + BOOST_CHECK( !ec ); + + BOOST_CHECK_NO_THROW( con->add_subprotocol("bar") ); + + c.connect(con); + + o = out.str(); + websocketpp::http::parser::request r; + r.consume(o.data(),o.size()); + + std::cout << o << std::endl; + + BOOST_CHECK( r.ready() ); + BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Protocol"), "foo, bar"); +} diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp index 08a9242654..c4113af371 100644 --- a/websocketpp/connection.hpp +++ b/websocketpp/connection.hpp @@ -39,11 +39,11 @@ #include #include - +#include #include -#include #include #include +#include namespace websocketpp { @@ -542,6 +542,35 @@ public: */ const std::vector & get_requested_subprotocols() const; + /// Adds the given subprotocol string to the request list (exception free) + /** + * Adds a subprotocol to the list to send with the opening handshake. This + * may be called multiple times to request more than one. If the server + * supports one of these, it may choose one. If so, it will return it + * in it's handshake reponse and the value will be available via + * get_subprotocol(). Subprotocol requests should be added in order of + * preference. + * + * @param request The subprotocol to request + * + * @param ec A reference to an error code that will be filled in the case of + * errors + */ + void add_subprotocol(const std::string &request, lib::error_code & ec); + + /// Adds the given subprotocol string to the request list + /** + * Adds a subprotocol to the list to send with the opening handshake. This + * may be called multiple times to request more than one. If the server + * supports one of these, it may choose one. If so, it will return it + * in it's handshake reponse and the value will be available via + * get_subprotocol(). Subprotocol requests should be added in order of + * preference. + * + * @param request The subprotocol to request + */ + void add_subprotocol(const std::string &request); + /// Select a subprotocol to use (exception free) /** * Indicates which subprotocol should be used for this connection. Valid @@ -549,6 +578,8 @@ public: * been requested by the client. Consult get_requested_subprotocols() for a * list of valid subprotocols. * + * This member function is valid on server endpoints/connections only + * * @param value The subprotocol to select * * @param ec A reference to an error code that will be filled in the case of @@ -563,6 +594,8 @@ public: * been requested by the client. Consult get_requested_subprotocols() for a * list of valid subprotocols. * + * This member function is valid on server endpoints/connections only + * * @param value The subprotocol to select */ void select_subprotocol(const std::string & value); diff --git a/websocketpp/error.hpp b/websocketpp/error.hpp index 8277ac4a95..21bfd7caf0 100644 --- a/websocketpp/error.hpp +++ b/websocketpp/error.hpp @@ -77,6 +77,9 @@ enum value { /// Invalid UTF-8 invalid_utf8, + /// Invalid subprotocol + invalid_subprotocol, + /// Bad or unknown connection bad_connection, @@ -87,7 +90,13 @@ enum value { con_creation_failed, /// Selected subprotocol was not requested by the client - unrequested_subprotocol + unrequested_subprotocol, + + /// Attempted to use a client specific feature on a server endpoint + client_only, + + /// Attempted to use a server specific feature on a client endpoint + server_only, }; // enum value @@ -125,6 +134,8 @@ public: return "Extracted close code is in a reserved range"; case error::invalid_utf8: return "Invalid UTF-8"; + case error::invalid_subprotocol: + return "Invalid subprotocol"; case error::bad_connection: return "Bad Connection"; case error::test: @@ -133,6 +144,10 @@ public: return "Connection creation attempt failed"; case error::unrequested_subprotocol: return "Selected subprotocol was not requested by the client"; + case error::client_only: + return "Feature not available on server endpoints"; + case error::server_only: + return "Feature not available on client endpoints"; default: return "Unknown"; } diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index a0f4939048..45ca188476 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -327,10 +327,45 @@ connection::get_requested_subprotocols() const { return m_requested_subprotocols; } +template +void connection::add_subprotocol(const std::string & value, + lib::error_code & ec) +{ + if (m_is_server) { + ec = error::make_error_code(error::client_only); + return; + } + + // If the value is empty or has a non-RFC2616 token character it is invalid. + if (value.empty() || std::find_if(value.begin(),value.end(), + http::is_not_token_char) != value.end()) + { + ec = error::make_error_code(error::invalid_subprotocol); + return; + } + + m_requested_subprotocols.push_back(value); +} + +template +void connection::add_subprotocol(const std::string & value) { + lib::error_code ec; + this->add_subprotocol(value,ec); + if (ec) { + throw ec; + } +} + + template void connection::select_subprotocol(const std::string & value, lib::error_code & ec) { + if (!m_is_server) { + ec = error::make_error_code(error::server_only); + return; + } + if (value.empty()) { ec = lib::error_code(); return;