Skip to content

Commit 413f099

Browse files
committed
Merge pull request cpp-netlib#602 from theopolis/master
[cpp-netlib#600] Add TLS SNI hostname to client options
2 parents c4f0567 + 1e18406 commit 413f099

12 files changed

+122
-93
lines changed

boost/network/protocol/http/client/async_impl.hpp

+10-7
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ struct async_client
3232
typedef typename string<Tag>::type string_type;
3333

3434
typedef std::function<void(boost::iterator_range<char const*> const&,
35-
std::error_code const&)> body_callback_function_type;
35+
std::error_code const&)>
36+
body_callback_function_type;
3637

3738
typedef std::function<bool(string_type&)> body_generator_function_type;
3839

@@ -43,11 +44,11 @@ struct async_client
4344
optional<string_type> verify_path,
4445
optional<string_type> certificate_file,
4546
optional<string_type> private_key_file,
46-
optional<string_type> ciphers, long ssl_options)
47+
optional<string_type> ciphers,
48+
optional<string_type> sni_hostname, long ssl_options)
4749
: connection_base(cache_resolved, follow_redirect, timeout),
48-
service_ptr(service.get()
49-
? service
50-
: std::make_shared<asio::io_service>()),
50+
service_ptr(service.get() ? service
51+
: std::make_shared<asio::io_service>()),
5152
service_(*service_ptr),
5253
resolver_(service_),
5354
sentinel_(new asio::io_service::work(service_)),
@@ -56,12 +57,13 @@ struct async_client
5657
certificate_file_(std::move(certificate_file)),
5758
private_key_file_(std::move(private_key_file)),
5859
ciphers_(std::move(ciphers)),
60+
sni_hostname_(std::move(sni_hostname)),
5961
ssl_options_(ssl_options),
6062
always_verify_peer_(always_verify_peer) {
6163
connection_base::resolver_strand_.reset(
6264
new asio::io_service::strand(service_));
6365
if (!service)
64-
lifetime_thread_.reset(new std::thread([this] () { service_.run(); }));
66+
lifetime_thread_.reset(new std::thread([this]() { service_.run(); }));
6567
}
6668

6769
~async_client() throw() = default;
@@ -82,7 +84,7 @@ struct async_client
8284
connection_ = connection_base::get_connection(
8385
resolver_, request_, always_verify_peer_, certificate_filename_,
8486
verify_path_, certificate_file_, private_key_file_, ciphers_,
85-
ssl_options_);
87+
sni_hostname_, ssl_options_);
8688
return connection_->send_request(method, request_, get_body, callback,
8789
generator);
8890
}
@@ -97,6 +99,7 @@ struct async_client
9799
optional<string_type> certificate_file_;
98100
optional<string_type> private_key_file_;
99101
optional<string_type> ciphers_;
102+
optional<string_type> sni_hostname_;
100103
long ssl_options_;
101104
bool always_verify_peer_;
102105
};

boost/network/protocol/http/client/connection/async_base.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,15 @@ struct async_connection_base {
4646
optional<string_type> certificate_file = optional<string_type>(),
4747
optional<string_type> private_key_file = optional<string_type>(),
4848
optional<string_type> ciphers = optional<string_type>(),
49+
optional<string_type> sni_hostname = optional<string_type>(),
4950
long ssl_options = 0) {
5051
typedef http_async_connection<Tag, version_major, version_minor>
5152
async_connection;
5253
typedef typename delegate_factory<Tag>::type delegate_factory_type;
5354
auto delegate = delegate_factory_type::new_connection_delegate(
5455
resolver.get_io_service(), https, always_verify_peer,
5556
certificate_filename, verify_path, certificate_file, private_key_file,
56-
ciphers, ssl_options);
57+
ciphers, sni_hostname, ssl_options);
5758
auto temp = std::make_shared<async_connection>(
5859
resolver, resolve, follow_redirect, timeout, std::move(delegate));
5960
BOOST_ASSERT(temp != nullptr);

