2 // Copyright (c) 2016-2019 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, using system_executor
14 //------------------------------------------------------------------------------
16 #include "example/common/root_certificates.hpp"
18 #include <boost/beast/core.hpp>
19 #include <boost/beast/http.hpp>
20 #include <boost/beast/ssl.hpp>
21 #include <boost/beast/version.hpp>
22 #include <boost/asio/strand.hpp>
23 #include <boost/asio/system_executor.hpp>
30 namespace beast = boost::beast; // from <boost/beast.hpp>
31 namespace http = beast::http; // from <boost/beast/http.hpp>
32 namespace net = boost::asio; // from <boost/asio.hpp>
33 namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
34 using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
36 //------------------------------------------------------------------------------
40 fail(beast::error_code ec, char const* what)
42 std::cerr << what << ": " << ec.message() << "\n";
45 // Performs an HTTP GET and prints the response
46 class session : public std::enable_shared_from_this<session>
48 tcp::resolver resolver_;
49 beast::ssl_stream<beast::tcp_stream> stream_;
50 beast::flat_buffer buffer_; // (Must persist between reads)
51 http::request<http::empty_body> req_;
52 http::response<http::string_body> res_;
54 // Objects are constructed with a strand to
55 // ensure that handlers do not execute concurrently.
56 session(net::strand<net::system_executor> strand, ssl::context& ctx)
58 , stream_(strand, ctx)
63 // Delegate construction to a prive constructor to be able to use
64 // the same strand for both I/O object.
66 session(ssl::context& ctx)
67 : session(net::make_strand(net::system_executor()), ctx)
71 // Start the asynchronous operation
79 // Set SNI Hostname (many hosts need this to handshake successfully)
80 if(! SSL_set_tlsext_host_name(stream_.native_handle(), host))
82 beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()};
83 std::cerr << ec.message() << "\n";
87 // Set up an HTTP GET request message
88 req_.version(version);
89 req_.method(http::verb::get);
91 req_.set(http::field::host, host);
92 req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
94 // Look up the domain name
95 resolver_.async_resolve(
98 beast::bind_front_handler(
100 shared_from_this()));
105 beast::error_code ec,
106 tcp::resolver::results_type results)
109 return fail(ec, "resolve");
111 // Set a timeout on the operation
112 beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
114 // Make the connection on the IP address we get from a lookup
115 beast::get_lowest_layer(stream_).async_connect(
117 beast::bind_front_handler(
118 &session::on_connect,
119 shared_from_this()));
123 on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type)
126 return fail(ec, "connect");
128 // Perform the SSL handshake
129 stream_.async_handshake(
130 ssl::stream_base::client,
131 beast::bind_front_handler(
132 &session::on_handshake,
133 shared_from_this()));
137 on_handshake(beast::error_code ec)
140 return fail(ec, "handshake");
142 // Set a timeout on the operation
143 beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
145 // Send the HTTP request to the remote host
146 http::async_write(stream_, req_,
147 beast::bind_front_handler(
149 shared_from_this()));
154 beast::error_code ec,
155 std::size_t bytes_transferred)
157 boost::ignore_unused(bytes_transferred);
160 return fail(ec, "write");
162 // Receive the HTTP response
163 http::async_read(stream_, buffer_, res_,
164 beast::bind_front_handler(
166 shared_from_this()));
171 beast::error_code ec,
172 std::size_t bytes_transferred)
174 boost::ignore_unused(bytes_transferred);
177 return fail(ec, "read");
179 // Write the message to standard out
180 std::cout << res_ << std::endl;
182 // Set a timeout on the operation
183 beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
185 // Gracefully close the stream
186 stream_.async_shutdown(
187 beast::bind_front_handler(
188 &session::on_shutdown,
189 shared_from_this()));
193 on_shutdown(beast::error_code ec)
195 if(ec == net::error::eof)
198 // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
202 return fail(ec, "shutdown");
204 // If we get here then the connection is closed gracefully
208 //------------------------------------------------------------------------------
210 int main(int argc, char** argv)
212 // Check command line arguments.
213 if(argc != 4 && argc != 5)
216 "Usage: http-client-async-ssl-system-executor <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
218 " http-client-async-ssl-system-executor www.example.com 443 /\n" <<
219 " http-client-async-ssl-system-executor www.example.com 443 / 1.0\n";
222 auto const host = argv[1];
223 auto const port = argv[2];
224 auto const target = argv[3];
225 int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11;
227 // The SSL context is required, and holds certificates
228 ssl::context ctx{ssl::context::tlsv12_client};
230 // This holds the root certificate used for verification
231 load_root_certificates(ctx);
233 // Verify the remote server's certificate
234 ctx.set_verify_mode(ssl::verify_peer);
236 // Launch the asynchronous operation
237 std::make_shared<session>(ctx)->run(host, port, target, version);
240 // The async operations will run on the system_executor.
241 // Because the main thread has nothing to do in this example, we just wait
242 // for the system_executor to run out of work.
243 net::system_executor().context().join();