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