2 // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 // Official repository: https://github.com/boostorg/beast
10 //------------------------------------------------------------------------------
12 // Example: HTTP SSL client, asynchronous
14 //------------------------------------------------------------------------------
16 #include "example/common/root_certificates.hpp"
18 #include <boost/beast/core.hpp>
19 #include <boost/beast/http.hpp>
20 #include <boost/beast/version.hpp>
21 #include <boost/asio/connect.hpp>
22 #include <boost/asio/ip/tcp.hpp>
23 #include <boost/asio/ssl/error.hpp>
24 #include <boost/asio/ssl/stream.hpp>
31 using tcp
= boost::asio::ip::tcp
; // from <boost/asio/ip/tcp.hpp>
32 namespace ssl
= boost::asio::ssl
; // from <boost/asio/ssl.hpp>
33 namespace http
= boost::beast::http
; // from <boost/beast/http.hpp>
35 //------------------------------------------------------------------------------
39 fail(boost::system::error_code ec
, char const* what
)
41 std::cerr
<< what
<< ": " << ec
.message() << "\n";
44 // Performs an HTTP GET and prints the response
45 class session
: public std::enable_shared_from_this
<session
>
47 tcp::resolver resolver_
;
48 ssl::stream
<tcp::socket
> stream_
;
49 boost::beast::flat_buffer buffer_
; // (Must persist between reads)
50 http::request
<http::empty_body
> req_
;
51 http::response
<http::string_body
> res_
;
54 // Resolver and stream require an io_context
56 session(boost::asio::io_context
& ioc
, ssl::context
& ctx
)
62 // Start the asynchronous operation
70 // Set SNI Hostname (many hosts need this to handshake successfully)
71 if(! SSL_set_tlsext_host_name(stream_
.native_handle(), host
))
73 boost::system::error_code ec
{static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()};
74 std::cerr
<< ec
.message() << "\n";
78 // Set up an HTTP GET request message
79 req_
.version(version
);
80 req_
.method(http::verb::get
);
82 req_
.set(http::field::host
, host
);
83 req_
.set(http::field::user_agent
, BOOST_BEAST_VERSION_STRING
);
85 // Look up the domain name
86 resolver_
.async_resolve(
92 std::placeholders::_1
,
93 std::placeholders::_2
));
98 boost::system::error_code ec
,
99 tcp::resolver::results_type results
)
102 return fail(ec
, "resolve");
104 // Make the connection on the IP address we get from a lookup
105 boost::asio::async_connect(
106 stream_
.next_layer(),
110 &session::on_connect
,
112 std::placeholders::_1
));
116 on_connect(boost::system::error_code ec
)
119 return fail(ec
, "connect");
121 // Perform the SSL handshake
122 stream_
.async_handshake(
123 ssl::stream_base::client
,
125 &session::on_handshake
,
127 std::placeholders::_1
));
131 on_handshake(boost::system::error_code ec
)
134 return fail(ec
, "handshake");
136 // Send the HTTP request to the remote host
137 http::async_write(stream_
, req_
,
141 std::placeholders::_1
,
142 std::placeholders::_2
));
147 boost::system::error_code ec
,
148 std::size_t bytes_transferred
)
150 boost::ignore_unused(bytes_transferred
);
153 return fail(ec
, "write");
155 // Receive the HTTP response
156 http::async_read(stream_
, buffer_
, res_
,
160 std::placeholders::_1
,
161 std::placeholders::_2
));
166 boost::system::error_code ec
,
167 std::size_t bytes_transferred
)
169 boost::ignore_unused(bytes_transferred
);
172 return fail(ec
, "read");
174 // Write the message to standard out
175 std::cout
<< res_
<< std::endl
;
177 // Gracefully close the stream
178 stream_
.async_shutdown(
180 &session::on_shutdown
,
182 std::placeholders::_1
));
186 on_shutdown(boost::system::error_code ec
)
188 if(ec
== boost::asio::error::eof
)
191 // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
192 ec
.assign(0, ec
.category());
195 return fail(ec
, "shutdown");
197 // If we get here then the connection is closed gracefully
201 //------------------------------------------------------------------------------
203 int main(int argc
, char** argv
)
205 // Check command line arguments.
206 if(argc
!= 4 && argc
!= 5)
209 "Usage: http-client-async-ssl <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
211 " http-client-async-ssl www.example.com 443 /\n" <<
212 " http-client-async-ssl www.example.com 443 / 1.0\n";
215 auto const host
= argv
[1];
216 auto const port
= argv
[2];
217 auto const target
= argv
[3];
218 int version
= argc
== 5 && !std::strcmp("1.0", argv
[4]) ? 10 : 11;
220 // The io_context is required for all I/O
221 boost::asio::io_context ioc
;
223 // The SSL context is required, and holds certificates
224 ssl::context ctx
{ssl::context::sslv23_client
};
226 // This holds the root certificate used for verification
227 load_root_certificates(ctx
);
229 // Launch the asynchronous operation
230 std::make_shared
<session
>(ioc
, ctx
)->run(host
, port
, target
, version
);
232 // Run the I/O service. The call will return when
233 // the get operation is complete.