• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // async_client.cpp
3 // ~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 #include <iostream>
12 #include <istream>
13 #include <ostream>
14 #include <string>
15 #include <boost/asio.hpp>
16 #include <boost/bind/bind.hpp>
17 
18 using boost::asio::ip::tcp;
19 
20 class client
21 {
22 public:
client(boost::asio::io_context & io_context,const std::string & server,const std::string & path)23   client(boost::asio::io_context& io_context,
24       const std::string& server, const std::string& path)
25     : resolver_(io_context),
26       socket_(io_context)
27   {
28     // Form the request. We specify the "Connection: close" header so that the
29     // server will close the socket after transmitting the response. This will
30     // allow us to treat all data up until the EOF as the content.
31     std::ostream request_stream(&request_);
32     request_stream << "GET " << path << " HTTP/1.0\r\n";
33     request_stream << "Host: " << server << "\r\n";
34     request_stream << "Accept: */*\r\n";
35     request_stream << "Connection: close\r\n\r\n";
36 
37     // Start an asynchronous resolve to translate the server and service names
38     // into a list of endpoints.
39     resolver_.async_resolve(server, "http",
40         boost::bind(&client::handle_resolve, this,
41           boost::asio::placeholders::error,
42           boost::asio::placeholders::results));
43   }
44 
45 private:
handle_resolve(const boost::system::error_code & err,const tcp::resolver::results_type & endpoints)46   void handle_resolve(const boost::system::error_code& err,
47       const tcp::resolver::results_type& endpoints)
48   {
49     if (!err)
50     {
51       // Attempt a connection to each endpoint in the list until we
52       // successfully establish a connection.
53       boost::asio::async_connect(socket_, endpoints,
54           boost::bind(&client::handle_connect, this,
55             boost::asio::placeholders::error));
56     }
57     else
58     {
59       std::cout << "Error: " << err.message() << "\n";
60     }
61   }
62 
handle_connect(const boost::system::error_code & err)63   void handle_connect(const boost::system::error_code& err)
64   {
65     if (!err)
66     {
67       // The connection was successful. Send the request.
68       boost::asio::async_write(socket_, request_,
69           boost::bind(&client::handle_write_request, this,
70             boost::asio::placeholders::error));
71     }
72     else
73     {
74       std::cout << "Error: " << err.message() << "\n";
75     }
76   }
77 
handle_write_request(const boost::system::error_code & err)78   void handle_write_request(const boost::system::error_code& err)
79   {
80     if (!err)
81     {
82       // Read the response status line. The response_ streambuf will
83       // automatically grow to accommodate the entire line. The growth may be
84       // limited by passing a maximum size to the streambuf constructor.
85       boost::asio::async_read_until(socket_, response_, "\r\n",
86           boost::bind(&client::handle_read_status_line, this,
87             boost::asio::placeholders::error));
88     }
89     else
90     {
91       std::cout << "Error: " << err.message() << "\n";
92     }
93   }
94 
handle_read_status_line(const boost::system::error_code & err)95   void handle_read_status_line(const boost::system::error_code& err)
96   {
97     if (!err)
98     {
99       // Check that response is OK.
100       std::istream response_stream(&response_);
101       std::string http_version;
102       response_stream >> http_version;
103       unsigned int status_code;
104       response_stream >> status_code;
105       std::string status_message;
106       std::getline(response_stream, status_message);
107       if (!response_stream || http_version.substr(0, 5) != "HTTP/")
108       {
109         std::cout << "Invalid response\n";
110         return;
111       }
112       if (status_code != 200)
113       {
114         std::cout << "Response returned with status code ";
115         std::cout << status_code << "\n";
116         return;
117       }
118 
119       // Read the response headers, which are terminated by a blank line.
120       boost::asio::async_read_until(socket_, response_, "\r\n\r\n",
121           boost::bind(&client::handle_read_headers, this,
122             boost::asio::placeholders::error));
123     }
124     else
125     {
126       std::cout << "Error: " << err << "\n";
127     }
128   }
129 
handle_read_headers(const boost::system::error_code & err)130   void handle_read_headers(const boost::system::error_code& err)
131   {
132     if (!err)
133     {
134       // Process the response headers.
135       std::istream response_stream(&response_);
136       std::string header;
137       while (std::getline(response_stream, header) && header != "\r")
138         std::cout << header << "\n";
139       std::cout << "\n";
140 
141       // Write whatever content we already have to output.
142       if (response_.size() > 0)
143         std::cout << &response_;
144 
145       // Start reading remaining data until EOF.
146       boost::asio::async_read(socket_, response_,
147           boost::asio::transfer_at_least(1),
148           boost::bind(&client::handle_read_content, this,
149             boost::asio::placeholders::error));
150     }
151     else
152     {
153       std::cout << "Error: " << err << "\n";
154     }
155   }
156 
handle_read_content(const boost::system::error_code & err)157   void handle_read_content(const boost::system::error_code& err)
158   {
159     if (!err)
160     {
161       // Write all of the data that has been read so far.
162       std::cout << &response_;
163 
164       // Continue reading remaining data until EOF.
165       boost::asio::async_read(socket_, response_,
166           boost::asio::transfer_at_least(1),
167           boost::bind(&client::handle_read_content, this,
168             boost::asio::placeholders::error));
169     }
170     else if (err != boost::asio::error::eof)
171     {
172       std::cout << "Error: " << err << "\n";
173     }
174   }
175 
176   tcp::resolver resolver_;
177   tcp::socket socket_;
178   boost::asio::streambuf request_;
179   boost::asio::streambuf response_;
180 };
181 
main(int argc,char * argv[])182 int main(int argc, char* argv[])
183 {
184   try
185   {
186     if (argc != 3)
187     {
188       std::cout << "Usage: async_client <server> <path>\n";
189       std::cout << "Example:\n";
190       std::cout << "  async_client www.boost.org /LICENSE_1_0.txt\n";
191       return 1;
192     }
193 
194     boost::asio::io_context io_context;
195     client c(io_context, argv[1], argv[2]);
196     io_context.run();
197   }
198   catch (std::exception& e)
199   {
200     std::cout << "Exception: " << e.what() << "\n";
201   }
202 
203   return 0;
204 }
205