boost/network/protocol/http/client/connection/connection_delegate_factory.hpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,14 @@ struct connection_delegate_factory {
3838
optional<string_type> certificate_filename,
3939
optional<string_type> verify_path, optional<string_type> certificate_file,
4040
optional<string_type> private_key_file, optional<string_type> ciphers,
41-
long ssl_options) {
41+
optional<string_type> sni_hostname, long ssl_options) {
4242
connection_delegate_ptr delegate;
4343
if (https) {
4444
#ifdef BOOST_NETWORK_ENABLE_HTTPS
4545
delegate = std::make_shared<ssl_delegate>(
4646
service, always_verify_peer, certificate_filename, verify_path,
47-
certificate_file, private_key_file, ciphers, ssl_options);
47+
certificate_file, private_key_file, ciphers, sni_hostname,
48+
ssl_options);
4849
#else
4950
BOOST_THROW_EXCEPTION(std::runtime_error("HTTPS not supported."));
5051
#endif /* BOOST_NETWORK_ENABLE_HTTPS */

boost/network/protocol/http/client/connection/ssl_delegate.hpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ struct ssl_delegate : public connection_delegate,
2929
optional<std::string> verify_path,
3030
optional<std::string> certificate_file,
3131
optional<std::string> private_key_file,
32-
optional<std::string> ciphers, long ssl_options);
32+
optional<std::string> ciphers,
33+
optional<std::string> sni_hostname, long ssl_options);
3334

3435
void connect(asio::ip::tcp::endpoint &endpoint, std::string host,
3536
std::uint16_t source_port,
@@ -50,6 +51,7 @@ struct ssl_delegate : public connection_delegate,
5051
optional<std::string> certificate_file_;
5152
optional<std::string> private_key_file_;
5253
optional<std::string> ciphers_;
54+
optional<std::string> sni_hostname_;
5355
long ssl_options_;
5456
std::unique_ptr<asio::ssl::context> context_;
5557
std::unique_ptr<asio::ip::tcp::socket> tcp_socket_;

boost/network/protocol/http/client/connection/ssl_delegate.ipp

+9-10
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ boost::network::http::impl::ssl_delegate::ssl_delegate(
1717
optional<std::string> certificate_filename,
1818
optional<std::string> verify_path, optional<std::string> certificate_file,
1919
optional<std::string> private_key_file, optional<std::string> ciphers,
20-
long ssl_options)
20+
optional<std::string> sni_hostname, long ssl_options)
2121
: service_(service),
2222
certificate_filename_(std::move(certificate_filename)),
2323
verify_path_(std::move(verify_path)),
2424
certificate_file_(std::move(certificate_file)),
2525
private_key_file_(std::move(private_key_file)),
2626
ciphers_(std::move(ciphers)),
27+
sni_hostname_(std::move(sni_hostname)),
2728
ssl_options_(ssl_options),
2829
always_verify_peer_(always_verify_peer) {}
2930

@@ -58,25 +59,23 @@ void boost::network::http::impl::ssl_delegate::connect(
5859
}
5960
}
6061
if (certificate_file_)
61-
context_->use_certificate_file(*certificate_file_,
62-
asio::ssl::context::pem);
62+
context_->use_certificate_file(*certificate_file_, asio::ssl::context::pem);
6363
if (private_key_file_)
64-
context_->use_private_key_file(*private_key_file_,
65-
asio::ssl::context::pem);
64+
context_->use_private_key_file(*private_key_file_, asio::ssl::context::pem);
6665

6766
tcp_socket_.reset(new asio::ip::tcp::socket(
6867
service_, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), source_port)));
6968
socket_.reset(new asio::ssl::stream<asio::ip::tcp::socket &>(
7069
*(tcp_socket_.get()), *context_));
7170

71+
if (sni_hostname_)
72+
SSL_set_tlsext_host_name(socket_->native_handle(), sni_hostname_->c_str());
7273
if (always_verify_peer_)
7374
socket_->set_verify_callback(asio::ssl::rfc2818_verification(host));
7475
auto self = this->shared_from_this();
7576
socket_->lowest_layer().async_connect(
7677
endpoint,
77-
[=] (std::error_code const &ec) {
78-
self->handle_connected(ec, handler);
79-
});
78+
[=](std::error_code const &ec) { self->handle_connected(ec, handler); });
8079
}
8180

