websocketpp  0.6.0
C++/Boost Asio based websocket client/server library
connection.hpp
1 /*
2  * Copyright (c) 2015, Peter Thorson. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution.
11  * * Neither the name of the WebSocket++ Project nor the
12  * names of its contributors may be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #ifndef WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
29 #define WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
30 
31 #include <websocketpp/transport/asio/base.hpp>
32 
33 #include <websocketpp/transport/base/connection.hpp>
34 
35 #include <websocketpp/logger/levels.hpp>
36 #include <websocketpp/http/constants.hpp>
37 
38 #include <websocketpp/base64/base64.hpp>
39 #include <websocketpp/error.hpp>
40 #include <websocketpp/uri.hpp>
41 
42 #include <websocketpp/common/asio.hpp>
43 #include <websocketpp/common/chrono.hpp>
44 #include <websocketpp/common/cpp11.hpp>
45 #include <websocketpp/common/memory.hpp>
46 #include <websocketpp/common/functional.hpp>
47 #include <websocketpp/common/connection_hdl.hpp>
48 
49 #include <istream>
50 #include <sstream>
51 #include <string>
52 #include <vector>
53 
54 namespace websocketpp {
55 namespace transport {
56 namespace asio {
57 
58 typedef lib::function<void(connection_hdl)> tcp_init_handler;
59 
61 
66 template <typename config>
67 class connection : public config::socket_type::socket_con_type {
68 public:
72  typedef lib::shared_ptr<type> ptr;
73 
75  typedef typename config::socket_type::socket_con_type socket_con_type;
77  typedef typename socket_con_type::ptr socket_con_ptr;
79  typedef typename config::alog_type alog_type;
81  typedef typename config::elog_type elog_type;
82 
83  typedef typename config::request_type request_type;
84  typedef typename request_type::ptr request_ptr;
85  typedef typename config::response_type response_type;
86  typedef typename response_type::ptr response_ptr;
87 
89  typedef lib::asio::io_service * io_service_ptr;
91  typedef lib::shared_ptr<lib::asio::io_service::strand> strand_ptr;
93  typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
94 
95  // connection is friends with its associated endpoint to allow the endpoint
96  // to call private/protected utility methods that we don't want to expose
97  // to the public api.
98  friend class endpoint<config>;
99 
100  // generate and manage our own io_service
101  explicit connection(bool is_server, alog_type & alog, elog_type & elog)
102  : m_is_server(is_server)
103  , m_alog(alog)
104  , m_elog(elog)
105  {
106  m_alog.write(log::alevel::devel,"asio con transport constructor");
107  }
108 
110  ptr get_shared() {
111  return lib::static_pointer_cast<type>(socket_con_type::get_shared());
112  }
113 
114  bool is_secure() const {
115  return socket_con_type::is_secure();
116  }
117 
119 
130  void set_uri(uri_ptr u) {
131  socket_con_type::set_uri(u);
132  }
133 
135 
144  void set_tcp_pre_init_handler(tcp_init_handler h) {
145  m_tcp_pre_init_handler = h;
146  }
147 
149 
158  void set_tcp_init_handler(tcp_init_handler h) {
160  }
161 
163 
173  void set_tcp_post_init_handler(tcp_init_handler h) {
174  m_tcp_post_init_handler = h;
175  }
176 
178 
189  void set_proxy(std::string const & uri, lib::error_code & ec) {
190  // TODO: return errors for illegal URIs here?
191  // TODO: should https urls be illegal for the moment?
192  m_proxy = uri;
193  m_proxy_data = lib::make_shared<proxy_data>();
194  ec = lib::error_code();
195  }
196 
198  void set_proxy(std::string const & uri) {
199  lib::error_code ec;
200  set_proxy(uri,ec);
201  if (ec) { throw exception(ec); }
202  }
203 
205 
217  void set_proxy_basic_auth(std::string const & username, std::string const &
218  password, lib::error_code & ec)
219  {
220  if (!m_proxy_data) {
221  ec = make_error_code(websocketpp::error::invalid_state);
222  return;
223  }
224 
225  // TODO: username can't contain ':'
226  std::string val = "Basic "+base64_encode(username + ":" + password);
227  m_proxy_data->req.replace_header("Proxy-Authorization",val);
228  ec = lib::error_code();
229  }
230 
232  void set_proxy_basic_auth(std::string const & username, std::string const &
233  password)
234  {
235  lib::error_code ec;
236  set_proxy_basic_auth(username,password,ec);
237  if (ec) { throw exception(ec); }
238  }
239 
241 
250  void set_proxy_timeout(long duration, lib::error_code & ec) {
251  if (!m_proxy_data) {
252  ec = make_error_code(websocketpp::error::invalid_state);
253  return;
254  }
255 
256  m_proxy_data->timeout_proxy = duration;
257  ec = lib::error_code();
258  }
259 
261  void set_proxy_timeout(long duration) {
262  lib::error_code ec;
263  set_proxy_timeout(duration,ec);
264  if (ec) { throw exception(ec); }
265  }
266 
267  std::string const & get_proxy() const {
268  return m_proxy;
269  }
270 
272 
281  std::string get_remote_endpoint() const {
282  lib::error_code ec;
283 
284  std::string ret = socket_con_type::get_remote_endpoint(ec);
285 
286  if (ec) {
287  m_elog.write(log::elevel::info,ret);
288  return "Unknown";
289  } else {
290  return ret;
291  }
292  }
293 
296  return m_connection_hdl;
297  }
298 
300 
313  timer_ptr set_timer(long duration, timer_handler callback) {
314  timer_ptr new_timer = lib::make_shared<lib::asio::steady_timer>(
315  lib::ref(*m_io_service),
316  lib::asio::milliseconds(duration)
317  );
318 
319  if (config::enable_multithreading) {
320  new_timer->async_wait(m_strand->wrap(lib::bind(
322  new_timer,
323  callback,
324  lib::placeholders::_1
325  )));
326  } else {
327  new_timer->async_wait(lib::bind(
329  new_timer,
330  callback,
331  lib::placeholders::_1
332  ));
333  }
334 
335  return new_timer;
336  }
337 
339 
349  void handle_timer(timer_ptr, timer_handler callback,
350  lib::asio::error_code const & ec)
351  {
352  if (ec) {
353  if (ec == lib::asio::error::operation_aborted) {
354  callback(make_error_code(transport::error::operation_aborted));
355  } else {
356  log_err(log::elevel::info,"asio handle_timer",ec);
357  callback(make_error_code(error::pass_through));
358  }
359  } else {
360  callback(lib::error_code());
361  }
362  }
363 
365  strand_ptr get_strand() {
366  return m_strand;
367  }
368 
370 
386 protected:
387  void init(init_handler callback) {
388  if (m_alog.static_test(log::alevel::devel)) {
389  m_alog.write(log::alevel::devel,"asio connection init");
390  }
391 
392  // TODO: pre-init timeout. Right now no implemented socket policies
393  // actually have an asyncronous pre-init
394 
395  m_init_handler = callback;
396 
397  socket_con_type::pre_init(
398  lib::bind(
399  &type::handle_pre_init,
400  get_shared(),
401  lib::placeholders::_1
402  )
403  );
404  }
405 
407 
414  lib::error_code proxy_init(std::string const & authority) {
415  if (!m_proxy_data) {
416  return websocketpp::error::make_error_code(
418  }
419  m_proxy_data->req.set_version("HTTP/1.1");
420  m_proxy_data->req.set_method("CONNECT");
421 
422  m_proxy_data->req.set_uri(authority);
423  m_proxy_data->req.replace_header("Host",authority);
424 
425  return lib::error_code();
426  }
427 
429 
438  lib::error_code init_asio (io_service_ptr io_service) {
439  m_io_service = io_service;
440 
441  if (config::enable_multithreading) {
442  m_strand = lib::make_shared<lib::asio::strand>(
443  lib::ref(*io_service));
444 
445  m_async_read_handler = m_strand->wrap(lib::bind(
446  &type::handle_async_read, get_shared(),lib::placeholders::_1,
447  lib::placeholders::_2));
448 
449  m_async_write_handler = m_strand->wrap(lib::bind(
450  &type::handle_async_write, get_shared(),lib::placeholders::_1,
451  lib::placeholders::_2));
452  } else {
453  m_async_read_handler = lib::bind(&type::handle_async_read,
454  get_shared(), lib::placeholders::_1, lib::placeholders::_2);
455 
456  m_async_write_handler = lib::bind(&type::handle_async_write,
457  get_shared(), lib::placeholders::_1, lib::placeholders::_2);
458  }
459 
460  lib::error_code ec = socket_con_type::init_asio(io_service, m_strand,
461  m_is_server);
462 
463  if (ec) {
464  // reset the handlers to break the circular reference:
465  // this->handler->this
466  lib::clear_function(m_async_read_handler);
467  lib::clear_function(m_async_write_handler);
468  }
469 
470  return ec;
471  }
472 
473  void handle_pre_init(lib::error_code const & ec) {
474  if (m_alog.static_test(log::alevel::devel)) {
475  m_alog.write(log::alevel::devel,"asio connection handle pre_init");
476  }
477 
478  if (m_tcp_pre_init_handler) {
479  m_tcp_pre_init_handler(m_connection_hdl);
480  }
481 
482  if (ec) {
483  m_init_handler(ec);
484  }
485 
486  // If we have a proxy set issue a proxy connect, otherwise skip to
487  // post_init
488  if (!m_proxy.empty()) {
489  proxy_write();
490  } else {
491  post_init();
492  }
493  }
494 
495  void post_init() {
496  if (m_alog.static_test(log::alevel::devel)) {
497  m_alog.write(log::alevel::devel,"asio connection post_init");
498  }
499 
500  timer_ptr post_timer;
501 
502  if (config::timeout_socket_post_init > 0) {
503  post_timer = set_timer(
504  config::timeout_socket_post_init,
505  lib::bind(
507  get_shared(),
508  post_timer,
509  m_init_handler,
510  lib::placeholders::_1
511  )
512  );
513  }
514 
515  socket_con_type::post_init(
516  lib::bind(
518  get_shared(),
519  post_timer,
520  m_init_handler,
521  lib::placeholders::_1
522  )
523  );
524  }
525 
527 
535  void handle_post_init_timeout(timer_ptr, init_handler callback,
536  lib::error_code const & ec)
537  {
538  lib::error_code ret_ec;
539 
540  if (ec) {
542  m_alog.write(log::alevel::devel,
543  "asio post init timer cancelled");
544  return;
545  }
546 
547  log_err(log::elevel::devel,"asio handle_post_init_timeout",ec);
548  ret_ec = ec;
549  } else {
550  if (socket_con_type::get_ec()) {
551  ret_ec = socket_con_type::get_ec();
552  } else {
553  ret_ec = make_error_code(transport::error::timeout);
554  }
555  }
556 
557  m_alog.write(log::alevel::devel,"Asio transport post-init timed out");
558  socket_con_type::cancel_socket();
559  callback(ret_ec);
560  }
561 
563 
571  void handle_post_init(timer_ptr post_timer, init_handler callback,
572  lib::error_code const & ec)
573  {
575  (post_timer && lib::asio::is_neg(post_timer->expires_from_now())))
576  {
577  m_alog.write(log::alevel::devel,"post_init cancelled");
578  return;
579  }
580 
581  if (post_timer) {
582  post_timer->cancel();
583  }
584 
585  if (m_alog.static_test(log::alevel::devel)) {
586  m_alog.write(log::alevel::devel,"asio connection handle_post_init");
587  }
588 
589  if (m_tcp_post_init_handler) {
590  m_tcp_post_init_handler(m_connection_hdl);
591  }
592 
593  callback(ec);
594  }
595 
596  void proxy_write() {
597  if (m_alog.static_test(log::alevel::devel)) {
598  m_alog.write(log::alevel::devel,"asio connection proxy_write");
599  }
600 
601  if (!m_proxy_data) {
602  m_elog.write(log::elevel::library,
603  "assertion failed: !m_proxy_data in asio::connection::proxy_write");
604  m_init_handler(make_error_code(error::general));
605  return;
606  }
607 
608  m_proxy_data->write_buf = m_proxy_data->req.raw();
609 
610  m_bufs.push_back(lib::asio::buffer(m_proxy_data->write_buf.data(),
611  m_proxy_data->write_buf.size()));
612 
613  m_alog.write(log::alevel::devel,m_proxy_data->write_buf);
614 
615  // Set a timer so we don't wait forever for the proxy to respond
616  m_proxy_data->timer = this->set_timer(
617  m_proxy_data->timeout_proxy,
618  lib::bind(
619  &type::handle_proxy_timeout,
620  get_shared(),
621  m_init_handler,
622  lib::placeholders::_1
623  )
624  );
625 
626  // Send proxy request
627  if (config::enable_multithreading) {
628  lib::asio::async_write(
629  socket_con_type::get_next_layer(),
630  m_bufs,
631  m_strand->wrap(lib::bind(
632  &type::handle_proxy_write, get_shared(),
633  m_init_handler,
634  lib::placeholders::_1
635  ))
636  );
637  } else {
638  lib::asio::async_write(
639  socket_con_type::get_next_layer(),
640  m_bufs,
641  lib::bind(
642  &type::handle_proxy_write, get_shared(),
643  m_init_handler,
644  lib::placeholders::_1
645  )
646  );
647  }
648  }
649 
650  void handle_proxy_timeout(init_handler callback, lib::error_code const & ec)
651  {
653  m_alog.write(log::alevel::devel,
654  "asio handle_proxy_write timer cancelled");
655  return;
656  } else if (ec) {
657  log_err(log::elevel::devel,"asio handle_proxy_write",ec);
658  callback(ec);
659  } else {
660  m_alog.write(log::alevel::devel,
661  "asio handle_proxy_write timer expired");
662  socket_con_type::cancel_socket();
663  callback(make_error_code(transport::error::timeout));
664  }
665  }
666 
667  void handle_proxy_write(init_handler callback,
668  lib::asio::error_code const & ec)
669  {
670  if (m_alog.static_test(log::alevel::devel)) {
671  m_alog.write(log::alevel::devel,
672  "asio connection handle_proxy_write");
673  }
674 
675  m_bufs.clear();
676 
677  // Timer expired or the operation was aborted for some reason.
678  // Whatever aborted it will be issuing the callback so we are safe to
679  // return
680  if (ec == lib::asio::error::operation_aborted ||
681  lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
682  {
683  m_elog.write(log::elevel::devel,"write operation aborted");
684  return;
685  }
686 
687  if (ec) {
688  log_err(log::elevel::info,"asio handle_proxy_write",ec);
689  m_proxy_data->timer->cancel();
690  callback(make_error_code(error::pass_through));
691  return;
692  }
693 
694  proxy_read(callback);
695  }
696 
697  void proxy_read(init_handler callback) {
698  if (m_alog.static_test(log::alevel::devel)) {
699  m_alog.write(log::alevel::devel,"asio connection proxy_read");
700  }
701 
702  if (!m_proxy_data) {
703  m_elog.write(log::elevel::library,
704  "assertion failed: !m_proxy_data in asio::connection::proxy_read");
705  m_proxy_data->timer->cancel();
706  callback(make_error_code(error::general));
707  return;
708  }
709 
710  if (config::enable_multithreading) {
711  lib::asio::async_read_until(
712  socket_con_type::get_next_layer(),
713  m_proxy_data->read_buf,
714  "\r\n\r\n",
715  m_strand->wrap(lib::bind(
717  callback,
718  lib::placeholders::_1, lib::placeholders::_2
719  ))
720  );
721  } else {
722  lib::asio::async_read_until(
723  socket_con_type::get_next_layer(),
724  m_proxy_data->read_buf,
725  "\r\n\r\n",
726  lib::bind(
728  callback,
729  lib::placeholders::_1, lib::placeholders::_2
730  )
731  );
732  }
733  }
734 
736 
742  lib::asio::error_code const & ec, size_t)
743  {
744  if (m_alog.static_test(log::alevel::devel)) {
745  m_alog.write(log::alevel::devel,
746  "asio connection handle_proxy_read");
747  }
748 
749  // Timer expired or the operation was aborted for some reason.
750  // Whatever aborted it will be issuing the callback so we are safe to
751  // return
752  if (ec == lib::asio::error::operation_aborted ||
753  lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
754  {
755  m_elog.write(log::elevel::devel,"read operation aborted");
756  return;
757  }
758 
759  // At this point there is no need to wait for the timer anymore
760  m_proxy_data->timer->cancel();
761 
762  if (ec) {
763  m_elog.write(log::elevel::info,
764  "asio handle_proxy_read error: "+ec.message());
765  callback(make_error_code(error::pass_through));
766  } else {
767  if (!m_proxy_data) {
768  m_elog.write(log::elevel::library,
769  "assertion failed: !m_proxy_data in asio::connection::handle_proxy_read");
770  callback(make_error_code(error::general));
771  return;
772  }
773 
774  std::istream input(&m_proxy_data->read_buf);
775 
776  m_proxy_data->res.consume(input);
777 
778  if (!m_proxy_data->res.headers_ready()) {
779  // we read until the headers were done in theory but apparently
780  // they aren't. Internal endpoint error.
781  callback(make_error_code(error::general));
782  return;
783  }
784 
785  m_alog.write(log::alevel::devel,m_proxy_data->res.raw());
786 
787  if (m_proxy_data->res.get_status_code() != http::status_code::ok) {
788  // got an error response back
789  // TODO: expose this error in a programmatically accessible way?
790  // if so, see below for an option on how to do this.
791  std::stringstream s;
792  s << "Proxy connection error: "
793  << m_proxy_data->res.get_status_code()
794  << " ("
795  << m_proxy_data->res.get_status_msg()
796  << ")";
797  m_elog.write(log::elevel::info,s.str());
798  callback(make_error_code(error::proxy_failed));
799  return;
800  }
801 
802  // we have successfully established a connection to the proxy, now
803  // we can continue and the proxy will transparently forward the
804  // WebSocket connection.
805 
806  // TODO: decide if we want an on_proxy callback that would allow
807  // access to the proxy response.
808 
809  // free the proxy buffers and req/res objects as they aren't needed
810  // anymore
811  m_proxy_data.reset();
812 
813  // Continue with post proxy initialization
814  post_init();
815  }
816  }
817 
819 
823  void async_read_at_least(size_t num_bytes, char *buf, size_t len,
824  read_handler handler)
825  {
826  if (m_alog.static_test(log::alevel::devel)) {
827  std::stringstream s;
828  s << "asio async_read_at_least: " << num_bytes;
829  m_alog.write(log::alevel::devel,s.str());
830  }
831 
832  if (!m_async_read_handler) {
833  m_alog.write(log::alevel::devel,
834  "async_read_at_least called after async_shutdown");
835  handler(make_error_code(transport::error::action_after_shutdown),0);
836  return;
837  }
838 
839  // TODO: safety vs speed ?
840  // maybe move into an if devel block
841  /*if (num_bytes > len) {
842  m_elog.write(log::elevel::devel,
843  "asio async_read_at_least error::invalid_num_bytes");
844  handler(make_error_code(transport::error::invalid_num_bytes),
845  size_t(0));
846  return;
847  }*/
848 
849  m_read_handler = handler;
850 
851  if (!m_read_handler) {
852  m_alog.write(log::alevel::devel,
853  "asio con async_read_at_least called with bad handler");
854  }
855 
856  lib::asio::async_read(
857  socket_con_type::get_socket(),
858  lib::asio::buffer(buf,len),
859  lib::asio::transfer_at_least(num_bytes),
860  make_custom_alloc_handler(
861  m_read_handler_allocator,
862  m_async_read_handler
863  )
864  );
865  }
866 
867  void handle_async_read(lib::asio::error_code const & ec,
868  size_t bytes_transferred)
869  {
870  m_alog.write(log::alevel::devel, "asio con handle_async_read");
871 
872  // translate asio error codes into more lib::error_codes
873  lib::error_code tec;
874  if (ec == lib::asio::error::eof) {
875  tec = make_error_code(transport::error::eof);
876  } else if (ec) {
877  // We don't know much more about the error at this point. As our
878  // socket/security policy if it knows more:
879  tec = socket_con_type::translate_ec(ec);
880 
881  if (tec == transport::error::tls_error ||
883  {
884  // These are aggregate/catch all errors. Log some human readable
885  // information to the info channel to give library users some
886  // more details about why the upstream method may have failed.
887  log_err(log::elevel::info,"asio async_read_at_least",ec);
888  }
889  }
890  if (m_read_handler) {
891  m_read_handler(tec,bytes_transferred);
892  // TODO: why does this line break things?
893  //m_read_handler = _WEBSOCKETPP_NULL_FUNCTION_;
894  } else {
895  // This can happen in cases where the connection is terminated while
896  // the transport is waiting on a read.
897  m_alog.write(log::alevel::devel,
898  "handle_async_read called with null read handler");
899  }
900  }
901 
902  void async_write(const char* buf, size_t len, write_handler handler) {
903  if (!m_async_write_handler) {
904  m_alog.write(log::alevel::devel,
905  "async_write (single) called after async_shutdown");
906  handler(make_error_code(transport::error::action_after_shutdown));
907  return;
908  }
909 
910  m_bufs.push_back(lib::asio::buffer(buf,len));
911 
912  m_write_handler = handler;
913 
914  lib::asio::async_write(
915  socket_con_type::get_socket(),
916  m_bufs,
917  make_custom_alloc_handler(
918  m_write_handler_allocator,
919  m_async_write_handler
920  )
921  );
922  }
923 
924  void async_write(std::vector<buffer> const & bufs, write_handler handler) {
925  if (!m_async_write_handler) {
926  m_alog.write(log::alevel::devel,
927  "async_write (vector) called after async_shutdown");
928  handler(make_error_code(transport::error::action_after_shutdown));
929  return;
930  }
931  std::vector<buffer>::const_iterator it;
932 
933  for (it = bufs.begin(); it != bufs.end(); ++it) {
934  m_bufs.push_back(lib::asio::buffer((*it).buf,(*it).len));
935  }
936 
937  m_write_handler = handler;
938 
939  lib::asio::async_write(
940  socket_con_type::get_socket(),
941  m_bufs,
942  make_custom_alloc_handler(
943  m_write_handler_allocator,
944  m_async_write_handler
945  )
946  );
947  }
948 
950 
954  void handle_async_write(lib::asio::error_code const & ec, size_t) {
955  m_bufs.clear();
956  lib::error_code tec;
957  if (ec) {
958  log_err(log::elevel::info,"asio async_write",ec);
959  tec = make_error_code(transport::error::pass_through);
960  }
961  if (m_write_handler) {
962  m_write_handler(tec);
963  // TODO: why does this line break things?
964  //m_write_handler = _WEBSOCKETPP_NULL_FUNCTION_;
965  } else {
966  // This can happen in cases where the connection is terminated while
967  // the transport is waiting on a read.
968  m_alog.write(log::alevel::devel,
969  "handle_async_write called with null write handler");
970  }
971  }
972 
974 
981  m_connection_hdl = hdl;
982  socket_con_type::set_handle(hdl);
983  }
984 
986 
989  lib::error_code interrupt(interrupt_handler handler) {
990  if (config::enable_multithreading) {
991  m_io_service->post(m_strand->wrap(handler));
992  } else {
993  m_io_service->post(handler);
994  }
995  return lib::error_code();
996  }
997 
998  lib::error_code dispatch(dispatch_handler handler) {
999  if (config::enable_multithreading) {
1000  m_io_service->post(m_strand->wrap(handler));
1001  } else {
1002  m_io_service->post(handler);
1003  }
1004  return lib::error_code();
1005  }
1006 
1007  /*void handle_interrupt(interrupt_handler handler) {
1008  handler();
1009  }*/
1010 
1013  if (m_alog.static_test(log::alevel::devel)) {
1014  m_alog.write(log::alevel::devel,"asio connection async_shutdown");
1015  }
1016 
1017  // Reset cached handlers now that we won't be reading or writing anymore
1018  // These cached handlers store shared pointers to this connection and
1019  // will leak the connection if not destroyed.
1020  lib::clear_function(m_async_read_handler);
1021  lib::clear_function(m_async_write_handler);
1022  lib::clear_function(m_init_handler);
1023 
1024  lib::clear_function(m_read_handler);
1025  lib::clear_function(m_write_handler);
1026 
1027  timer_ptr shutdown_timer;
1028  shutdown_timer = set_timer(
1029  config::timeout_socket_shutdown,
1030  lib::bind(
1032  get_shared(),
1033  shutdown_timer,
1034  callback,
1035  lib::placeholders::_1
1036  )
1037  );
1038 
1039  socket_con_type::async_shutdown(
1040  lib::bind(
1041  &type::handle_async_shutdown,
1042  get_shared(),
1043  shutdown_timer,
1044  callback,
1045  lib::placeholders::_1
1046  )
1047  );
1048  }
1049 
1051 
1056  void handle_async_shutdown_timeout(timer_ptr, init_handler callback,
1057  lib::error_code const & ec)
1058  {
1059  lib::error_code ret_ec;
1060 
1061  if (ec) {
1063  m_alog.write(log::alevel::devel,
1064  "asio socket shutdown timer cancelled");
1065  return;
1066  }
1067 
1068  log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec);
1069  ret_ec = ec;
1070  } else {
1071  ret_ec = make_error_code(transport::error::timeout);
1072  }
1073 
1074  m_alog.write(log::alevel::devel,
1075  "Asio transport socket shutdown timed out");
1076  socket_con_type::cancel_socket();
1077  callback(ret_ec);
1078  }
1079 
1080  void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler
1081  callback, lib::asio::error_code const & ec)
1082  {
1083  if (ec == lib::asio::error::operation_aborted ||
1084  lib::asio::is_neg(shutdown_timer->expires_from_now()))
1085  {
1086  m_alog.write(log::alevel::devel,"async_shutdown cancelled");
1087  return;
1088  }
1089 
1090  shutdown_timer->cancel();
1091 
1092  lib::error_code tec;
1093  if (ec) {
1094  if (ec == lib::asio::error::not_connected) {
1095  // The socket was already closed when we tried to close it. This
1096  // happens periodically (usually if a read or write fails
1097  // earlier and if it is a real error will be caught at another
1098  // level of the stack.
1099  } else {
1100  // We don't know anything more about this error, give our
1101  // socket/security policy a crack at it.
1102  tec = socket_con_type::translate_ec(ec);
1103 
1104  if (tec == transport::error::tls_short_read) {
1105  // TLS short read at this point is somewhat expected if both
1106  // sides try and end the connection at the same time or if
1107  // SSLv2 is being used. In general there is nothing that can
1108  // be done here other than a low level development log.
1109  } else {
1110  // all other errors are effectively pass through errors of
1111  // some sort so print some detail on the info channel for
1112  // library users to look up if needed.
1113  log_err(log::elevel::info,"asio async_shutdown",ec);
1114  }
1115  }
1116  } else {
1117  if (m_alog.static_test(log::alevel::devel)) {
1118  m_alog.write(log::alevel::devel,
1119  "asio con handle_async_shutdown");
1120  }
1121  }
1122  callback(tec);
1123  }
1124 private:
1126  template <typename error_type>
1127  void log_err(log::level l, const char * msg, const error_type & ec) {
1128  std::stringstream s;
1129  s << msg << " error: " << ec << " (" << ec.message() << ")";
1130  m_elog.write(l,s.str());
1131  }
1132 
1133  // static settings
1134  const bool m_is_server;
1135  alog_type& m_alog;
1136  elog_type& m_elog;
1137 
1138  struct proxy_data {
1139  proxy_data() : timeout_proxy(config::timeout_proxy) {}
1140 
1141  request_type req;
1142  response_type res;
1143  std::string write_buf;
1144  lib::asio::streambuf read_buf;
1145  long timeout_proxy;
1146  timer_ptr timer;
1147  };
1148 
1149  std::string m_proxy;
1150  lib::shared_ptr<proxy_data> m_proxy_data;
1151 
1152  // transport resources
1153  io_service_ptr m_io_service;
1154  strand_ptr m_strand;
1155  connection_hdl m_connection_hdl;
1156 
1157  std::vector<lib::asio::const_buffer> m_bufs;
1158 
1159  // Handlers
1160  tcp_init_handler m_tcp_pre_init_handler;
1161  tcp_init_handler m_tcp_post_init_handler;
1162 
1163  handler_allocator m_read_handler_allocator;
1164  handler_allocator m_write_handler_allocator;
1165 
1166  read_handler m_read_handler;
1167  write_handler m_write_handler;
1168  init_handler m_init_handler;
1169 
1170  async_read_handler m_async_read_handler;
1171  async_write_handler m_async_write_handler;
1172 };
1173 
1174 
1175 } // namespace asio
1176 } // namespace transport
1177 } // namespace websocketpp
1178 
1179 #endif // WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
Asio based endpoint transport component.
Definition: base.hpp:143
config::elog_type elog_type
Type of this transport's error logging policy.
Definition: connection.hpp:81
void set_proxy_basic_auth(std::string const &username, std::string const &password, lib::error_code &ec)
Set the basic auth credentials to use (exception free)
Definition: connection.hpp:217
lib::asio::io_service * io_service_ptr
Type of a pointer to the Asio io_service being used.
Definition: connection.hpp:89
void set_tcp_pre_init_handler(tcp_init_handler h)
Sets the tcp pre init handler.
Definition: connection.hpp:144
strand_ptr get_strand()
Get a pointer to this connection's strand.
Definition: connection.hpp:365
lib::function< void(lib::error_code const &)> write_handler
The type and signature of the callback passed to the write method.
Definition: connection.hpp:123
static level const devel
Low level debugging information (warning: very chatty)
Definition: levels.hpp:63
lib::weak_ptr< void > connection_hdl
A handle to uniquely identify a connection.
socket_con_type::ptr socket_con_ptr
Type of a shared pointer to the socket connection component.
Definition: connection.hpp:77
void handle_post_init_timeout(timer_ptr, init_handler callback, lib::error_code const &ec)
Post init timeout callback.
Definition: connection.hpp:535
lib::function< void()> interrupt_handler
The type and signature of the callback passed to the interrupt method.
Definition: connection.hpp:132
lib::shared_ptr< type > ptr
Type of a shared pointer to this connection transport component.
Definition: connection.hpp:72
lib::function< void(lib::error_code const &, size_t)> read_handler
The type and signature of the callback passed to the read method.
Definition: connection.hpp:120
std::string base64_encode(unsigned char const *input, size_t len)
Encode a char buffer into a base64 string.
Definition: base64.hpp:66
underlying transport pass through
Definition: connection.hpp:153
void init(init_handler callback)
Initialize transport for reading.
Definition: connection.hpp:387
connection_hdl get_handle() const
Get the connection handle.
Definition: connection.hpp:295
void handle_async_shutdown_timeout(timer_ptr, init_handler callback, lib::error_code const &ec)
Async shutdown timeout handler.
void set_tcp_post_init_handler(tcp_init_handler h)
Sets the tcp post init handler.
Definition: connection.hpp:173
static level const devel
Development messages (warning: very chatty)
Definition: levels.hpp:141
std::string get_remote_endpoint() const
Get the remote endpoint address.
Definition: connection.hpp:281
void handle_timer(timer_ptr, timer_handler callback, lib::asio::error_code const &ec)
Timer callback.
Definition: connection.hpp:349
timer_ptr set_timer(long duration, timer_handler callback)
Call back a function after a period of time.
Definition: connection.hpp:313
void set_proxy(std::string const &uri, lib::error_code &ec)
Set the proxy to connect through (exception free)
Definition: connection.hpp:189
config::alog_type alog_type
Type of this transport's access logging policy.
Definition: connection.hpp:79
lib::shared_ptr< lib::asio::io_service::strand > strand_ptr
Type of a pointer to the Asio io_service::strand being used.
Definition: connection.hpp:91
lib::function< void()> dispatch_handler
The type and signature of the callback passed to the dispatch method.
Definition: connection.hpp:135
void handle_proxy_read(init_handler callback, lib::asio::error_code const &ec, size_t)
Proxy read callback.
Definition: connection.hpp:741
The connection was in the wrong state for this operation.
Definition: error.hpp:74
lib::error_code interrupt(interrupt_handler handler)
Trigger the on_interrupt handler.
Definition: connection.hpp:989
static level const info
Definition: levels.hpp:69
void set_proxy_timeout(long duration, lib::error_code &ec)
Set the proxy timeout duration (exception free)
Definition: connection.hpp:250
lib::function< void(lib::error_code const &)> timer_handler
The type and signature of the callback passed to the read method.
Definition: connection.hpp:126
there was an error in the underlying transport library
Definition: base.hpp:174
lib::function< void(lib::error_code const &)> shutdown_handler
The type and signature of the callback passed to the shutdown method.
Definition: connection.hpp:129
Namespace for the WebSocket++ project.
Definition: base64.hpp:41
void set_proxy_timeout(long duration)
Set the proxy timeout duration (exception)
Definition: connection.hpp:261
lib::function< void(lib::error_code const &)> init_handler
The type and signature of the callback passed to the init hook.
Definition: connection.hpp:117
void handle_async_write(lib::asio::error_code const &ec, size_t)
Async write callback.
Definition: connection.hpp:954
Asio based connection transport component.
Definition: connection.hpp:67
lib::shared_ptr< uri > uri_ptr
Pointer to a URI.
Definition: uri.hpp:350
lib::shared_ptr< lib::asio::steady_timer > timer_ptr
Type of a pointer to the Asio timer class.
Definition: connection.hpp:93
The connection to the requested proxy server failed.
Definition: base.hpp:177
ptr get_shared()
Get a shared pointer to this component.
Definition: connection.hpp:110
connection< config > type
Type of this connection transport component.
Definition: connection.hpp:70
void handle_post_init(timer_ptr post_timer, init_handler callback, lib::error_code const &ec)
Post init timeout callback.
Definition: connection.hpp:571
void async_shutdown(shutdown_handler callback)
close and clean up the underlying socket
lib::error_code proxy_init(std::string const &authority)
initialize the proxy buffers and http parsers
Definition: connection.hpp:414
lib::error_code init_asio(io_service_ptr io_service)
Finish constructing the transport.
Definition: connection.hpp:438
config::socket_type::socket_con_type socket_con_type
Type of the socket connection component.
Definition: connection.hpp:75
void set_proxy(std::string const &uri)
Set the proxy to connect through (exception)
Definition: connection.hpp:198
void async_read_at_least(size_t num_bytes, char *buf, size_t len, read_handler handler)
read at least num_bytes bytes into buf and then call handler.
Definition: connection.hpp:823
void set_tcp_init_handler(tcp_init_handler h)
Sets the tcp pre init handler (deprecated)
Definition: connection.hpp:158
static level const library
Definition: levels.hpp:66
void set_handle(connection_hdl hdl)
Set Connection Handle.
Definition: connection.hpp:980
void set_uri(uri_ptr u)
Set uri hook.
Definition: connection.hpp:130
void set_proxy_basic_auth(std::string const &username, std::string const &password)
Set the basic auth credentials to use (exception)
Definition: connection.hpp:232