MEPP2 Project
http_client_async_ssl.cpp
Go to the documentation of this file.
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
13 //
14 //------------------------------------------------------------------------------
15 
16 #include "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 <cstdlib>
24 #include <functional>
25 #include <iostream>
26 #include <memory>
27 #include <string>
28 
29 namespace beast = boost::beast; // from <boost/beast.hpp>
30 namespace http = beast::http; // from <boost/beast/http.hpp>
31 namespace net = boost::asio; // from <boost/asio.hpp>
32 namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
33 using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
34 
35 //------------------------------------------------------------------------------
36 
37 // Report a failure
38 void
39 fail(beast::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  beast::ssl_stream<beast::tcp_stream> stream_;
49  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  explicit
56  net::executor ex,
57  ssl::context& ctx)
58  : resolver_(ex)
59  , stream_(ex, ctx)
60  {
61  }
62 
63  // Start the asynchronous operation
64  void
65  run(
66  char const* host,
67  char const* port,
68  char const* target,
69  int version)
70  {
71  // Set SNI Hostname (many hosts need this to handshake successfully)
72  if(! SSL_set_tlsext_host_name(stream_.native_handle(), host))
73  {
74  beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()};
75  std::cerr << ec.message() << "\n";
76  return;
77  }
78 
79  // Set up an HTTP GET request message
80  req_.version(version);
81  req_.method(http::verb::get);
82  req_.target(target);
83  req_.set(http::field::host, host);
84  req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
85 
86  // Look up the domain name
87  resolver_.async_resolve(
88  host,
89  port,
90  beast::bind_front_handler(
92  shared_from_this()));
93  }
94 
95  void
97  beast::error_code ec,
98  tcp::resolver::results_type results)
99  {
100  if(ec)
101  return fail(ec, "resolve");
102 
103  // Set a timeout on the operation
104  beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
105 
106  // Make the connection on the IP address we get from a lookup
107  beast::get_lowest_layer(stream_).async_connect(
108  results,
109  beast::bind_front_handler(
111  shared_from_this()));
112  }
113 
114  void
115  on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type)
116  {
117  if(ec)
118  return fail(ec, "connect");
119 
120  // Perform the SSL handshake
121  stream_.async_handshake(
122  ssl::stream_base::client,
123  beast::bind_front_handler(
125  shared_from_this()));
126  }
127 
128  void
129  on_handshake(beast::error_code ec)
130  {
131  if(ec)
132  return fail(ec, "handshake");
133 
134  // Set a timeout on the operation
135  beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
136 
137  // Send the HTTP request to the remote host
138  http::async_write(stream_, req_,
139  beast::bind_front_handler(
141  shared_from_this()));
142  }
143 
144  void
146  beast::error_code ec,
147  std::size_t bytes_transferred)
148  {
149  boost::ignore_unused(bytes_transferred);
150 
151  if(ec)
152  return fail(ec, "write");
153 
154  // Receive the HTTP response
155  http::async_read(stream_, buffer_, res_,
156  beast::bind_front_handler(
158  shared_from_this()));
159  }
160 
161  void
163  beast::error_code ec,
164  std::size_t bytes_transferred)
165  {
166  boost::ignore_unused(bytes_transferred);
167 
168  if(ec)
169  return fail(ec, "read");
170 
171  // Write the message to standard out
172  std::cout << res_ << std::endl;
173 
174  // Set a timeout on the operation
175  beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
176 
177  // Gracefully close the stream
178  stream_.async_shutdown(
179  beast::bind_front_handler(
181  shared_from_this()));
182  }
183 
184  void
185  on_shutdown(beast::error_code ec)
186  {
187  if(ec == net::error::eof)
188  {
189  // Rationale:
190  // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
191  ec = {};
192  }
193  if(ec)
194  return fail(ec, "shutdown");
195 
196  // If we get here then the connection is closed gracefully
197  }
198 };
199 
200 //------------------------------------------------------------------------------
201 
202 int main(int argc, char** argv)
203 {
204  // Check command line arguments.
205  if(argc != 4 && argc != 5)
206  {
207  std::cerr <<
208  "Usage: http-client-async-ssl <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
209  "Example:\n" <<
210  " http-client-async-ssl www.example.com 443 /\n" <<
211  " http-client-async-ssl www.example.com 443 / 1.0\n";
212  return EXIT_FAILURE;
213  }
214  auto const host = argv[1];
215  auto const port = argv[2];
216  auto const target = argv[3];
217  int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11;
218 
219  // The io_context is required for all I/O
220  net::io_context ioc;
221 
222  // The SSL context is required, and holds certificates
223  ssl::context ctx{ssl::context::tlsv12_client};
224 
225  // This holds the root certificate used for verification
227 
228  // Verify the remote server's certificate
229  ctx.set_verify_mode(ssl::verify_peer);
230 
231  // Launch the asynchronous operation
232  // The session is constructed with a strand to
233  // ensure that handlers do not execute concurrently.
234  std::make_shared<session>(
235  net::make_strand(ioc),
236  ctx
237  )->run(host, port, target, version);
238 
239  // Run the I/O service. The call will return when
240  // the get operation is complete.
241  ioc.run();
242 
243  return EXIT_SUCCESS;
244 }
session::on_connect
void on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type)
Definition: http_client_async.cpp:103
tcp
boost::asio::ip::tcp tcp
Definition: http_client_async_ssl.cpp:33
session::on_write
void on_write(beast::error_code ec, std::size_t bytes_transferred)
Definition: http_client_async.cpp:119
session::on_shutdown
void on_shutdown(beast::error_code ec)
Definition: http_client_async_ssl.cpp:185
session::stream_
beast::ssl_stream< beast::tcp_stream > stream_
Definition: http_client_async_ssl.cpp:48
session::on_handshake
void on_handshake(beast::error_code ec)
Definition: http_client_async_ssl.cpp:129
session
Definition: http_client_async.cpp:42
session::resolver_
tcp::resolver resolver_
Definition: http_client_async.cpp:43
session::run
void run(char const *host, char const *port, char const *target, int version)
Definition: http_client_async_ssl.cpp:65
root_certificates.hpp
FEVV::DataStructures::AIF::target
boost::graph_traits< FEVV::DataStructures::AIF::AIFMesh >::vertex_descriptor target(typename boost::graph_traits< FEVV::DataStructures::AIF::AIFMesh >::edge_descriptor e, const FEVV::DataStructures::AIF::AIFMesh &)
Returns the target vertex of e.
Definition: Graph_traits_aif.h:400
session::req_
http::request< http::empty_body > req_
Definition: http_client_async.cpp:46
session::on_resolve
void on_resolve(beast::error_code ec, tcp::resolver::results_type results)
Definition: http_client_async.cpp:84
session::stream_
beast::tcp_stream stream_
Definition: http_client_async.cpp:44
fail
void fail(beast::error_code ec, char const *what)
Definition: http_client_async_ssl.cpp:39
main
int main(int argc, char **argv)
Definition: http_client_async_ssl.cpp:202
boost::get
boost::property_map< FEVV::DataStructures::AIF::AIFMesh, boost::vertex_index_t >::const_type get(const boost::vertex_index_t &, const FEVV::DataStructures::AIF::AIFMesh &)
Returns the vertex index property map of the mesh.
Definition: Graph_properties_aif.h:108
session::on_read
void on_read(beast::error_code ec, std::size_t bytes_transferred)
Definition: http_client_async.cpp:136
session::res_
http::response< http::string_body > res_
Definition: http_client_async.cpp:47
detail::load_root_certificates
void load_root_certificates(ssl::context &ctx, boost::system::error_code &ec)
Definition: root_certificates.hpp:47
session::session
session(net::executor ex, ssl::context &ctx)
Definition: http_client_async_ssl.cpp:55
tcp
boost::asio::ip::tcp tcp
Definition: http_client_async.cpp:29
session::buffer_
beast::flat_buffer buffer_
Definition: http_client_async.cpp:45