http_client_async_ssl_system_executor.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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. // Example: HTTP SSL client, asynchronous, using system_executor
  12. //
  13. //------------------------------------------------------------------------------
  14. #include "example/common/root_certificates.hpp"
  15. #include <boost/beast/core.hpp>
  16. #include <boost/beast/http.hpp>
  17. #include <boost/beast/ssl.hpp>
  18. #include <boost/beast/version.hpp>
  19. #include <boost/asio/strand.hpp>
  20. #include <boost/asio/system_executor.hpp>
  21. #include <cstdlib>
  22. #include <functional>
  23. #include <iostream>
  24. #include <memory>
  25. #include <string>
  26. namespace beast = boost::beast; // from <boost/beast.hpp>
  27. namespace http = beast::http; // from <boost/beast/http.hpp>
  28. namespace net = boost::asio; // from <boost/asio.hpp>
  29. namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
  30. using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
  31. //------------------------------------------------------------------------------
  32. // Report a failure
  33. void
  34. fail(beast::error_code ec, char const* what)
  35. {
  36. std::cerr << what << ": " << ec.message() << "\n";
  37. }
  38. // Performs an HTTP GET and prints the response
  39. class session : public std::enable_shared_from_this<session>
  40. {
  41. tcp::resolver resolver_;
  42. beast::ssl_stream<beast::tcp_stream> stream_;
  43. beast::flat_buffer buffer_; // (Must persist between reads)
  44. http::request<http::empty_body> req_;
  45. http::response<http::string_body> res_;
  46. // Objects are constructed with a strand to
  47. // ensure that handlers do not execute concurrently.
  48. session(net::strand<net::system_executor> strand, ssl::context& ctx)
  49. : resolver_(strand)
  50. , stream_(strand, ctx)
  51. {
  52. }
  53. public:
  54. // Delegate construction to a prive constructor to be able to use
  55. // the same strand for both I/O object.
  56. explicit
  57. session(ssl::context& ctx)
  58. : session(net::make_strand(net::system_executor()), ctx)
  59. {
  60. }
  61. // Start the asynchronous operation
  62. void
  63. run(
  64. char const* host,
  65. char const* port,
  66. char const* target,
  67. int version)
  68. {
  69. // Set SNI Hostname (many hosts need this to handshake successfully)
  70. if(! SSL_set_tlsext_host_name(stream_.native_handle(), host))
  71. {
  72. beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()};
  73. std::cerr << ec.message() << "\n";
  74. return;
  75. }
  76. // Set up an HTTP GET request message
  77. req_.version(version);
  78. req_.method(http::verb::get);
  79. req_.target(target);
  80. req_.set(http::field::host, host);
  81. req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
  82. // Look up the domain name
  83. resolver_.async_resolve(
  84. host,
  85. port,
  86. beast::bind_front_handler(
  87. &session::on_resolve,
  88. shared_from_this()));
  89. }
  90. void
  91. on_resolve(
  92. beast::error_code ec,
  93. tcp::resolver::results_type results)
  94. {
  95. if(ec)
  96. return fail(ec, "resolve");
  97. // Set a timeout on the operation
  98. beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
  99. // Make the connection on the IP address we get from a lookup
  100. beast::get_lowest_layer(stream_).async_connect(
  101. results,
  102. beast::bind_front_handler(
  103. &session::on_connect,
  104. shared_from_this()));
  105. }
  106. void
  107. on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type)
  108. {
  109. if(ec)
  110. return fail(ec, "connect");
  111. // Perform the SSL handshake
  112. stream_.async_handshake(
  113. ssl::stream_base::client,
  114. beast::bind_front_handler(
  115. &session::on_handshake,
  116. shared_from_this()));
  117. }
  118. void
  119. on_handshake(beast::error_code ec)
  120. {
  121. if(ec)
  122. return fail(ec, "handshake");
  123. // Set a timeout on the operation
  124. beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
  125. // Send the HTTP request to the remote host
  126. http::async_write(stream_, req_,
  127. beast::bind_front_handler(
  128. &session::on_write,
  129. shared_from_this()));
  130. }
  131. void
  132. on_write(
  133. beast::error_code ec,
  134. std::size_t bytes_transferred)
  135. {
  136. boost::ignore_unused(bytes_transferred);
  137. if(ec)
  138. return fail(ec, "write");
  139. // Receive the HTTP response
  140. http::async_read(stream_, buffer_, res_,
  141. beast::bind_front_handler(
  142. &session::on_read,
  143. shared_from_this()));
  144. }
  145. void
  146. on_read(
  147. beast::error_code ec,
  148. std::size_t bytes_transferred)
  149. {
  150. boost::ignore_unused(bytes_transferred);
  151. if(ec)
  152. return fail(ec, "read");
  153. // Write the message to standard out
  154. std::cout << res_ << std::endl;
  155. // Set a timeout on the operation
  156. beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
  157. // Gracefully close the stream
  158. stream_.async_shutdown(
  159. beast::bind_front_handler(
  160. &session::on_shutdown,
  161. shared_from_this()));
  162. }
  163. void
  164. on_shutdown(beast::error_code ec)
  165. {
  166. if(ec == net::error::eof)
  167. {
  168. // Rationale:
  169. // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
  170. ec = {};
  171. }
  172. if(ec)
  173. return fail(ec, "shutdown");
  174. // If we get here then the connection is closed gracefully
  175. }
  176. };
  177. //------------------------------------------------------------------------------
  178. int main(int argc, char** argv)
  179. {
  180. // Check command line arguments.
  181. if(argc != 4 && argc != 5)
  182. {
  183. std::cerr <<
  184. "Usage: http-client-async-ssl-system-executor <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
  185. "Example:\n" <<
  186. " http-client-async-ssl-system-executor www.example.com 443 /\n" <<
  187. " http-client-async-ssl-system-executor www.example.com 443 / 1.0\n";
  188. return EXIT_FAILURE;
  189. }
  190. auto const host = argv[1];
  191. auto const port = argv[2];
  192. auto const target = argv[3];
  193. int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11;
  194. // The SSL context is required, and holds certificates
  195. ssl::context ctx{ssl::context::tlsv12_client};
  196. // This holds the root certificate used for verification
  197. load_root_certificates(ctx);
  198. // Verify the remote server's certificate
  199. ctx.set_verify_mode(ssl::verify_peer);
  200. // Launch the asynchronous operation
  201. std::make_shared<session>(ctx)->run(host, port, target, version);
  202. // The async operations will run on the system_executor.
  203. // Because the main thread has nothing to do in this example, we just wait
  204. // for the system_executor to run out of work.
  205. net::system_executor().context().join();
  206. return EXIT_SUCCESS;
  207. }