diff --git a/examples/broadcast_server_tls/broadcast_server_tls.cpp b/examples/broadcast_server_tls/broadcast_server_tls.cpp index 939d611d34..e97a338fa8 100644 --- a/examples/broadcast_server_tls/broadcast_server_tls.cpp +++ b/examples/broadcast_server_tls/broadcast_server_tls.cpp @@ -116,8 +116,8 @@ int main(int argc, char* argv[]) { std::cout << "Starting WebSocket broadcast server on port " << port << std::endl; e.listen(port); } - } catch (std::string e) { - //std::cerr << "Exception: " << e.what() << std::endl; + } catch (std::exception& e) { + std::cerr << "Exception: " << e.what() << std::endl; } diff --git a/src/processors/hybi.hpp b/src/processors/hybi.hpp index fe10e188d3..920dbb1fc5 100644 --- a/src/processors/hybi.hpp +++ b/src/processors/hybi.hpp @@ -186,13 +186,21 @@ public: uri_ptr get_uri(const http::parser::request& request) const { std::string h = request.header("Host"); - size_t found = h.find(":"); - if (found == std::string::npos) { + 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(m_connection.is_secure(),h,request.uri())); } else { return uri_ptr(new uri(m_connection.is_secure(), - h.substr(0,found), - h.substr(found+1), + h.substr(0,last_colon), + h.substr(last_colon+1), request.uri())); } diff --git a/src/processors/hybi_legacy.hpp b/src/processors/hybi_legacy.hpp index 65d2c044c4..acdb00b894 100644 --- a/src/processors/hybi_legacy.hpp +++ b/src/processors/hybi_legacy.hpp @@ -103,13 +103,21 @@ public: uri_ptr get_uri(const http::parser::request& request) const { std::string h = request.header("Host"); - size_t found = h.find(":"); - if (found == std::string::npos) { + 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(m_connection.is_secure(),h,request.uri())); } else { return uri_ptr(new uri(m_connection.is_secure(), - h.substr(0,found), - h.substr(found+1), + h.substr(0,last_colon), + h.substr(last_colon+1), request.uri())); } diff --git a/src/roles/client.hpp b/src/roles/client.hpp index 000c4e6cad..d73e2b9c49 100644 --- a/src/roles/client.hpp +++ b/src/roles/client.hpp @@ -263,6 +263,8 @@ client::connect(const std::string& u) { throw ""; } + std::cout << "host: " << location->get_host() << std::endl; + tcp::resolver::query query(location->get_host(),location->get_port_str()); tcp::resolver::iterator iterator = m_resolver.resolve(query); diff --git a/src/roles/server.hpp b/src/roles/server.hpp index 91dac63435..866424dfce 100644 --- a/src/roles/server.hpp +++ b/src/roles/server.hpp @@ -177,7 +177,10 @@ public: server(boost::asio::io_service& m) : m_ws_endpoint(static_cast< endpoint_type& >(*this)), m_io_service(m), - m_endpoint(), + // the only way to set an endpoint address family appears to be using + // this constructor, which also requires a port. This port number can be + // ignored, as it is always overwriten later by the listen() member func + m_endpoint(boost::asio::ip::tcp::v6(),9000), m_acceptor(m), m_timer(m,boost::posix_time::seconds(0)) {} @@ -415,15 +418,19 @@ void server::connection::handle_read_request( // Set URI std::string h = m_request.header("Host"); - size_t found = h.find(":"); - if (found == std::string::npos) { + size_t last_colon = h.rfind(":"); + size_t last_sbrace = h.rfind("]"); + + if (last_colon == std::string::npos || + (last_sbrace != std::string::npos && last_sbrace > last_colon)) + { // TODO: this makes the assumption that WS and HTTP // default ports are the same. m_uri.reset(new uri(m_endpoint.is_secure(),h,m_request.uri())); } else { m_uri.reset(new uri(m_endpoint.is_secure(), - h.substr(0,found), - h.substr(found+1), + h.substr(0,last_colon), + h.substr(last_colon+1), m_request.uri())); } diff --git a/src/uri.cpp b/src/uri.cpp index 5c884be58b..1955322b69 100644 --- a/src/uri.cpp +++ b/src/uri.cpp @@ -28,14 +28,140 @@ #include "uri.hpp" #include +#include #include using websocketpp::uri; uri::uri(const std::string& uri) { - boost::cmatch matches; - static const boost::regex expression("(ws|wss)://([^/:\\[]+|\\[[0-9:]+\\])(:\\d{1,5})?(/[^#]*)?"); + /*enum state { + BEGIN = 0, + HOST_BEGIN = 1, + READ_IPV6_LITERAL = 2, + READ_HOSTNAME = 3, + BEGIN_PORT = 4, + READ_PORT = 5, + READ_RESOURCE = 6, + }; + + state the_state = BEGIN; + std::string temp; + + for (std::string::const_iterator it = uri.begin(); it != uri.end(); it++) { + switch (the_state) { + case BEGIN: + // we are looking for a ws:// or wss:// + if (temp.size() < 5) { + temp.append(1,*it); + } else { + throw websocketpp::uri_exception("Scheme is too long"); + } + + if (temp == "ws://") { + m_secure = false; + the_state = HOST_BEGIN; + temp.clear(); + } else if (temp == "wss://") { + m_secure = true; + the_state = HOST_BEGIN; + temp.clear(); + } + break; + case HOST_BEGIN: + // if character is [ go into IPv6 literal state + // otherwise go into read hostname state + if (*it == '[') { + the_state = READ_IPV6_LITERAL; + } else { + *it--; + the_state = READ_HOSTNAME; + } + break; + case READ_IPV6_LITERAL: + // read 0-9a-fA-F:. until ] or 45 characters have been read + if (*it == ']') { + the_state = BEGIN_PORT; + break; + } + + if (m_host.size() > 45) { + throw websocketpp::uri_exception("IPv6 literal is too long"); + } + + if (*it == '.' || + *it == ':' || + (*it >= '0' && *it <= '9') || + (*it >= 'a' && *it <= 'f') || + (*it >= 'A' && *it <= 'F')) + { + m_host.append(1,*it); + } + break; + case READ_HOSTNAME: + // + if (*it == ':') { + it--; + the_state = BEGIN_PORT; + break; + } + + if (*it == '/') { + it--; + the_state = BEGIN_PORT; + break; + } + + // TODO: max url length? + + // TODO: check valid characters? + if (1) { + m_host.append(1,*it); + } + break; + case BEGIN_PORT: + if (*it == ':') { + the_state = READ_PORT; + } else if (*it == '/') { + m_port = get_port_from_string(""); + *it--; + the_state = READ_RESOURCE; + } else { + throw websocketpp::uri_exception("Error parsing WebSocket URI"); + } + break; + case READ_PORT: + if (*it >= '0' && *it <= '9') { + if (temp.size() < 5) { + temp.append(1,*it); + } else { + throw websocketpp::uri_exception("Port is too long"); + } + } else if (*it == '/') { + m_port = get_port_from_string(temp); + temp.clear(); + *it--; + the_state = READ_RESOURCE; + + } else { + throw websocketpp::uri_exception("Error parsing WebSocket URI"); + } + break; + case READ_RESOURCE: + // max length check? + + if (*it == '#') { + throw websocketpp::uri_exception("WebSocket URIs cannot have fragments"); + } else { + m_resource.append(1,*it); + } + break; + } + }*/ + + + boost::cmatch matches; + static const boost::regex expression("(ws|wss)://([^/:\\[]+|\\[[0-9a-fA-F:]+\\])(:\\d{1,5})?(/[^#]*)?"); // TODO: should this split resource into path/query? @@ -43,6 +169,10 @@ uri::uri(const std::string& uri) { m_secure = (matches[1] == "wss"); m_host = matches[2]; + if (m_host[0] == '[') { + m_host = m_host.substr(1,m_host.size()-2); + } + std::string port(matches[3]); if (port != "") { @@ -168,7 +298,7 @@ uint16_t uri::get_port_from_string(const std::string& port) const { } if (t_port == 0) { - throw websocketpp::uri_exception("Error parsing port string"); + throw websocketpp::uri_exception("Error parsing port string: "+port); } return t_port; diff --git a/test/basic/Makefile b/test/basic/Makefile index 8c0a64f2b4..42e410ab1d 100644 --- a/test/basic/Makefile +++ b/test/basic/Makefile @@ -1,5 +1,5 @@ -BOOST_LIB_PATH ?= /Users/zaphoyd/Documents/boost_1_48_0/stage/lib -BOOST_INCLUDE_PATH ?= /Users/zaphoyd/Documents/boost_1_48_0 +BOOST_LIB_PATH ?= /usr/local/lib +BOOST_INCLUDE_PATH ?= /usr/local/include CFLAGS = -O2 -I$(BOOST_INCLUDE_PATH) LDFLAGS = -L$(BOOST_LIB_PATH) diff --git a/websocketpp.xcodeproj/project.pbxproj b/websocketpp.xcodeproj/project.pbxproj index ab7147fa38..5b1369fdf1 100644 --- a/websocketpp.xcodeproj/project.pbxproj +++ b/websocketpp.xcodeproj/project.pbxproj @@ -230,6 +230,10 @@ B62A5A6F14774CD5005F9EB0 /* uri.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = uri.hpp; path = src/uri.hpp; sourceTree = ""; }; B62A5A7014774F08005F9EB0 /* md5.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = md5.hpp; path = src/md5/md5.hpp; sourceTree = ""; }; B62A5A71147759EA005F9EB0 /* common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = common.hpp; path = src/common.hpp; sourceTree = ""; }; + B64E12D214BDE132006F20F0 /* logging.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = logging.cpp; sourceTree = ""; }; + B64E12D314BDE132006F20F0 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + B64E12D414BDE132006F20F0 /* parsing.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = parsing.cpp; sourceTree = ""; }; + B64E12D514BDE132006F20F0 /* tests */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = tests; sourceTree = ""; }; B653A714148A607D004D7BD9 /* hybi_header.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = hybi_header.hpp; sourceTree = ""; }; B653A718148AB555004D7BD9 /* hybi_header.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hybi_header.cpp; sourceTree = ""; }; B66388451487D71800DDAE13 /* echo_server_tls.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = echo_server_tls.cpp; path = examples/echo_server_tls/echo_server_tls.cpp; sourceTree = ""; }; @@ -496,6 +500,25 @@ path = src/processors; sourceTree = ""; }; + B64E12D014BDE132006F20F0 /* test */ = { + isa = PBXGroup; + children = ( + B64E12D114BDE132006F20F0 /* basic */, + ); + path = test; + sourceTree = ""; + }; + B64E12D114BDE132006F20F0 /* basic */ = { + isa = PBXGroup; + children = ( + B64E12D214BDE132006F20F0 /* logging.cpp */, + B64E12D314BDE132006F20F0 /* Makefile */, + B64E12D414BDE132006F20F0 /* parsing.cpp */, + B64E12D514BDE132006F20F0 /* tests */, + ); + path = basic; + sourceTree = ""; + }; B66388431487D70000DDAE13 /* echo_server_tls */ = { isa = PBXGroup; children = ( @@ -623,6 +646,7 @@ B6DF1CC61435ED380029A1B1 /* examples */, B6DF1CC51435ECE40029A1B1 /* libraries */, B6138791145CA6F700ED9B19 /* Makefile */, + B64E12D014BDE132006F20F0 /* test */, B6DF1C7F1434ABB70029A1B1 /* src */, B6DF1C6A1434A7A30029A1B1 /* Products */, );