From 4474868372b501b6c31e4e5f9a9a86d8d1155de6 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 16 May 2013 10:30:38 -0500 Subject: [PATCH 1/7] adds unique_lock and condition_variable to common thread tools --- websocketpp/common/thread.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/websocketpp/common/thread.hpp b/websocketpp/common/thread.hpp index 2aac62de20..b2ae135f68 100644 --- a/websocketpp/common/thread.hpp +++ b/websocketpp/common/thread.hpp @@ -50,10 +50,14 @@ namespace lib { using std::mutex; using std::lock_guard; using std::thread; + using std::unique_lock; + using std::condition_variable; #else using boost::mutex; using boost::lock_guard; using boost::thread; + using boost::unique_lock; + using boost::condition_variable; #endif } // namespace lib From f988315ea82aa6ca2a960d75daabd3475787f586 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Thu, 16 May 2013 10:31:02 -0500 Subject: [PATCH 2/7] updates broadcast_server to use common/thread rather than hardcoding boost thread --- examples/broadcast_server/SConscript | 4 +- .../broadcast_server/broadcast_server.cpp | 39 ++++++++++++------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/examples/broadcast_server/SConscript b/examples/broadcast_server/SConscript index 4ec5759dba..e2d28ccec8 100644 --- a/examples/broadcast_server/SConscript +++ b/examples/broadcast_server/SConscript @@ -14,10 +14,10 @@ prgs = [] # if a C++11 environment is avaliable build using that, otherwise use boost if env_cpp11.has_key('WSPP_CPP11_ENABLED'): - ALL_LIBS = boostlibs(['system','thread'],env_cpp11) + [platform_libs] + [polyfill_libs] + ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs] prgs += env_cpp11.Program('broadcast_server', ["broadcast_server.cpp"], LIBS = ALL_LIBS) else: - ALL_LIBS = boostlibs(['system','regex'],env) + [platform_libs] + [polyfill_libs] + ALL_LIBS = boostlibs(['system','thread','regex'],env) + [platform_libs] + [polyfill_libs] prgs += env.Program('broadcast_server', ["broadcast_server.cpp"], LIBS = ALL_LIBS) Return('prgs') diff --git a/examples/broadcast_server/broadcast_server.cpp b/examples/broadcast_server/broadcast_server.cpp index e0691a0b22..70e48b9554 100644 --- a/examples/broadcast_server/broadcast_server.cpp +++ b/examples/broadcast_server/broadcast_server.cpp @@ -4,9 +4,10 @@ #include -#include +/*#include #include -#include +#include */ +#include typedef websocketpp::server server; @@ -15,6 +16,11 @@ using websocketpp::lib::placeholders::_1; using websocketpp::lib::placeholders::_2; using websocketpp::lib::bind; +using websocketpp::lib::thread; +using websocketpp::lib::mutex; +using websocketpp::lib::unique_lock; +using websocketpp::lib::condition_variable; + /* on_open insert connection_hdl into channel * on_close remove connection_hdl from channel * on_message queue send to all channels @@ -67,7 +73,7 @@ public: } void on_open(connection_hdl hdl) { - boost::unique_lock lock(m_action_lock); + unique_lock lock(m_action_lock); //std::cout << "on_open" << std::endl; m_actions.push(action(SUBSCRIBE,hdl)); lock.unlock(); @@ -75,7 +81,7 @@ public: } void on_close(connection_hdl hdl) { - boost::unique_lock lock(m_action_lock); + unique_lock lock(m_action_lock); //std::cout << "on_close" << std::endl; m_actions.push(action(UNSUBSCRIBE,hdl)); lock.unlock(); @@ -84,7 +90,7 @@ public: void on_message(connection_hdl hdl, server::message_ptr msg) { // queue message up for sending by processing thread - boost::unique_lock lock(m_action_lock); + unique_lock lock(m_action_lock); //std::cout << "on_message" << std::endl; m_actions.push(action(MESSAGE,msg)); lock.unlock(); @@ -93,7 +99,7 @@ public: void process_messages() { while(1) { - boost::unique_lock lock(m_action_lock); + unique_lock lock(m_action_lock); while(m_actions.empty()) { m_action_cond.wait(lock); @@ -105,13 +111,13 @@ public: lock.unlock(); if (a.type == SUBSCRIBE) { - boost::unique_lock lock(m_connection_lock); + unique_lock lock(m_connection_lock); m_connections.insert(a.hdl); } else if (a.type == UNSUBSCRIBE) { - boost::unique_lock lock(m_connection_lock); + unique_lock lock(m_connection_lock); m_connections.erase(a.hdl); } else if (a.type == MESSAGE) { - boost::unique_lock lock(m_connection_lock); + unique_lock lock(m_connection_lock); con_list::iterator it; for (it = m_connections.begin(); it != m_connections.end(); ++it) { @@ -129,17 +135,24 @@ private: con_list m_connections; std::queue m_actions; - boost::mutex m_action_lock; - boost::mutex m_connection_lock; - boost::condition_variable m_action_cond; + mutex m_action_lock; + mutex m_connection_lock; + condition_variable m_action_cond; }; int main() { + try { broadcast_server server; // Start a thread to run the processing loop - boost::thread(bind(&broadcast_server::process_messages,&server)); + thread t(bind(&broadcast_server::process_messages,&server)); // Run the asio loop with the main thread server.run(9002); + + t.join(); + + } catch (std::exception & e) { + std::cout << e.what() << std::endl; + } } From 8c6b049e999231e7c4e009e1834fff4ab8eb1306 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Sat, 18 May 2013 06:53:17 -0500 Subject: [PATCH 3/7] adds better support for custom URI schemes --- websocketpp/uri.hpp | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/websocketpp/uri.hpp b/websocketpp/uri.hpp index 090e4cfded..aa43561cde 100644 --- a/websocketpp/uri.hpp +++ b/websocketpp/uri.hpp @@ -63,10 +63,11 @@ public: explicit uri(const std::string& uri) { // TODO: should this split resource into path/query? lib::cmatch matches; - const lib::regex expression("(http|ws|wss)://([^/:\\[]+|\\[[0-9a-fA-F:.]+\\])(:\\d{1,5})?(/[^#]*)?"); + const lib::regex expression("(http|https|ws|wss)://([^/:\\[]+|\\[[0-9a-fA-F:.]+\\])(:\\d{1,5})?(/[^#]*)?"); if (lib::regex_match(uri.c_str(), matches, expression)) { - m_secure = (matches[1] == "wss"); + m_scheme = matches[1]; + m_secure = (m_scheme == "wss" || m_scheme == "https"); m_host = matches[2]; // strip brackets from IPv6 literal URIs @@ -169,23 +170,48 @@ public: }*/ uri(bool secure, const std::string& host, uint16_t port, const std::string& resource) - : m_host(host) + : m_scheme(secure ? "wss" : "ws") + , m_host(host) , m_resource(resource == "" ? "/" : resource) , m_port(port) , m_secure(secure) {} uri(bool secure, const std::string& host, const std::string& resource) - : m_host(host) + : m_scheme(secure ? "wss" : "ws") + , m_host(host) , m_resource(resource == "" ? "/" : resource) , m_port(secure ? uri_default_secure_port : uri_default_port) , m_secure(secure) {} uri(bool secure, const std::string& host, const std::string& port, const std::string& resource) - : m_host(host) + : m_scheme(secure ? "wss" : "ws") + , m_host(host) , m_resource(resource == "" ? "/" : resource) , m_port(get_port_from_string(port)) , m_secure(secure) {} + + uri(std::string scheme, const std::string& host, uint16_t port, const std::string& resource) + : m_scheme(scheme) + , m_host(host) + , m_resource(resource == "" ? "/" : resource) + , m_port(port) + , m_secure(scheme == "wss" || scheme == "https") {} + + uri(std::string scheme, const std::string& host, const std::string& resource) + : m_scheme(scheme) + , m_host(host) + , m_resource(resource == "" ? "/" : resource) + , m_port((scheme == "wss" || scheme == "https") ? uri_default_secure_port : uri_default_port) + , m_secure(scheme == "wss" || scheme == "https") {} + + uri(std::string scheme, const std::string& host, const std::string& port, const std::string& resource) + : m_scheme(scheme) + , m_host(host) + , m_resource(resource == "" ? "/" : resource) + , m_port(get_port_from_string(port)) + , m_secure(scheme == "wss" || scheme == "https") {} + bool get_secure() const { return m_secure; } @@ -227,7 +253,7 @@ public: std::string str() const { std::stringstream s; - s << "ws" << (m_secure ? "s" : "") << "://" << m_host; + s << m_scheme << "://" << m_host; if (m_port != (m_secure ? uri_default_secure_port : uri_default_port)) { s << ":" << m_port; @@ -274,8 +300,7 @@ private: return static_cast(t_port); } - - + std::string m_scheme; std::string m_host; std::string m_resource; uint16_t m_port; From 065dd26779f9c20040fd957fcf26a1e3f692f0b5 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Sat, 18 May 2013 06:53:46 -0500 Subject: [PATCH 4/7] adds free processor extract uri from request utility method --- websocketpp/processors/processor.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/websocketpp/processors/processor.hpp b/websocketpp/processors/processor.hpp index 6725c97c50..cde2f58c39 100644 --- a/websocketpp/processors/processor.hpp +++ b/websocketpp/processors/processor.hpp @@ -106,6 +106,29 @@ int get_websocket_version(request_type& r) { return version; } +template +uri_ptr get_uri_from_host(request_type & request, std::string scheme) { + std::string h = request.get_header("Host"); + + size_t last_colon = h.rfind(":"); + size_t last_sbrace = h.rfind("]"); + + // no : = hostname with no port + // last : before ] = ipv6 literal with no port + // : with no ] = hostname with port + // : after ] = ipv6 literal with port + if (last_colon == std::string::npos || + (last_sbrace != std::string::npos && last_sbrace > last_colon)) + { + return uri_ptr(new uri(scheme, h, request.get_uri())); + } else { + return uri_ptr(new uri(scheme, + h.substr(0,last_colon), + h.substr(last_colon+1), + request.get_uri())); + } +} + // All other functions are WebSocket version dependent. processor is a base // class for version dependent processing functions. From dd534ace64c475902375ca63195455ecfad7e020 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Sat, 18 May 2013 06:54:32 -0500 Subject: [PATCH 5/7] switches processor to use new free get_uri function --- websocketpp/processors/hybi13.hpp | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/websocketpp/processors/hybi13.hpp b/websocketpp/processors/hybi13.hpp index 55e22ac7af..b4ee5f53ed 100644 --- a/websocketpp/processors/hybi13.hpp +++ b/websocketpp/processors/hybi13.hpp @@ -291,27 +291,7 @@ public: } uri_ptr get_uri(const request_type& request) const { - std::string h = request.get_header("Host"); - - size_t last_colon = h.rfind(":"); - size_t last_sbrace = h.rfind("]"); - - // no : = hostname with no port - // last : before ] = ipv6 literal with no port - // : with no ] = hostname with port - // : after ] = ipv6 literal with port - if (last_colon == std::string::npos || - (last_sbrace != std::string::npos && last_sbrace > last_colon)) - { - return uri_ptr(new uri(base::m_secure,h,request.get_uri())); - } else { - return uri_ptr(new uri(base::m_secure, - h.substr(0,last_colon), - h.substr(last_colon+1), - request.get_uri())); - } - - // TODO: check if get_uri is a full uri + return get_uri_from_host(request,(base::m_secure ? "wss" : "ws")); } /// Process new websocket connection bytes From d7e5235036817dc75716e106f217515542034a62 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Sat, 18 May 2013 06:54:56 -0500 Subject: [PATCH 6/7] extract URI for HTTP connections also --- websocketpp/impl/connection_impl.hpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp index 06b0b809d4..71cd96b6c9 100644 --- a/websocketpp/impl/connection_impl.hpp +++ b/websocketpp/impl/connection_impl.hpp @@ -900,7 +900,17 @@ bool connection::process_handshake_request() { if (!processor::is_websocket_handshake(m_request)) { // this is not a websocket handshake. Process as plain HTTP m_alog.write(log::alevel::devel,"HTTP REQUEST"); - + + // extract URI from request + try { + m_uri = processor::get_uri_from_host(m_request,(transport_con_type::is_secure() ? "https" : "http")); + } catch (const websocketpp::uri_exception& e) { + m_alog.write(log::alevel::devel, + std::string("BAD REQUEST: uri failed to parse: ")+e.what()); + m_response.set_status(http::status_code::bad_request); + return false; + } + if (m_http_handler) { m_http_handler(m_connection_hdl); } From 8f8bbb99e9eb5d672fea8cc2a2d18c0d17ba97d4 Mon Sep 17 00:00:00 2001 From: Peter Thorson Date: Sat, 18 May 2013 06:56:49 -0500 Subject: [PATCH 7/7] adds more unit tests for http requests --- test/connection/connection.cpp | 21 ++++++++++++++++++++- test/connection/connection_tu2.cpp | 19 +------------------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/test/connection/connection.cpp b/test/connection/connection.cpp index 5cbcea6ca8..38b147da9d 100644 --- a/test/connection/connection.cpp +++ b/test/connection/connection.cpp @@ -100,7 +100,14 @@ void validate_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) s->send(hdl, msg->get_payload(), msg->get_opcode()); } - +void http_func(server* s, websocketpp::connection_hdl hdl) { + server::connection_ptr con = s->get_con_from_hdl(hdl); + + std::string res = con->get_resource(); + + con->set_body(res); + con->set_status(websocketpp::http::status_code::ok); +} BOOST_AUTO_TEST_CASE( connection_extensions ) { connection_setup env(true); @@ -123,6 +130,18 @@ BOOST_AUTO_TEST_CASE( basic_websocket_request ) { BOOST_CHECK(run_server_test(s,input) == output); } +BOOST_AUTO_TEST_CASE( http_request ) { + std::string input = "GET /foo/bar HTTP/1.1\r\nHost: www.example.com\r\nOrigin: http://www.example.com\r\n\r\n"; + std::string output = "HTTP/1.1 200 OK\r\nContent-Length: 8\r\nServer: "; + output+=websocketpp::user_agent; + output+="\r\n\r\n/foo/bar"; + + server s; + s.set_http_handler(bind(&http_func,&s,::_1)); + + BOOST_CHECK_EQUAL(run_server_test(s,input), output); +} + /* BOOST_AUTO_TEST_CASE( user_reject_origin ) { diff --git a/test/connection/connection_tu2.cpp b/test/connection/connection_tu2.cpp index 9abd009afd..bbbc0287ec 100644 --- a/test/connection/connection_tu2.cpp +++ b/test/connection/connection_tu2.cpp @@ -33,24 +33,7 @@ void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { std::string run_server_test(std::string input) { server test_server; - server::connection_ptr con; - - test_server.set_message_handler(bind(&echo_func,&test_server,::_1,::_2)); - - std::stringstream output; - - test_server.register_ostream(&output); - - con = test_server.get_connection(); - - con->start(); - - std::stringstream channel; - - channel << input; - channel >> *con; - - return output.str(); + return run_server_test(test_server,input); } std::string run_server_test(server& s, std::string input) {