]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/example/http/client/async-ssl/http_client_async_ssl.cpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / libs / beast / example / http / client / async-ssl / http_client_async_ssl.cpp
1 //
2 // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
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)
6 //
7 // Official repository: https://github.com/boostorg/beast
8 //
9
10 //------------------------------------------------------------------------------
11 //
12 // Example: HTTP SSL client, asynchronous
13 //
14 //------------------------------------------------------------------------------
15
16 #include "example/common/root_certificates.hpp"
17
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>
25 #include <cstdlib>
26 #include <functional>
27 #include <iostream>
28 #include <memory>
29 #include <string>
30
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>
34
35 //------------------------------------------------------------------------------
36
37 // Report a failure
38 void
39 fail(boost::system::error_code ec, char const* what)
40 {
41 std::cerr << what << ": " << ec.message() << "\n";
42 }
43
44 // Performs an HTTP GET and prints the response
45 class session : public std::enable_shared_from_this<session>
46 {
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_;
52
53 public:
54 // Resolver and stream require an io_context
55 explicit
56 session(boost::asio::io_context& ioc, ssl::context& ctx)
57 : resolver_(ioc)
58 , stream_(ioc, ctx)
59 {
60 }
61
62 // Start the asynchronous operation
63 void
64 run(
65 char const* host,
66 char const* port,
67 char const* target,
68 int version)
69 {
70 // Set SNI Hostname (many hosts need this to handshake successfully)
71 if(! SSL_set_tlsext_host_name(stream_.native_handle(), host))
72 {
73 boost::system::error_code ec{static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()};
74 std::cerr << ec.message() << "\n";
75 return;
76 }
77
78 // Set up an HTTP GET request message
79 req_.version(version);
80 req_.method(http::verb::get);
81 req_.target(target);
82 req_.set(http::field::host, host);
83 req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
84
85 // Look up the domain name
86 resolver_.async_resolve(
87 host,
88 port,
89 std::bind(
90 &session::on_resolve,
91 shared_from_this(),
92 std::placeholders::_1,
93 std::placeholders::_2));
94 }
95
96 void
97 on_resolve(
98 boost::system::error_code ec,
99 tcp::resolver::results_type results)
100 {
101 if(ec)
102 return fail(ec, "resolve");
103
104 // Make the connection on the IP address we get from a lookup
105 boost::asio::async_connect(
106 stream_.next_layer(),
107 results.begin(),
108 results.end(),
109 std::bind(
110 &session::on_connect,
111 shared_from_this(),
112 std::placeholders::_1));
113 }
114
115 void
116 on_connect(boost::system::error_code ec)
117 {
118 if(ec)
119 return fail(ec, "connect");
120
121 // Perform the SSL handshake
122 stream_.async_handshake(
123 ssl::stream_base::client,
124 std::bind(
125 &session::on_handshake,
126 shared_from_this(),
127 std::placeholders::_1));
128 }
129
130 void
131 on_handshake(boost::system::error_code ec)
132 {
133 if(ec)
134 return fail(ec, "handshake");
135
136 // Send the HTTP request to the remote host
137 http::async_write(stream_, req_,
138 std::bind(
139 &session::on_write,
140 shared_from_this(),
141 std::placeholders::_1,
142 std::placeholders::_2));
143 }
144
145 void
146 on_write(
147 boost::system::error_code ec,
148 std::size_t bytes_transferred)
149 {
150 boost::ignore_unused(bytes_transferred);
151
152 if(ec)
153 return fail(ec, "write");
154
155 // Receive the HTTP response
156 http::async_read(stream_, buffer_, res_,
157 std::bind(
158 &session::on_read,
159 shared_from_this(),
160 std::placeholders::_1,
161 std::placeholders::_2));
162 }
163
164 void
165 on_read(
166 boost::system::error_code ec,
167 std::size_t bytes_transferred)
168 {
169 boost::ignore_unused(bytes_transferred);
170
171 if(ec)
172 return fail(ec, "read");
173
174 // Write the message to standard out
175 std::cout << res_ << std::endl;
176
177 // Gracefully close the stream
178 stream_.async_shutdown(
179 std::bind(
180 &session::on_shutdown,
181 shared_from_this(),
182 std::placeholders::_1));
183 }
184
185 void
186 on_shutdown(boost::system::error_code ec)
187 {
188 if(ec == boost::asio::error::eof)
189 {
190 // Rationale:
191 // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
192 ec.assign(0, ec.category());
193 }
194 if(ec)
195 return fail(ec, "shutdown");
196
197 // If we get here then the connection is closed gracefully
198 }
199 };
200
201 //------------------------------------------------------------------------------
202
203 int main(int argc, char** argv)
204 {
205 // Check command line arguments.
206 if(argc != 4 && argc != 5)
207 {
208 std::cerr <<
209 "Usage: http-client-async-ssl <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
210 "Example:\n" <<
211 " http-client-async-ssl www.example.com 443 /\n" <<
212 " http-client-async-ssl www.example.com 443 / 1.0\n";
213 return EXIT_FAILURE;
214 }
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;
219
220 // The io_context is required for all I/O
221 boost::asio::io_context ioc;
222
223 // The SSL context is required, and holds certificates
224 ssl::context ctx{ssl::context::sslv23_client};
225
226 // This holds the root certificate used for verification
227 load_root_certificates(ctx);
228
229 // Launch the asynchronous operation
230 std::make_shared<session>(ioc, ctx)->run(host, port, target, version);
231
232 // Run the I/O service. The call will return when
233 // the get operation is complete.
234 ioc.run();
235
236 return EXIT_SUCCESS;
237 }