RAPP Platform API
 All Classes Namespaces Files Functions Variables Typedefs
asio_service_http.cpp
Go to the documentation of this file.
1 #include "asio_service_http.hpp"
2 
3 namespace rapp {
4 namespace cloud {
5 
7  boost::asio::ip::tcp::resolver::query & query,
8  boost::asio::ip::tcp::resolver & resolver,
9  boost::asio::io_service & io_service
10  )
11 {
12  auto content_length = post_.size() * sizeof(std::string::value_type);
13  header_ += "Host: " + std::string(rapp::cloud::address) + "\r\n"
14  + "Accept-Token: " + token_ + "\r\n"
15  + "Connection: close\r\n"
16  + "Content-Length: " + boost::lexical_cast<std::string>(content_length)
17  + "\r\n\r\n";
18 
19  std::ostream request_stream(&request_);
20  request_stream << header_ << post_ << "\r\n";
21 
22  // init timer
23  if (!timer_) {
24  timer_ = std::unique_ptr<boost::asio::deadline_timer>(
25  new boost::asio::deadline_timer(io_service));
26  }
27  timer_->async_wait(boost::bind(&asio_service_http::check_timeout, this));
28 
29  // init socket
30  if (!socket_) {
31  socket_ = std::unique_ptr<boost::asio::ip::tcp::socket>(
32  new boost::asio::ip::tcp::socket(io_service));
33  }
34  resolver.async_resolve(query,
36  this,
37  boost::asio::placeholders::error,
38  boost::asio::placeholders::iterator));
39 }
40 
42  const boost::system::error_code & err,
43  boost::asio::ip::tcp::resolver::iterator endpoint_iterator
44  )
45 {
46  assert(socket);
47  if (!err){
48  auto endpoint = * endpoint_iterator;
49  socket_->async_connect(endpoint,
51  this,
52  boost::asio::placeholders::error,
53  ++endpoint_iterator));
54  }
55  else
56  error_handler(err);
57 }
58 
60  const boost::system::error_code & err,
61  boost::asio::ip::tcp::resolver::iterator endpoint_iterator
62  )
63 {
64  assert(socket_);
65  if (!err) {
66  timer_->expires_from_now(boost::posix_time::seconds(30));
67  boost::asio::async_write(*socket_.get(),
68  request_,
70  this,
71  boost::asio::placeholders::error));
72  }
73  else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator()) {
74  socket_->close();
75  auto endpoint = *endpoint_iterator;
76  socket_->async_connect(endpoint,
78  this,
79  boost::asio::placeholders::error,
80  ++endpoint_iterator));
81  }
82  else {
83  error_handler(err);
84  }
85 }
86 
88 void asio_service_http::handle_write_request(const boost::system::error_code & err)
89 {
90  assert(socket_ && timer_);
91  if (!err) {
92  timer_->expires_from_now(boost::posix_time::seconds(30));
93  // Read the response status line - Callback handler is ::handle_read_status_line
94  boost::asio::async_read_until(*socket_.get(),
95  response_,
96  "\r\n",
98  this,
99  boost::asio::placeholders::error));
100  }
101  else {
102  error_handler(err);
103  }
104 }
105 
107 void asio_service_http::handle_read_status_line(const boost::system::error_code & err)
108 {
109  assert(socket_ && timer_);
110  if (!err) {
111  // Check that HTTP Header Response is OK.
112  std::istream response_stream(&response_);
113  std::string http_version;
114  response_stream >> http_version;
115  unsigned int status_code;
116  response_stream >> status_code;
117  std::string status_message;
118  std::getline( response_stream, status_message );
119  if (!response_stream || http_version.substr(0, 5) != "HTTP/") {
120  invalid_request("http Invalid response");
121  return;
122  }
123  if (status_code != 200) {
124  invalid_request(std::to_string(status_code));
125  return;
126  }
127  timer_->expires_from_now(boost::posix_time::seconds(30));
128  // Read the response headers, which are terminated by a blank line.
129  // This is HTTP Protocol 1.0 & 1.1
130  boost::asio::async_read_until(*socket_.get(),
131  response_,
132  "\r\n\r\n",
134  this,
135  boost::asio::placeholders::error));
136  }
137  else {
138  error_handler(err);
139  }
140 }
141 
143 void asio_service_http::handle_read_headers(const boost::system::error_code & err)
144 {
145  assert(socket_ && timer_);
146  if (!err) {
147  timer_->expires_from_now(boost::posix_time::seconds(30));
148  // Start reading Content data until EOF (see handle_read_content)
149  boost::asio::async_read_until(*socket_.get(),
150  response_,
151  "\r\n\r\n",
153  this,
154  boost::asio::placeholders::error,
155  boost::asio::placeholders::bytes_transferred));
156  }
157  else {
158  error_handler(err);
159  }
160 }
161 
163 void asio_service_http::handle_read_content(const boost::system::error_code & err, std::size_t bytes)
164 {
165  assert(socket_ && timer_);
166  if (!err) {
167  timer_->expires_from_now(boost::posix_time::seconds(30));
168  // Continue reading remaining data until EOF - It reccursively calls its self
169  boost::asio::async_read(*socket_.get(),
170  response_,
171  boost::asio::transfer_at_least(1),
173  this,
174  boost::asio::placeholders::error,
175  boost::asio::placeholders::bytes_transferred));
176 
177  // Parse HTTP Content.
178  std::string buffer((std::istreambuf_iterator<char>(&response_)),
179  std::istreambuf_iterator<char>());
180 
181  // extract content length once and strip header
182  if (!flag_) {
183  if (has_content_length(buffer)) {
185  std::string tmp = buffer;
186  buffer = strip_header(tmp);
187  }
188  else {
189  throw std::runtime_error("no `Content-Length` in header response");
190  }
191  flag_ = true;
192  }
193 
194  // append buffer and count bytes
195  if (!buffer.empty()) {
196  json_ += buffer;
197  bytes_transferred_ += buffer.size();
199  assert(callback_);
200  callback_(json_);
201  }
202  }
203  }
204  // some error
205  else if (err != boost::asio::error::eof) {
206  error_handler(err);
207  }
208  // Received end of file
209  else if (err == boost::asio::error::eof) {
211  std::cerr << "connection dropped unexpectedly" << std::endl;
212  return;
213  }
214  }
215 }
216 
218 {
219  assert(timer_ && socket_);
220  header_.clear();
221  post_.clear();
222  json_.clear();
223  content_length_ = 0;
224  bytes_transferred_ = 0;
225  flag_ = false;
226  socket_->close();
227  timer_->expires_at(boost::posix_time::pos_infin);
228 }
229 
231 {
232  assert(timer_);
233  if (timer_->expires_at() <= boost::asio::deadline_timer::traits_type::now()) {
234  std::cerr << "connection time-out" << std::endl;
235  reset();
236  }
237  // Put the actor back to sleep.
238  timer_->async_wait(boost::bind(&asio_service_http::check_timeout, this));
239 }
240 
241 
242 }
243 }
void handle_resolve(const boost::system::error_code &err, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
Callback for Handling Address Resolution.
std::string header_
Header that will be used.
void invalid_request(const std::string message)
Handle Invalid Query - e.g.: response which states our query was invalid.
std::size_t content_length_
Content-Length.
void check_timeout()
check timed-out
void content_length(std::string response, std::size_t &length)
get the content leangth from
std::string post_
Actual post Data.
bool has_content_length(std::string response)
examine if the header response contains a content-length filed
void handle_connect(const boost::system::error_code &err, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
void schedule(boost::asio::ip::tcp::resolver::query &query, boost::asio::ip::tcp::resolver &resolver, boost::asio::io_service &io_service)
std::atomic< bool > flag_
flag used to extract header
void reset()
reset handler (clear data, bytes, etc) and stop connection
boost::asio::streambuf response_
Response Container.
std::string strip_header(std::string response)
remove/strip the HTTP header and
boost::asio::streambuf request_
Request Container.
std::function< void(std::string)> callback_
Callback Handler - use with std::bind or boost variant.
void handle_read_headers(const boost::system::error_code &err)
Callback for Handling Headers.
void error_handler(const boost::system::error_code &error)
Handle an Error.
Definition: asio_handler.cpp:6
std::string json_
JSON reply.
const std::string token_
user authentication token
void handle_read_content(const boost::system::error_code &err, std::size_t bytes)
Callback for Handling Actual Data Contents.
void handle_read_status_line(const boost::system::error_code &err)
Callback for handling HTTP Header Response Data.
std::unique_ptr< boost::asio::ip::tcp::socket > socket_
TCP Socket (plain-text)
std::size_t bytes_transferred_
Bytes Transferred.
void handle_write_request(const boost::system::error_code &err)
Callback for handling request and waiting for response.
std::unique_ptr< boost::asio::deadline_timer > timer_
time-out timer
constexpr char address[]
api.rapp.cloud -
Definition: asio_socket.hpp:8