8281
void boost::network::http::impl::ssl_delegate::handle_connected(
@@ -104,8 +103,8 @@ void boost::network::http::impl::ssl_delegate::read_some(
104103
void boost::network::http::impl::ssl_delegate::disconnect() {
105104
if (socket_.get() && socket_->lowest_layer().is_open()) {
106105
std::error_code ignored;
107-
socket_->lowest_layer().shutdown(
108-
asio::ip::tcp::socket::shutdown_both, ignored);
106+
socket_->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both,
107+
ignored);
109108
if (!ignored) {
110109
socket_->lowest_layer().close(ignored);
111110
}

boost/network/protocol/http/client/connection/sync_base.hpp

+16-16
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ struct sync_connection_base_impl {
3939

4040
template <class Socket>
4141
void init_socket(Socket& socket_, resolver_type& resolver_,
42-
string_type /*unused*/const& hostname, string_type const& port,
43-
resolver_function_type resolve_) {
42+
string_type /*unused*/ const& hostname,
43+
string_type const& port, resolver_function_type resolve_) {
4444
using asio::ip::tcp;
4545
std::error_code error = asio::error::host_not_found;
4646
typename resolver_type::iterator endpoint_iterator, end;
@@ -100,7 +100,7 @@ struct sync_connection_base_impl {
100100
}
101101

102102
template <class Socket>
103-
void send_request_impl(Socket& socket_, string_type /*unused*/const& method,
103+
void send_request_impl(Socket& socket_, string_type /*unused*/ const& method,
104104
asio::streambuf& request_buffer) {
105105
// TODO(dberris): review parameter necessity.
106106
(void)method;
@@ -118,8 +118,8 @@ struct sync_connection_base_impl {
118118
std::error_code error;
119119
if (response_buffer.size() > 0) body_stream << &response_buffer;
120120

121-
while (asio::read(socket_, response_buffer,
122-
asio::transfer_at_least(1), error)) {
121+
while (asio::read(socket_, response_buffer, asio::transfer_at_least(1),
122+
error)) {
123123
body_stream << &response_buffer;
124124
}
125125
}
@@ -171,8 +171,7 @@ struct sync_connection_base_impl {
171171
read(socket_, response_buffer,
172172
asio::transfer_at_least(bytes_to_read), error);
173173
if (chunk_bytes_read == 0) {
174-
if (error != asio::error::eof)
175-
throw std::system_error(error);
174+
if (error != asio::error::eof) throw std::system_error(error);
176175
stopping_inner = true;
177176
}
178177
}
@@ -195,14 +194,14 @@ struct sync_connection_base_impl {
195194
} else {
196195
size_t already_read = response_buffer.size();
197196
if (already_read) body_stream << &response_buffer;
198-
size_t length = std::stoul(std::begin(content_length_range)->second) -
199-
already_read;
200-
if (length == 0) { return;
201-
}
197+
size_t length =
198+
std::stoul(std::begin(content_length_range)->second) - already_read;
199+
if (length == 0) {
200+
return;
201+
}
202202
size_t bytes_read = 0;
203203
while ((bytes_read = asio::read(socket_, response_buffer,
204-
asio::transfer_at_least(1),
205-
error))) {
204+
asio::transfer_at_least(1), error))) {
206205
body_stream << &response_buffer;
207206
length -= bytes_read;
208207
if ((length <= 0) || error) break;
@@ -223,7 +222,7 @@ struct sync_connection_base_impl {
223222
} else {
224223
read_body_transfer_chunk_encoding(socket_, response_, response_buffer,
225224
body_stream);
226-
}
225+
}
227226
} else {
228227
throw std::runtime_error("Unsupported HTTP version number.");
229228
}
@@ -249,14 +248,15 @@ struct sync_connection_base {
249248
new_connection(
250249
resolver_type& resolver, resolver_function_type resolve, bool https,
251250
bool always_verify_peer, int timeout,
252-
optional<string_type> /*unused*/const& certificate_filename =
251+
optional<string_type> /*unused*/ const& certificate_filename =
253252
optional<string_type>(),
254253
optional<string_type> const& verify_path = optional<string_type>(),
255254
optional<string_type> const& certificate_file =
256255
optional<string_type>(),
257256
optional<string_type> const& private_key_file =
258257
optional<string_type>(),
259258
optional<string_type> const& ciphers = optional<string_type>(),
259+
optional<string_type> const& sni_hostname = optional<string_type>(),
260260
long ssl_options = 0) {
261261
if (https) {
262262
#ifdef BOOST_NETWORK_ENABLE_HTTPS
@@ -265,7 +265,7 @@ struct sync_connection_base {
265265
new https_sync_connection<Tag, version_major, version_minor>(
266266
resolver, resolve, always_verify_peer, timeout,
267267
certificate_filename, verify_path, certificate_file,
268-
private_key_file, ciphers, ssl_options));
268+
private_key_file, ciphers, sni_hostname, ssl_options));
269269
#else
270270
throw std::runtime_error("HTTPS not supported.");
271271
#endif

boost/network/protocol/http/client/connection/sync_ssl.hpp

+20-16
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,20 @@ struct https_sync_connection
5050
https_sync_connection(
5151
resolver_type& resolver, resolver_function_type resolve,
5252
bool always_verify_peer, int timeout,
53-
optional<string_type> /*unused*/const& certificate_filename =
53+
optional<string_type> /*unused*/ const& certificate_filename =
5454
optional<string_type>(),
5555
optional<string_type> const& verify_path = optional<string_type>(),
5656
optional<string_type> const& certificate_file = optional<string_type>(),
5757
optional<string_type> const& private_key_file = optional<string_type>(),
5858
optional<string_type> const& ciphers = optional<string_type>(),
59+
optional<string_type> const& sni_hostname = optional<string_type>(),
5960
long ssl_options = 0)
6061
: connection_base(),
6162
timeout_(timeout),
6263
timer_(resolver.get_io_service()),
6364
resolver_(resolver),
6465
resolve_(std::move(resolve)),
65-
context_(resolver.get_io_service(),
66-
asio::ssl::context::sslv23_client),
66+
context_(resolver.get_io_service(), asio::ssl::context::sslv23_client),
6767
socket_(resolver.get_io_service(), context_) {
6868
if (ciphers) {
6969
::SSL_CTX_set_cipher_list(context_.native_handle(), ciphers->c_str());
@@ -86,20 +86,21 @@ struct https_sync_connection
8686
context_.set_verify_mode(asio::ssl::context_base::verify_none);
8787
}
8888
if (certificate_file)
89-
context_.use_certificate_file(*certificate_file,
90-
asio::ssl::context::pem);
89+
context_.use_certificate_file(*certificate_file, asio::ssl::context::pem);
9190
if (private_key_file)
92-
context_.use_private_key_file(*private_key_file,
93-
asio::ssl::context::pem);
91+
context_.use_private_key_file(*private_key_file, asio::ssl::context::pem);
92+
if (sni_hostname)
93+
SSL_set_tlsext_host_name(socket_.native_handle(), sni_hostname->c_str());
9494
}
9595

96-
void init_socket(string_type /*unused*/const& hostname, string_type const& port) {
96+
void init_socket(string_type /*unused*/ const& hostname,
97+
string_type const& port) {
9798
connection_base::init_socket(socket_.lowest_layer(), resolver_, hostname,
9899
port, resolve_);
99100
socket_.handshake(asio::ssl::stream_base::client);
100101
}
101102

102-
void send_request_impl(string_type /*unused*/const& method,
103+
void send_request_impl(string_type /*unused*/ const& method,
103104
basic_request<Tag> const& request_,
104105
body_generator_function_type generator) {
105106
asio::streambuf request_buffer;
@@ -120,9 +121,8 @@ struct https_sync_connection
120121
if (timeout_ > 0) {
121122
timer_.expires_from_now(boost::posix_time::seconds(timeout_));
122123
auto self = this->shared_from_this();
123-
timer_.async_wait([=] (std::error_code const &ec) {
124-
self->handle_timeout(ec);
125-
});
124+
timer_.async_wait(
125+
[=](std::error_code const& ec) { self->handle_timeout(ec); });
126126
}
127127
}
128128

@@ -141,7 +141,8 @@ struct https_sync_connection
141141
connection_base::read_body(socket_, response_, response_buffer);
142142
typename headers_range<basic_response<Tag> >::type connection_range =
143143
headers(response_)["Connection"];
144-
if (version_major == 1 && version_minor == 1 && !boost::empty(connection_range) &&
144+
if (version_major == 1 && version_minor == 1 &&
145+
!boost::empty(connection_range) &&
145146
boost::iequals(std::begin(connection_range)->second, "close")) {
146147
close_socket();
147148
} else if (version_major == 1 && version_minor == 0) {
@@ -156,16 +157,19 @@ struct https_sync_connection
156157
std::error_code ignored;
157158
socket_.lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both,
158159
ignored);
159-
if (ignored) { return; }
160+
if (ignored) {
161+
return;
162+
}
160163
socket_.lowest_layer().close(ignored);
161164
}
162165

163166
~https_sync_connection() { close_socket(); }
164167

165168
private:
166169
void handle_timeout(std::error_code const& ec) {
167-
if (!ec) { close_socket();
168-
}
170+
if (!ec) {
171+
close_socket();
172+
}
169173
}
170174

171175
int timeout_;

0 commit comments

Comments
 (0)