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, coroutine
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/spawn.hpp>
23 #include <boost/asio/ip/tcp.hpp>
24 #include <boost/asio/ssl/error.hpp>
25 #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
47 std::string
const& host
,
48 std::string
const& port
,
49 std::string
const& target
,
51 boost::asio::io_context
& ioc
,
53 boost::asio::yield_context yield
)
55 boost::system::error_code ec
;
57 // These objects perform our I/O
58 tcp::resolver resolver
{ioc
};
59 ssl::stream
<tcp::socket
> stream
{ioc
, ctx
};
61 // Set SNI Hostname (many hosts need this to handshake successfully)
62 if(! SSL_set_tlsext_host_name(stream
.native_handle(), host
.c_str()))
64 ec
.assign(static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category());
65 std::cerr
<< ec
.message() << "\n";
69 // Look up the domain name
70 auto const results
= resolver
.async_resolve(host
, port
, yield
[ec
]);
72 return fail(ec
, "resolve");
74 // Make the connection on the IP address we get from a lookup
75 boost::asio::async_connect(stream
.next_layer(), results
.begin(), results
.end(), yield
[ec
]);
77 return fail(ec
, "connect");
79 // Perform the SSL handshake
80 stream
.async_handshake(ssl::stream_base::client
, yield
[ec
]);
82 return fail(ec
, "handshake");
84 // Set up an HTTP GET request message
85 http::request
<http::string_body
> req
{http::verb::get
, target
, version
};
86 req
.set(http::field::host
, host
);
87 req
.set(http::field::user_agent
, BOOST_BEAST_VERSION_STRING
);
89 // Send the HTTP request to the remote host
90 http::async_write(stream
, req
, yield
[ec
]);
92 return fail(ec
, "write");
94 // This buffer is used for reading and must be persisted
95 boost::beast::flat_buffer b
;
97 // Declare a container to hold the response
98 http::response
<http::dynamic_body
> res
;
100 // Receive the HTTP response
101 http::async_read(stream
, b
, res
, yield
[ec
]);
103 return fail(ec
, "read");
105 // Write the message to standard out
106 std::cout
<< res
<< std::endl
;
108 // Gracefully close the stream
109 stream
.async_shutdown(yield
[ec
]);
110 if(ec
== boost::asio::error::eof
)
113 // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
114 ec
.assign(0, ec
.category());
117 return fail(ec
, "shutdown");
119 // If we get here then the connection is closed gracefully
122 //------------------------------------------------------------------------------
124 int main(int argc
, char** argv
)
126 // Check command line arguments.
127 if(argc
!= 4 && argc
!= 5)
130 "Usage: http-client-coro-ssl <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
132 " http-client-coro-ssl www.example.com 443 /\n" <<
133 " http-client-coro-ssl www.example.com 443 / 1.0\n";
136 auto const host
= argv
[1];
137 auto const port
= argv
[2];
138 auto const target
= argv
[3];
139 int version
= argc
== 5 && !std::strcmp("1.0", argv
[4]) ? 10 : 11;
141 // The io_context is required for all I/O
142 boost::asio::io_context ioc
;
144 // The SSL context is required, and holds certificates
145 ssl::context ctx
{ssl::context::sslv23_client
};
147 // This holds the root certificate used for verification
148 load_root_certificates(ctx
);
150 // Launch the asynchronous operation
151 boost::asio::spawn(ioc
, std::bind(
159 std::placeholders::_1
));
161 // Run the I/O service. The call will return when
162 // the get operation is complete.