Created
November 27, 2020 10:09
-
-
Save madmongo1/cd62a5bebefb724ae45f0f60582c164e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
std::string | |
deduce_port(std::string const &scheme, std::string port) | |
{ | |
using boost::algorithm::iequals; | |
if (port.empty()) | |
{ | |
if (iequals(scheme, "ws") or iequals(scheme, "http")) | |
port = "http"; | |
else if (iequals(scheme, "wss") or iequals(scheme, "https")) | |
port = "https"; | |
else | |
throw system_error(net::error::invalid_argument, | |
"can't deduce port"); | |
} | |
return port; | |
} | |
transport_type | |
deduce_transport(std::string const &scheme, std::string const &port) | |
{ | |
using boost::algorithm::iequals; | |
if (scheme.empty()) | |
{ | |
if (port.empty()) | |
return transport_type::tcp; | |
if (iequals(port, "http") or iequals(port, "ws") or | |
iequals(port, "80")) | |
return transport_type::tcp; | |
if (iequals(port, "https") or iequals(port, "wss") or | |
iequals(port, "443")) | |
return transport_type::tls; | |
throw system_error(net::error::invalid_argument, | |
"cannot deduce transport"); | |
} | |
else | |
{ | |
if (iequals(scheme, "http") or iequals(scheme, "ws")) | |
return transport_type::tcp; | |
if (iequals(scheme, "https") or iequals(scheme, "wss")) | |
return transport_type::tls; | |
throw system_error(net::error::invalid_argument, | |
"invalid scheme"); | |
} | |
} | |
auto | |
set_sni(ssl_layer &stream, std::string const &host) -> void | |
{ | |
if (not SSL_set_tlsext_host_name(stream.native_handle(), | |
host.c_str())) | |
throw system_error( | |
error_code(static_cast< int >(::ERR_get_error()), | |
net::error::get_ssl_category())); | |
} | |
std::string | |
build_target(std::string const &path, | |
std::string const &query, | |
std::string const &fragment) | |
{ | |
std::string result; | |
if (path.empty()) | |
result = "/"; | |
else | |
result = path; | |
if (!query.empty()) | |
result += "?" + query; | |
if (!fragment.empty()) | |
result += "#" + fragment; | |
return result; | |
} | |
net::awaitable< net::ip::tcp::resolver::results_type > | |
resolve(std::string const &host, std::string const &port) | |
{ | |
spdlog::debug("resolving [host {}][port {}]", host, port); | |
auto resolver = | |
net::ip::tcp::resolver(co_await net::this_coro::executor); | |
co_return co_await resolver.async_resolve( | |
host, port, net::use_awaitable); | |
} | |
net::awaitable< void > | |
connect_tcp(beast::tcp_stream & stream, | |
net::ip::tcp::resolver::results_type results) | |
{ | |
spdlog::trace("connecting tcp"); | |
stream.expires_after(30s); | |
auto ep = | |
co_await stream.async_connect(results, net::use_awaitable); | |
boost::ignore_unused(ep); | |
spdlog::trace("connected on {}", ep); | |
} | |
net::awaitable< void > | |
connect_tls(beast::ssl_stream< beast::tcp_stream > &stream, | |
std::string const & host) | |
{ | |
spdlog::trace("tls handshake [host {}]", host); | |
set_sni(stream, host); | |
stream.next_layer().expires_after(30s); | |
co_await stream.async_handshake(net::ssl::stream_base::client, | |
net::use_awaitable); | |
} | |
auto | |
variant_websocket::connect(net::ssl::context & sslctx, | |
std::string url, | |
boost::beast::http::fields headers) | |
-> net::awaitable< void > | |
{ | |
assert(this->empty()); | |
static auto url_regex = std::regex( | |
R"regex((ws|wss|http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\x3f?([^ #]*)#?([^ ]*))regex", | |
std::regex_constants::icase); | |
auto match = std::smatch(); | |
if (not std::regex_match(url, match, url_regex)) | |
throw system_error(net::error::invalid_argument, "invalid url"); | |
auto &protocol = match[1]; | |
auto &host = match[2]; | |
auto &port_ind = match[3]; | |
auto &path = match[4]; | |
auto &query = match[5]; | |
auto &fragment = match[6]; | |
auto transport = deduce_transport(protocol, port_ind); | |
auto port = deduce_port(protocol, port_ind); | |
switch (transport) | |
{ | |
case transport_type::tcp: | |
emplace_tcp(co_await net::this_coro::executor); | |
break; | |
case transport_type::tls: | |
emplace_tls(co_await net::this_coro::executor, sslctx); | |
break; | |
} | |
auto &tcp_layer = get_tcp(); | |
// connect tcp | |
co_await connect_tcp(tcp_layer, co_await resolve(host.str(), port)); | |
// tls handshake | |
if (auto tls = query_tls()) | |
co_await connect_tls(*tls, host.str()); | |
// websocket handshake | |
spdlog::trace("handshaking"); | |
set_headers(headers); | |
beast::websocket::response_type response; | |
co_await client_handshake( | |
response, host.str(), build_target(path, query, fragment)); | |
tcp_layer.expires_never(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment