From e5e613d948e458ff2b3de9e284b1b92fe5be634e Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 19 Oct 2017 14:32:35 +0200 Subject: [PATCH 1/3] Ability to set a sni_hostname at the request level --- .../http/client/connection/async_normal.hpp | 16 +++---- .../client/connection/connection_delegate.hpp | 3 +- .../client/connection/normal_delegate.hpp | 2 +- .../client/connection/normal_delegate.ipp | 2 +- .../http/client/connection/ssl_delegate.hpp | 2 +- .../http/client/connection/ssl_delegate.ipp | 10 ++++- boost/network/protocol/http/impl/request.hpp | 24 +++++++---- libs/network/test/http/client_get_test.cpp | 42 +++++++++++++++++++ 8 files changed, 80 insertions(+), 21 deletions(-) diff --git a/boost/network/protocol/http/client/connection/async_normal.hpp b/boost/network/protocol/http/client/connection/async_normal.hpp index 19c15590f..5cfcbb561 100644 --- a/boost/network/protocol/http/client/connection/async_normal.hpp +++ b/boost/network/protocol/http/client/connection/async_normal.hpp @@ -101,12 +101,14 @@ struct http_async_connection string_type host_ = host(request); std::uint16_t source_port = request.source_port(); + auto sni_hostname = request.sni_hostname(); + auto self = this->shared_from_this(); resolve_(resolver_, host_, port_, request_strand_.wrap( [=] (boost::system::error_code const &ec, resolver_iterator_pair endpoint_range) { - self->handle_resolved(host_, port_, source_port, get_body, + self->handle_resolved(host_, port_, source_port, sni_hostname, get_body, callback, generator, ec, endpoint_range); })); if (timeout_ > 0) { @@ -139,7 +141,7 @@ struct http_async_connection } void handle_resolved(string_type host, std::uint16_t port, - std::uint16_t source_port, bool get_body, + std::uint16_t source_port, optional sni_hostname, bool get_body, body_callback_function_type callback, body_generator_function_type generator, boost::system::error_code const& ec, @@ -151,10 +153,10 @@ struct http_async_connection boost::asio::ip::tcp::endpoint endpoint(iter->endpoint().address(), port); auto self = this->shared_from_this(); delegate_->connect( - endpoint, host, source_port, + endpoint, host, source_port, sni_hostname, request_strand_.wrap([=] (boost::system::error_code const &ec) { auto iter_copy = iter; - self->handle_connected(host, port, source_port, get_body, callback, + self->handle_connected(host, port, source_port, sni_hostname, get_body, callback, generator, std::make_pair(++iter_copy, resolver_iterator()), ec); })); } else { @@ -163,7 +165,7 @@ struct http_async_connection } void handle_connected(string_type host, std::uint16_t port, - std::uint16_t source_port, bool get_body, + std::uint16_t source_port, optional sni_hostname, bool get_body, body_callback_function_type callback, body_generator_function_type generator, resolver_iterator_pair endpoint_range, @@ -186,10 +188,10 @@ struct http_async_connection boost::asio::ip::tcp::endpoint endpoint(iter->endpoint().address(), port); auto self = this->shared_from_this(); delegate_->connect( - endpoint, host, source_port, + endpoint, host, source_port, sni_hostname, request_strand_.wrap([=] (boost::system::error_code const &ec) { auto iter_copy = iter; - self->handle_connected(host, port, source_port, get_body, callback, + self->handle_connected(host, port, source_port, sni_hostname, get_body, callback, generator, std::make_pair(++iter_copy, resolver_iterator()), ec); })); diff --git a/boost/network/protocol/http/client/connection/connection_delegate.hpp b/boost/network/protocol/http/client/connection/connection_delegate.hpp index 4e223864e..6bbf4d32c 100644 --- a/boost/network/protocol/http/client/connection/connection_delegate.hpp +++ b/boost/network/protocol/http/client/connection/connection_delegate.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace boost { namespace network { @@ -19,7 +20,7 @@ namespace impl { struct connection_delegate { virtual void connect(boost::asio::ip::tcp::endpoint &endpoint, std::string host, - std::uint16_t source_port, + std::uint16_t source_port, optional sni_hostname, std::function handler) = 0; virtual void write( boost::asio::streambuf &command_streambuf, diff --git a/boost/network/protocol/http/client/connection/normal_delegate.hpp b/boost/network/protocol/http/client/connection/normal_delegate.hpp index a4187f7fc..24955cd05 100644 --- a/boost/network/protocol/http/client/connection/normal_delegate.hpp +++ b/boost/network/protocol/http/client/connection/normal_delegate.hpp @@ -25,7 +25,7 @@ struct normal_delegate : connection_delegate { explicit normal_delegate(boost::asio::io_service &service); void connect(boost::asio::ip::tcp::endpoint &endpoint, std::string host, - std::uint16_t source_port, + std::uint16_t source_port, optional sni_hostname, std::function handler) override; void write(boost::asio::streambuf &command_streambuf, std::function handler) diff --git a/boost/network/protocol/http/client/connection/normal_delegate.ipp b/boost/network/protocol/http/client/connection/normal_delegate.ipp index 552f50417..027f04a8c 100644 --- a/boost/network/protocol/http/client/connection/normal_delegate.ipp +++ b/boost/network/protocol/http/client/connection/normal_delegate.ipp @@ -21,7 +21,7 @@ boost::network::http::impl::normal_delegate::normal_delegate( void boost::network::http::impl::normal_delegate::connect( boost::asio::ip::tcp::endpoint &endpoint, std::string host, - std::uint16_t source_port, + std::uint16_t source_port, optional sni_hostname, std::function handler) { // TODO(dberris): review parameter necessity. diff --git a/boost/network/protocol/http/client/connection/ssl_delegate.hpp b/boost/network/protocol/http/client/connection/ssl_delegate.hpp index 63adc1818..6ad298dcb 100644 --- a/boost/network/protocol/http/client/connection/ssl_delegate.hpp +++ b/boost/network/protocol/http/client/connection/ssl_delegate.hpp @@ -33,7 +33,7 @@ struct ssl_delegate : public connection_delegate, optional sni_hostname, long ssl_options); void connect(boost::asio::ip::tcp::endpoint &endpoint, std::string host, - std::uint16_t source_port, + std::uint16_t source_port, optional sni_hostname, std::function handler) override; void write( boost::asio::streambuf &command_streambuf, diff --git a/boost/network/protocol/http/client/connection/ssl_delegate.ipp b/boost/network/protocol/http/client/connection/ssl_delegate.ipp index e94a43994..7575b12b4 100644 --- a/boost/network/protocol/http/client/connection/ssl_delegate.ipp +++ b/boost/network/protocol/http/client/connection/ssl_delegate.ipp @@ -30,8 +30,9 @@ boost::network::http::impl::ssl_delegate::ssl_delegate( void boost::network::http::impl::ssl_delegate::connect( boost::asio::ip::tcp::endpoint &endpoint, std::string host, - std::uint16_t source_port, + std::uint16_t source_port, optional sni_hostname, std::function handler) { + context_.reset( new boost::asio::ssl::context(boost::asio::ssl::context::method::sslv23_client)); if (ciphers_) { @@ -71,8 +72,13 @@ void boost::network::http::impl::ssl_delegate::connect( socket_.reset(new boost::asio::ssl::stream( *(tcp_socket_.get()), *context_)); - if (sni_hostname_) + if (sni_hostname) { // at request level + SSL_set_tlsext_host_name(socket_->native_handle(), sni_hostname->c_str()); + } else if (sni_hostname_) { // at client level SSL_set_tlsext_host_name(socket_->native_handle(), sni_hostname_->c_str()); + } + + if (always_verify_peer_) socket_->set_verify_callback(boost::asio::ssl::rfc2818_verification(host)); auto self = this->shared_from_this(); diff --git a/boost/network/protocol/http/impl/request.hpp b/boost/network/protocol/http/impl/request.hpp index 85ab0f3ae..04d0b8562 100644 --- a/boost/network/protocol/http/impl/request.hpp +++ b/boost/network/protocol/http/impl/request.hpp @@ -50,29 +50,32 @@ namespace http { */ template struct basic_request : public basic_message { - mutable boost::network::uri::uri uri_; - std::uint16_t source_port_; - typedef basic_message base_type; - public: typedef Tag tag; typedef typename string::type string_type; typedef std::uint16_t port_type; + private: + mutable boost::network::uri::uri uri_; + std::uint16_t source_port_; + optional sni_hostname_; + typedef basic_message base_type; + + public: explicit basic_request(string_type const& uri_) - : uri_(uri_), source_port_(0) {} + : uri_(uri_), source_port_(0), sni_hostname_() {} explicit basic_request(boost::network::uri::uri const& uri_) - : uri_(uri_), source_port_(0) {} + : uri_(uri_), source_port_(0), sni_hostname_() {} void uri(string_type const& new_uri) { uri_ = new_uri; } void uri(boost::network::uri::uri const& new_uri) { uri_ = new_uri; } - basic_request() : base_type(), source_port_(0) {} + basic_request() : base_type(), source_port_(0), sni_hostname_() {} basic_request(basic_request const& other) - : base_type(other), uri_(other.uri_), source_port_(other.source_port_) {} + : base_type(other), uri_(other.uri_), source_port_(other.source_port_), sni_hostname_(other.sni_hostname_) {} basic_request& operator=(basic_request rhs) { rhs.swap(*this); @@ -85,6 +88,7 @@ struct basic_request : public basic_message { base_ref.swap(this_ref); boost::swap(other.uri_, this->uri_); boost::swap(other.source_port_, this->source_port_); + boost::swap(other.sni_hostname_, this->sni_hostname_); } string_type const host() const { return uri_.host(); } @@ -114,6 +118,10 @@ struct basic_request : public basic_message { void source_port(const std::uint16_t port) { source_port_ = port; } std::uint16_t source_port() const { return source_port_; } + + void sni_hostname(string_type const &sni_hostname) { sni_hostname_ = sni_hostname; } + + const optional &sni_hostname() const { return sni_hostname_; } }; /** This is the implementation of a POD request type diff --git a/libs/network/test/http/client_get_test.cpp b/libs/network/test/http/client_get_test.cpp index b5eb5b77b..4197b0efd 100644 --- a/libs/network/test/http/client_get_test.cpp +++ b/libs/network/test/http/client_get_test.cpp @@ -65,3 +65,45 @@ TYPED_TEST(HTTPClientTest, TemporaryClientObjectTest) { EXPECT_TRUE(response.status() == 200u || (response.status() >= 300 && response.status() < 400)); } + + +typedef boost::network::http::basic_client async_client; +#define EXC_PTR(cmd) try { cmd } catch (const std::exception_ptr& ex) { std::rethrow_exception(ex); } + +TYPED_TEST(HTTPClientTest, ReuseResponse) { + + async_client client; + + async_client::request req("https://static.deepomatic.com/compass/NVR_ch2_J_20161209122514_20161209122514_orig.jpg"); + + req.sni_hostname(req.host()); + + auto response = client.get(req); + + while (!ready(response)); + + EXC_PTR(response.status();); + + // we get the expected headers and body + auto hdrs = headers(response); + + for (auto &h : hdrs) { + std::cout << h.first << ": " << h.second << std::endl; + } + + std::cout << "---" << std::endl; + + req.uri("http://example.com"); // changing the url + + response = client.get(req); + + while (!ready(response)); + + // here the headers are the same as the previous response, they should be those from example.com + auto hdrs2 = headers(response); + + for (auto &h : hdrs2) { + std::cout << h.first << ": " << h.second << std::endl; + } + +} From 336fa627e0cccd4eb9afcb4e4639359e0b179ca4 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 19 Oct 2017 14:53:11 +0200 Subject: [PATCH 2/3] Better test --- libs/network/test/http/client_get_test.cpp | 49 ++++++++-------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/libs/network/test/http/client_get_test.cpp b/libs/network/test/http/client_get_test.cpp index 4197b0efd..57b6bd9b5 100644 --- a/libs/network/test/http/client_get_test.cpp +++ b/libs/network/test/http/client_get_test.cpp @@ -70,40 +70,27 @@ TYPED_TEST(HTTPClientTest, TemporaryClientObjectTest) { typedef boost::network::http::basic_client async_client; #define EXC_PTR(cmd) try { cmd } catch (const std::exception_ptr& ex) { std::rethrow_exception(ex); } -TYPED_TEST(HTTPClientTest, ReuseResponse) { - - async_client client; - - async_client::request req("https://static.deepomatic.com/compass/NVR_ch2_J_20161209122514_20161209122514_orig.jpg"); - - req.sni_hostname(req.host()); - - auto response = client.get(req); - - while (!ready(response)); - - EXC_PTR(response.status();); - - // we get the expected headers and body - auto hdrs = headers(response); - - for (auto &h : hdrs) { - std::cout << h.first << ": " << h.second << std::endl; - } - - std::cout << "---" << std::endl; - - req.uri("http://example.com"); // changing the url - - response = client.get(req); +TYPED_TEST(HTTPClientTest, GetRequestSNI) { + using client = TypeParam; + // need sni_hostname to be set + typename client::request request("https://www.guide-du-chien.com/wp-content/uploads/2016/10/Beagle.jpg"); + typename client::response response; - while (!ready(response)); + // trying without setting sni_hostname + ASSERT_NO_THROW(response = client().get(request)); + // raise "tlsv1 alert internal error" + ASSERT_THROW(response.status(), std::exception_ptr); - // here the headers are the same as the previous response, they should be those from example.com - auto hdrs2 = headers(response); + // setting sni_hostname + request.sni_hostname(request.host()); + ASSERT_NO_THROW(response = client().get(request)); + EXPECT_EQ(200u, response.status()); - for (auto &h : hdrs2) { - std::cout << h.first << ": " << h.second << std::endl; + try { + auto data = body(response); + std::cout << "Body size: " << data.size() << std::endl; + } catch (...) { + FAIL() << "Caught exception while retrieving body from GET request"; } } From 70ea80eaa92df1c772e510a03f923bf63e84f324 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 19 Oct 2017 14:54:37 +0200 Subject: [PATCH 3/3] Test only if https enabled --- libs/network/test/http/client_get_test.cpp | 44 ++++++++++------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/libs/network/test/http/client_get_test.cpp b/libs/network/test/http/client_get_test.cpp index 57b6bd9b5..f456f93a9 100644 --- a/libs/network/test/http/client_get_test.cpp +++ b/libs/network/test/http/client_get_test.cpp @@ -46,30 +46,6 @@ TYPED_TEST(HTTPClientTest, GetHTTPSTest) { } } -#endif - -TYPED_TEST(HTTPClientTest, TemporaryClientObjectTest) { - using client = TypeParam; - typename client::request request("http://cpp-netlib.org/"); - typename client::response response; - ASSERT_NO_THROW(response = client().get(request)); - auto range = headers(response); - ASSERT_TRUE(!boost::empty(range)); - try { - auto data = body(response); - std::cout << data; - } catch (...) { - FAIL() << "Caught exception while retrieving body from GET request"; - } - EXPECT_EQ("HTTP/1.", response.version().substr(0, 7)); - EXPECT_TRUE(response.status() == 200u || - (response.status() >= 300 && response.status() < 400)); -} - - -typedef boost::network::http::basic_client async_client; -#define EXC_PTR(cmd) try { cmd } catch (const std::exception_ptr& ex) { std::rethrow_exception(ex); } - TYPED_TEST(HTTPClientTest, GetRequestSNI) { using client = TypeParam; // need sni_hostname to be set @@ -94,3 +70,23 @@ TYPED_TEST(HTTPClientTest, GetRequestSNI) { } } + +#endif + +TYPED_TEST(HTTPClientTest, TemporaryClientObjectTest) { + using client = TypeParam; + typename client::request request("http://cpp-netlib.org/"); + typename client::response response; + ASSERT_NO_THROW(response = client().get(request)); + auto range = headers(response); + ASSERT_TRUE(!boost::empty(range)); + try { + auto data = body(response); + std::cout << data; + } catch (...) { + FAIL() << "Caught exception while retrieving body from GET request"; + } + EXPECT_EQ("HTTP/1.", response.version().substr(0, 7)); + EXPECT_TRUE(response.status() == 200u || + (response.status() >= 300 && response.status() < 400)); +}