http_client_async.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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 client, asynchronous
  12. //
  13. //------------------------------------------------------------------------------
  14. #include <boost/beast/core.hpp>
  15. #include <boost/beast/http.hpp>
  16. #include <boost/beast/version.hpp>
  17. #include <boost/asio/strand.hpp>
  18. #include <cstdlib>
  19. #include <functional>
  20. #include <iostream>
  21. #include <memory>
  22. #include <string>
  23. namespace beast = boost::beast; // from <boost/beast.hpp>
  24. namespace http = beast::http; // from <boost/beast/http.hpp>
  25. namespace net = boost::asio; // from <boost/asio.hpp>
  26. using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
  27. //------------------------------------------------------------------------------
  28. // Report a failure
  29. void
  30. fail(beast::error_code ec, char const* what)
  31. {
  32. std::cerr << what << ": " << ec.message() << "\n";
  33. }
  34. // Performs an HTTP GET and prints the response
  35. class session : public std::enable_shared_from_this<session>
  36. {
  37. tcp::resolver resolver_;
  38. beast::tcp_stream stream_;
  39. beast::flat_buffer buffer_; // (Must persist between reads)
  40. http::request<http::empty_body> req_;
  41. http::response<http::string_body> res_;
  42. public:
  43. // Objects are constructed with a strand to
  44. // ensure that handlers do not execute concurrently.
  45. explicit
  46. session(net::io_context& ioc)
  47. : resolver_(net::make_strand(ioc))
  48. , stream_(net::make_strand(ioc))
  49. {
  50. }
  51. // Start the asynchronous operation
  52. void
  53. run(
  54. char const* host,
  55. char const* port,
  56. char const* target,
  57. int version)
  58. {
  59. // Set up an HTTP GET request message
  60. req_.version(version);
  61. req_.method(http::verb::get);
  62. req_.target(target);
  63. req_.set(http::field::host, host);
  64. req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
  65. // Look up the domain name
  66. resolver_.async_resolve(
  67. host,
  68. port,
  69. beast::bind_front_handler(
  70. &session::on_resolve,
  71. shared_from_this()));
  72. }
  73. void
  74. on_resolve(
  75. beast::error_code ec,
  76. tcp::resolver::results_type results)
  77. {
  78. if(ec)
  79. return fail(ec, "resolve");
  80. // Set a timeout on the operation
  81. stream_.expires_after(std::chrono::seconds(30));
  82. // Make the connection on the IP address we get from a lookup
  83. stream_.async_connect(
  84. results,
  85. beast::bind_front_handler(
  86. &session::on_connect,
  87. shared_from_this()));
  88. }
  89. void
  90. on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type)
  91. {
  92. if(ec)
  93. return fail(ec, "connect");
  94. // Set a timeout on the operation
  95. stream_.expires_after(std::chrono::seconds(30));
  96. // Send the HTTP request to the remote host
  97. http::async_write(stream_, req_,
  98. beast::bind_front_handler(
  99. &session::on_write,
  100. shared_from_this()));
  101. }
  102. void
  103. on_write(
  104. beast::error_code ec,
  105. std::size_t bytes_transferred)
  106. {
  107. boost::ignore_unused(bytes_transferred);
  108. if(ec)
  109. return fail(ec, "write");
  110. // Receive the HTTP response
  111. http::async_read(stream_, buffer_, res_,
  112. beast::bind_front_handler(
  113. &session::on_read,
  114. shared_from_this()));
  115. }
  116. void
  117. on_read(
  118. beast::error_code ec,
  119. std::size_t bytes_transferred)
  120. {
  121. boost::ignore_unused(bytes_transferred);
  122. if(ec)
  123. return fail(ec, "read");
  124. // Write the message to standard out
  125. std::cout << res_ << std::endl;
  126. // Gracefully close the socket
  127. stream_.socket().shutdown(tcp::socket::shutdown_both, ec);
  128. // not_connected happens sometimes so don't bother reporting it.
  129. if(ec && ec != beast::errc::not_connected)
  130. return fail(ec, "shutdown");
  131. // If we get here then the connection is closed gracefully
  132. }
  133. };
  134. //------------------------------------------------------------------------------
  135. int main(int argc, char** argv)
  136. {
  137. // Check command line arguments.
  138. if(argc != 4 && argc != 5)
  139. {
  140. std::cerr <<
  141. "Usage: http-client-async <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
  142. "Example:\n" <<
  143. " http-client-async www.example.com 80 /\n" <<
  144. " http-client-async www.example.com 80 / 1.0\n";
  145. return EXIT_FAILURE;
  146. }
  147. auto const host = argv[1];
  148. auto const port = argv[2];
  149. auto const target = argv[3];
  150. int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11;
  151. // The io_context is required for all I/O
  152. net::io_context ioc;
  153. // Launch the asynchronous operation
  154. std::make_shared<session>(ioc)->run(host, port, target, version);
  155. // Run the I/O service. The call will return when
  156. // the get operation is complete.
  157. ioc.run();
  158. return EXIT_SUCCESS;
  159. }