RAPP Platform API
 All Classes Namespaces Files Functions Variables Typedefs
asio_socket_https.cpp
Go to the documentation of this file.
1 #include "asio_socket_https.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  // init timer
20  if (!timer_) {
21  timer_ = std::unique_ptr<boost::asio::deadline_timer>(
22  new boost::asio::deadline_timer(io_service));
23  }
24  timer_->async_wait(boost::bind(&asio_socket_https::check_timeout, this));
25 
26  // init tls socket wrapper
27  if (!tls_socket_) {
28  tls_socket_ = std::unique_ptr<boost_tls_socket>(
29  new boost_tls_socket(io_service, ctx_));
30  }
31  assert(tls_socket_);
32  // disable ssl v2 and ssl v3 (allow only tls)
33  ctx_.set_options(boost::asio::ssl::context::default_workarounds
34  |boost::asio::ssl::context::no_sslv2
35  |boost::asio::ssl::context::no_sslv3
36  |boost::asio::ssl::context::no_tlsv1
37  |boost::asio::ssl::context::single_dh_use);
38 
39  // if using a self-signed certificate the only way to pass verification
40  // is to "install" it locally and use it for comparison
41  ctx_.load_verify_file("ca.pem");
42  // resolve iterator
43  boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
44  // set TLS verify mode
45  tls_socket_->set_verify_mode(boost::asio::ssl::verify_peer
46  |boost::asio::ssl::verify_fail_if_no_peer_cert);
47  // verify callback
48  tls_socket_->set_verify_callback(boost::bind(&asio_socket_https::verify_certificate,
49  this, _1, _2));
50  // begin connect
51  boost::asio::async_connect(tls_socket_->lowest_layer(),
52  endpoint_iterator,
54  this,
55  boost::asio::placeholders::error));
56 }
57 
59 bool asio_socket_https::verify_certificate(bool preverified, boost::asio::ssl::verify_context& ctx)
60 {
61  char subject_name[256];
62  X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
63  X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
64  return preverified;
65 }
66 
68 void asio_socket_https::handle_connect(const boost::system::error_code& error)
69 {
70  assert(tls_socket_);
71  if (!error) {
72  timer_->expires_from_now(boost::posix_time::seconds(30));
73  tls_socket_->async_handshake(boost::asio::ssl::stream_base::client,
75  this,
76  boost::asio::placeholders::error));
77  }
78  else {
79  std::cerr << "connect failed: " << error.message() << "\n";
80  }
81 }
82 
84 void asio_socket_https::handle_handshake(const boost::system::error_code& error)
85 {
86  assert(tls_socket_ && timer_);
87  if (!error) {
88  // read stream = header + post body into request_
89  std::ostream request_stream(&request_);
90  request_stream << header_ << post_ << "\r\n";
91  timer_->expires_from_now(boost::posix_time::seconds(30));
92  // write to the socket
93  boost::asio::async_write(*tls_socket_,
94  request_,
95  boost::bind(&asio_socket_https::handle_write,
96  this,
97  boost::asio::placeholders::error));
98  }
99  else {
100  std::cerr << "Handshake failed: " << error.message() << "\n";
101  }
102 }
103 
105 void asio_socket_https::handle_write(const boost::system::error_code & err)
106 {
107  assert(tls_socket_ && timer_);
108  if (!err) {
109  timer_->expires_from_now(boost::posix_time::seconds(30));
110  // Read the response status line - Callback handler is ::handle_read_status_line
111  boost::asio::async_read_until(*tls_socket_,
112  response_,
113  "\r\n",
115  this,
116  boost::asio::placeholders::error));
117  }
118  else {
119  error_handler(err);
120  }
121 }
122 
124 void asio_socket_https::handle_read_status_line(const boost::system::error_code & err)
125 {
126  assert(tls_socket_ && timer_);
127  if (!err) {
128  // Check that HTTP Header Response is OK.
129  std::istream response_stream(&response_);
130  std::string http_version;
131  response_stream >> http_version;
132  unsigned int status_code;
133  response_stream >> status_code;
134  std::string status_message;
135  std::getline( response_stream, status_message );
136  if (!response_stream || http_version.substr(0, 5) != "HTTP/") {
137  invalid_request("http Invalid response");
138  return;
139  }
140  if (status_code != 200) {
141  invalid_request(std::to_string(status_code));
142  return;
143  }
144  timer_->expires_from_now(boost::posix_time::seconds(30));
145  // Read the response headers, which are terminated by a blank line.
146  // This is HTTP Protocol 1.0 & 1.1
147  boost::asio::async_read_until(*tls_socket_,
148  response_,
149  "\r\n\r\n",
151  this,
152  boost::asio::placeholders::error));
153  }
154  else {
155  error_handler(err);
156  }
157 }
158 
160 void asio_socket_https::handle_read_headers(const boost::system::error_code & err)
161 {
162  assert(tls_socket_ && timer_);
163  if (!err) {
164  timer_->expires_from_now(boost::posix_time::seconds(30));
165  // Start reading Content data until EOF (see handle_read_content)
166  boost::asio::async_read_until(*tls_socket_,
167  response_,
168  "\r\n\r\n",
170  this,
171  boost::asio::placeholders::error));
172  }
173  else {
174  error_handler(err);
175  }
176 }
177 
179 void asio_socket_https::handle_read_content(const boost::system::error_code & err)
180 {
181  assert(tls_socket_ && timer_);
182  if (!err) {
183  timer_->expires_from_now(boost::posix_time::seconds(30));
184  // Continue reading remaining data until EOF - It reccursively calls its self
185  boost::asio::async_read(*tls_socket_,
186  response_,
187  boost::asio::transfer_at_least(1),
189  this,
190  boost::asio::placeholders::error));
191 
192  // Parse HTTP Content.
193  std::string json((std::istreambuf_iterator<char>(&response_)),
194  std::istreambuf_iterator<char>());
195 
196  // TODO: see asio_service_http
197  }
198  else if (err != boost::asio::error::eof) {
199  error_handler(err);
200  }
201 }
202 
204 {
205  assert(timer_ && tls_socket_);
206  header_.clear();
207  post_.clear();
208  json_.clear();
209  content_length_ = 0;
210  bytes_transferred_ = 0;
211  flag_ = false;
212  tls_socket_->lowest_layer().close();
213  timer_->expires_at(boost::posix_time::pos_infin);
214 }
215 
217 {
218  assert(timer_);
219  if (timer_->expires_at() <= boost::asio::deadline_timer::traits_type::now()) {
220  std::cerr << "connection time-out" << std::endl;
221  reset();
222  }
223  // Put the actor back to sleep.
224  timer_->async_wait(boost::bind(&asio_socket_https::check_timeout, this));
225 }
226 
227 
228 }
229 }
std::unique_ptr< boost_tls_socket > tls_socket_
tls/ssl socket wrapper
std::string header_
Header that will be used.
void handle_connect(const boost::system::error_code &error)
begin connect
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.
boost::asio::ssl::context ctx_
tls context
bool verify_certificate(bool preverified, boost::asio::ssl::verify_context &ctx)
verify TLS certificate
void content_length(std::string response, std::size_t &length)
get the content leangth from
std::string post_
Actual post Data.
void handle_read_headers(const boost::system::error_code &err)
Callback for Handling Headers.
boost::asio::ssl::stream< boost::asio::ip::tcp::socket > boost_tls_socket
boost tls wraps around a tcp socket
std::atomic< bool > flag_
flag used to extract header
boost::asio::streambuf response_
Response Container.
boost::asio::streambuf request_
Request Container.
void handle_write(const boost::system::error_code &err)
Callback for handling request and waiting for response.
void error_handler(const boost::system::error_code &error)
Handle an Error.
Definition: asio_handler.cpp:6
void reset()
reset handler (clear data, bytes, etc) and stop connection
void check_timeout()
check timed-out
std::string json_
JSON reply.
const std::string token_
user authentication token
void handle_read_status_line(const boost::system::error_code &err)
Callback for handling HTTP Header Response Data.
void handle_handshake(const boost::system::error_code &error)
handle handshake
void schedule(boost::asio::ip::tcp::resolver::query &query, boost::asio::ip::tcp::resolver &resolver, boost::asio::io_service &io_service)
void handle_read_content(const boost::system::error_code &err)
Callback for Handling Actual Data Contents.
std::size_t bytes_transferred_
Bytes Transferred.
std::unique_ptr< boost::asio::deadline_timer > timer_
time-out timer
constexpr char address[]
api.rapp.cloud -
Definition: asio_socket.hpp:8