Imported Upstream version 1.72.0
[platform/upstream/boost.git] / libs / beast / example / http / client / async-ssl-system-executor / http_client_async_ssl_system_executor.cpp
1 //
2 // Copyright (c) 2016-2019 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, using system_executor
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/ssl.hpp>
21 #include <boost/beast/version.hpp>
22 #include <boost/asio/strand.hpp>
23 #include <boost/asio/system_executor.hpp>
24 #include <cstdlib>
25 #include <functional>
26 #include <iostream>
27 #include <memory>
28 #include <string>
29
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>
35
36 //------------------------------------------------------------------------------
37
38 // Report a failure
39 void
40 fail(beast::error_code ec, char const* what)
41 {
42     std::cerr << what << ": " << ec.message() << "\n";
43 }
44
45 // Performs an HTTP GET and prints the response
46 class session : public std::enable_shared_from_this<session>
47 {
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_;
53
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)
57         : resolver_(strand)
58         , stream_(strand, ctx)
59     {
60     }
61
62 public:
63     // Delegate construction to a prive constructor to be able to use
64     // the same strand for both I/O object.
65     explicit
66     session(ssl::context& ctx)
67         : session(net::make_strand(net::system_executor()), ctx)
68     {
69     }
70
71     // Start the asynchronous operation
72     void
73     run(
74         char const* host,
75         char const* port,
76         char const* target,
77         int version)
78     {
79         // Set SNI Hostname (many hosts need this to handshake successfully)
80         if(! SSL_set_tlsext_host_name(stream_.native_handle(), host))
81         {
82             beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()};
83             std::cerr << ec.message() << "\n";
84             return;
85         }
86
87         // Set up an HTTP GET request message
88         req_.version(version);
89         req_.method(http::verb::get);
90         req_.target(target);
91         req_.set(http::field::host, host);
92         req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
93
94         // Look up the domain name
95         resolver_.async_resolve(
96             host,
97             port,
98             beast::bind_front_handler(
99                 &session::on_resolve,
100                 shared_from_this()));
101     }
102
103     void
104     on_resolve(
105         beast::error_code ec,
106         tcp::resolver::results_type results)
107     {
108         if(ec)
109             return fail(ec, "resolve");
110
111         // Set a timeout on the operation
112         beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
113
114         // Make the connection on the IP address we get from a lookup
115         beast::get_lowest_layer(stream_).async_connect(
116             results,
117             beast::bind_front_handler(
118                 &session::on_connect,
119                 shared_from_this()));
120     }
121
122     void
123     on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type)
124     {
125         if(ec)
126             return fail(ec, "connect");
127
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()));
134     }
135
136     void
137     on_handshake(beast::error_code ec)
138     {
139         if(ec)
140             return fail(ec, "handshake");
141
142         // Set a timeout on the operation
143         beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
144
145         // Send the HTTP request to the remote host
146         http::async_write(stream_, req_,
147             beast::bind_front_handler(
148                 &session::on_write,
149                 shared_from_this()));
150     }
151
152     void
153     on_write(
154         beast::error_code ec,
155         std::size_t bytes_transferred)
156     {
157         boost::ignore_unused(bytes_transferred);
158
159         if(ec)
160             return fail(ec, "write");
161
162         // Receive the HTTP response
163         http::async_read(stream_, buffer_, res_,
164             beast::bind_front_handler(
165                 &session::on_read,
166                 shared_from_this()));
167     }
168
169     void
170     on_read(
171         beast::error_code ec,
172         std::size_t bytes_transferred)
173     {
174         boost::ignore_unused(bytes_transferred);
175
176         if(ec)
177             return fail(ec, "read");
178
179         // Write the message to standard out
180         std::cout << res_ << std::endl;
181
182         // Set a timeout on the operation
183         beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
184
185         // Gracefully close the stream
186         stream_.async_shutdown(
187             beast::bind_front_handler(
188                 &session::on_shutdown,
189                 shared_from_this()));
190     }
191
192     void
193     on_shutdown(beast::error_code ec)
194     {
195         if(ec == net::error::eof)
196         {
197             // Rationale:
198             // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
199             ec = {};
200         }
201         if(ec)
202             return fail(ec, "shutdown");
203
204         // If we get here then the connection is closed gracefully
205     }
206 };
207
208 //------------------------------------------------------------------------------
209
210 int main(int argc, char** argv)
211 {
212     // Check command line arguments.
213     if(argc != 4 && argc != 5)
214     {
215         std::cerr <<
216             "Usage: http-client-async-ssl-system-executor <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
217             "Example:\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";
220         return EXIT_FAILURE;
221     }
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;
226
227     // The SSL context is required, and holds certificates
228     ssl::context ctx{ssl::context::tlsv12_client};
229
230     // This holds the root certificate used for verification
231     load_root_certificates(ctx);
232
233     // Verify the remote server's certificate
234     ctx.set_verify_mode(ssl::verify_peer);
235
236     // Launch the asynchronous operation
237     std::make_shared<session>(ctx)->run(host, port, target, version);
238
239
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();
244
245     return EXIT_SUCCESS;
246 }