websocketpp  0.5.1
C++/Boost Asio based websocket client/server library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
connection.hpp
1 /*
2  * Copyright (c) 2014, 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 
41 #include <websocketpp/common/cpp11.hpp>
42 #include <websocketpp/common/memory.hpp>
43 #include <websocketpp/common/functional.hpp>
44 #include <websocketpp/common/connection_hdl.hpp>
45 
46 #include <boost/asio.hpp>
47 #include <boost/system/error_code.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 boost::asio::io_service* io_service_ptr;
91  typedef lib::shared_ptr<boost::asio::io_service::strand> strand_ptr;
93  typedef lib::shared_ptr<boost::asio::deadline_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 
128  void set_tcp_pre_init_handler(tcp_init_handler h) {
129  m_tcp_pre_init_handler = h;
130  }
131 
133 
142  void set_tcp_init_handler(tcp_init_handler h) {
144  }
145 
147 
157  void set_tcp_post_init_handler(tcp_init_handler h) {
158  m_tcp_post_init_handler = h;
159  }
160 
162 
173  void set_proxy(std::string const & uri, lib::error_code & ec) {
174  // TODO: return errors for illegal URIs here?
175  // TODO: should https urls be illegal for the moment?
176  m_proxy = uri;
177  m_proxy_data = lib::make_shared<proxy_data>();
178  ec = lib::error_code();
179  }
180 
182  void set_proxy(std::string const & uri) {
183  lib::error_code ec;
184  set_proxy(uri,ec);
185  if (ec) { throw exception(ec); }
186  }
187 
189 
201  void set_proxy_basic_auth(std::string const & username, std::string const &
202  password, lib::error_code & ec)
203  {
204  if (!m_proxy_data) {
205  ec = make_error_code(websocketpp::error::invalid_state);
206  return;
207  }
208 
209  // TODO: username can't contain ':'
210  std::string val = "Basic "+base64_encode(username + ":" + password);
211  m_proxy_data->req.replace_header("Proxy-Authorization",val);
212  ec = lib::error_code();
213  }
214 
216  void set_proxy_basic_auth(std::string const & username, std::string const &
217  password)
218  {
219  lib::error_code ec;
220  set_proxy_basic_auth(username,password,ec);
221  if (ec) { throw exception(ec); }
222  }
223 
225 
234  void set_proxy_timeout(long duration, lib::error_code & ec) {
235  if (!m_proxy_data) {
236  ec = make_error_code(websocketpp::error::invalid_state);
237  return;
238  }
239 
240  m_proxy_data->timeout_proxy = duration;
241  ec = lib::error_code();
242  }
243 
245  void set_proxy_timeout(long duration) {
246  lib::error_code ec;
247  set_proxy_timeout(duration,ec);
248  if (ec) { throw exception(ec); }
249  }
250 
251  std::string const & get_proxy() const {
252  return m_proxy;
253  }
254 
256 
265  std::string get_remote_endpoint() const {
266  lib::error_code ec;
267 
268  std::string ret = socket_con_type::get_remote_endpoint(ec);
269 
270  if (ec) {
271  m_elog.write(log::elevel::info,ret);
272  return "Unknown";
273  } else {
274  return ret;
275  }
276  }
277 
280  return m_connection_hdl;
281  }
282 
284 
297  timer_ptr set_timer(long duration, timer_handler callback) {
298  timer_ptr new_timer = lib::make_shared<boost::asio::deadline_timer>(
299  lib::ref(*m_io_service),
300  boost::posix_time::milliseconds(duration)
301  );
302 
303  if (config::enable_multithreading) {
304  new_timer->async_wait(m_strand->wrap(lib::bind(
306  new_timer,
307  callback,
308  lib::placeholders::_1
309  )));
310  } else {
311  new_timer->async_wait(lib::bind(
313  new_timer,
314  callback,
315  lib::placeholders::_1
316  ));
317  }
318 
319  return new_timer;
320  }
321 
323 
333  void handle_timer(timer_ptr, timer_handler callback,
334  boost::system::error_code const & ec)
335  {
336  if (ec) {
337  if (ec == boost::asio::error::operation_aborted) {
338  callback(make_error_code(transport::error::operation_aborted));
339  } else {
340  log_err(log::elevel::info,"asio handle_timer",ec);
341  callback(make_error_code(error::pass_through));
342  }
343  } else {
344  callback(lib::error_code());
345  }
346  }
347 protected:
349  strand_ptr get_strand() {
350  return m_strand;
351  }
352 
354 
370  void init(init_handler callback) {
371  if (m_alog.static_test(log::alevel::devel)) {
372  m_alog.write(log::alevel::devel,"asio connection init");
373  }
374 
375  // TODO: pre-init timeout. Right now no implemented socket policies
376  // actually have an asyncronous pre-init
377 
378  m_init_handler = callback;
379 
380  socket_con_type::pre_init(
381  lib::bind(
382  &type::handle_pre_init,
383  get_shared(),
384  lib::placeholders::_1
385  )
386  );
387  }
388 
390 
397  lib::error_code proxy_init(std::string const & authority) {
398  if (!m_proxy_data) {
399  return websocketpp::error::make_error_code(
401  }
402  m_proxy_data->req.set_version("HTTP/1.1");
403  m_proxy_data->req.set_method("CONNECT");
404 
405  m_proxy_data->req.set_uri(authority);
406  m_proxy_data->req.replace_header("Host",authority);
407 
408  return lib::error_code();
409  }
410 
412 
421  lib::error_code init_asio (io_service_ptr io_service) {
422  m_io_service = io_service;
423 
424  if (config::enable_multithreading) {
425  m_strand = lib::make_shared<boost::asio::strand>(
426  lib::ref(*io_service));
427 
428  m_async_read_handler = m_strand->wrap(lib::bind(
429  &type::handle_async_read, get_shared(),lib::placeholders::_1,
430  lib::placeholders::_2));
431 
432  m_async_write_handler = m_strand->wrap(lib::bind(
433  &type::handle_async_write, get_shared(),lib::placeholders::_1,
434  lib::placeholders::_2));
435  } else {
436  m_async_read_handler = lib::bind(&type::handle_async_read,
437  get_shared(), lib::placeholders::_1, lib::placeholders::_2);
438 
439  m_async_write_handler = lib::bind(&type::handle_async_write,
440  get_shared(), lib::placeholders::_1, lib::placeholders::_2);
441  }
442 
443  lib::error_code ec = socket_con_type::init_asio(io_service, m_strand,
444  m_is_server);
445 
446  if (ec) {
447  // reset the handlers to break the circular reference:
448  // this->handler->this
449  lib::clear_function(m_async_read_handler);
450  lib::clear_function(m_async_write_handler);
451  }
452 
453  return ec;
454  }
455 
456  void handle_pre_init(lib::error_code const & ec) {
457  if (m_alog.static_test(log::alevel::devel)) {
458  m_alog.write(log::alevel::devel,"asio connection handle pre_init");
459  }
460 
461  if (m_tcp_pre_init_handler) {
462  m_tcp_pre_init_handler(m_connection_hdl);
463  }
464 
465  if (ec) {
466  m_init_handler(ec);
467  }
468 
469  // If we have a proxy set issue a proxy connect, otherwise skip to
470  // post_init
471  if (!m_proxy.empty()) {
472  proxy_write();
473  } else {
474  post_init();
475  }
476  }
477 
478  void post_init() {
479  if (m_alog.static_test(log::alevel::devel)) {
480  m_alog.write(log::alevel::devel,"asio connection post_init");
481  }
482 
483  timer_ptr post_timer;
484 
485  if (config::timeout_socket_post_init > 0) {
486  post_timer = set_timer(
487  config::timeout_socket_post_init,
488  lib::bind(
490  get_shared(),
491  post_timer,
492  m_init_handler,
493  lib::placeholders::_1
494  )
495  );
496  }
497 
498  socket_con_type::post_init(
499  lib::bind(
501  get_shared(),
502  post_timer,
503  m_init_handler,
504  lib::placeholders::_1
505  )
506  );
507  }
508 
510 
518  void handle_post_init_timeout(timer_ptr, init_handler callback,
519  lib::error_code const & ec)
520  {
521  lib::error_code ret_ec;
522 
523  if (ec) {
525  m_alog.write(log::alevel::devel,
526  "asio post init timer cancelled");
527  return;
528  }
529 
530  log_err(log::elevel::devel,"asio handle_post_init_timeout",ec);
531  ret_ec = ec;
532  } else {
533  if (socket_con_type::get_ec()) {
534  ret_ec = socket_con_type::get_ec();
535  } else {
536  ret_ec = make_error_code(transport::error::timeout);
537  }
538  }
539 
540  m_alog.write(log::alevel::devel,"Asio transport post-init timed out");
541  socket_con_type::cancel_socket();
542  callback(ret_ec);
543  }
544 
546 
554  void handle_post_init(timer_ptr post_timer, init_handler callback,
555  lib::error_code const & ec)
556  {
558  (post_timer && post_timer->expires_from_now().is_negative()))
559  {
560  m_alog.write(log::alevel::devel,"post_init cancelled");
561  return;
562  }
563 
564  if (post_timer) {
565  post_timer->cancel();
566  }
567 
568  if (m_alog.static_test(log::alevel::devel)) {
569  m_alog.write(log::alevel::devel,"asio connection handle_post_init");
570  }
571 
572  if (m_tcp_post_init_handler) {
573  m_tcp_post_init_handler(m_connection_hdl);
574  }
575 
576  callback(ec);
577  }
578 
579  void proxy_write() {
580  if (m_alog.static_test(log::alevel::devel)) {
581  m_alog.write(log::alevel::devel,"asio connection proxy_write");
582  }
583 
584  if (!m_proxy_data) {
585  m_elog.write(log::elevel::library,
586  "assertion failed: !m_proxy_data in asio::connection::proxy_write");
587  m_init_handler(make_error_code(error::general));
588  return;
589  }
590 
591  m_proxy_data->write_buf = m_proxy_data->req.raw();
592 
593  m_bufs.push_back(boost::asio::buffer(m_proxy_data->write_buf.data(),
594  m_proxy_data->write_buf.size()));
595 
596  m_alog.write(log::alevel::devel,m_proxy_data->write_buf);
597 
598  // Set a timer so we don't wait forever for the proxy to respond
599  m_proxy_data->timer = this->set_timer(
600  m_proxy_data->timeout_proxy,
601  lib::bind(
602  &type::handle_proxy_timeout,
603  get_shared(),
604  m_init_handler,
605  lib::placeholders::_1
606  )
607  );
608 
609  // Send proxy request
610  if (config::enable_multithreading) {
611  boost::asio::async_write(
612  socket_con_type::get_next_layer(),
613  m_bufs,
614  m_strand->wrap(lib::bind(
615  &type::handle_proxy_write, get_shared(),
616  m_init_handler,
617  lib::placeholders::_1
618  ))
619  );
620  } else {
621  boost::asio::async_write(
622  socket_con_type::get_next_layer(),
623  m_bufs,
624  lib::bind(
625  &type::handle_proxy_write, get_shared(),
626  m_init_handler,
627  lib::placeholders::_1
628  )
629  );
630  }
631  }
632 
633  void handle_proxy_timeout(init_handler callback, lib::error_code const & ec)
634  {
636  m_alog.write(log::alevel::devel,
637  "asio handle_proxy_write timer cancelled");
638  return;
639  } else if (ec) {
640  log_err(log::elevel::devel,"asio handle_proxy_write",ec);
641  callback(ec);
642  } else {
643  m_alog.write(log::alevel::devel,
644  "asio handle_proxy_write timer expired");
645  socket_con_type::cancel_socket();
646  callback(make_error_code(transport::error::timeout));
647  }
648  }
649 
650  void handle_proxy_write(init_handler callback,
651  boost::system::error_code const & ec)
652  {
653  if (m_alog.static_test(log::alevel::devel)) {
654  m_alog.write(log::alevel::devel,
655  "asio connection handle_proxy_write");
656  }
657 
658  m_bufs.clear();
659 
660  // Timer expired or the operation was aborted for some reason.
661  // Whatever aborted it will be issuing the callback so we are safe to
662  // return
663  if (ec == boost::asio::error::operation_aborted ||
664  m_proxy_data->timer->expires_from_now().is_negative())
665  {
666  m_elog.write(log::elevel::devel,"write operation aborted");
667  return;
668  }
669 
670  if (ec) {
671  log_err(log::elevel::info,"asio handle_proxy_write",ec);
672  m_proxy_data->timer->cancel();
673  callback(make_error_code(error::pass_through));
674  return;
675  }
676 
677  proxy_read(callback);
678  }
679 
680  void proxy_read(init_handler callback) {
681  if (m_alog.static_test(log::alevel::devel)) {
682  m_alog.write(log::alevel::devel,"asio connection proxy_read");
683  }
684 
685  if (!m_proxy_data) {
686  m_elog.write(log::elevel::library,
687  "assertion failed: !m_proxy_data in asio::connection::proxy_read");
688  m_proxy_data->timer->cancel();
689  callback(make_error_code(error::general));
690  return;
691  }
692 
693  if (config::enable_multithreading) {
694  boost::asio::async_read_until(
695  socket_con_type::get_next_layer(),
696  m_proxy_data->read_buf,
697  "\r\n\r\n",
698  m_strand->wrap(lib::bind(
700  callback,
701  lib::placeholders::_1, lib::placeholders::_2
702  ))
703  );
704  } else {
705  boost::asio::async_read_until(
706  socket_con_type::get_next_layer(),
707  m_proxy_data->read_buf,
708  "\r\n\r\n",
709  lib::bind(
711  callback,
712  lib::placeholders::_1, lib::placeholders::_2
713  )
714  );
715  }
716  }
717 
719 
725  boost::system::error_code const & ec, size_t)
726  {
727  if (m_alog.static_test(log::alevel::devel)) {
728  m_alog.write(log::alevel::devel,
729  "asio connection handle_proxy_read");
730  }
731 
732  // Timer expired or the operation was aborted for some reason.
733  // Whatever aborted it will be issuing the callback so we are safe to
734  // return
735  if (ec == boost::asio::error::operation_aborted ||
736  m_proxy_data->timer->expires_from_now().is_negative())
737  {
738  m_elog.write(log::elevel::devel,"read operation aborted");
739  return;
740  }
741 
742  // At this point there is no need to wait for the timer anymore
743  m_proxy_data->timer->cancel();
744 
745  if (ec) {
746  m_elog.write(log::elevel::info,
747  "asio handle_proxy_read error: "+ec.message());
748  callback(make_error_code(error::pass_through));
749  } else {
750  if (!m_proxy_data) {
751  m_elog.write(log::elevel::library,
752  "assertion failed: !m_proxy_data in asio::connection::handle_proxy_read");
753  callback(make_error_code(error::general));
754  return;
755  }
756 
757  std::istream input(&m_proxy_data->read_buf);
758 
759  m_proxy_data->res.consume(input);
760 
761  if (!m_proxy_data->res.headers_ready()) {
762  // we read until the headers were done in theory but apparently
763  // they aren't. Internal endpoint error.
764  callback(make_error_code(error::general));
765  return;
766  }
767 
768  m_alog.write(log::alevel::devel,m_proxy_data->res.raw());
769 
770  if (m_proxy_data->res.get_status_code() != http::status_code::ok) {
771  // got an error response back
772  // TODO: expose this error in a programmatically accessible way?
773  // if so, see below for an option on how to do this.
774  std::stringstream s;
775  s << "Proxy connection error: "
776  << m_proxy_data->res.get_status_code()
777  << " ("
778  << m_proxy_data->res.get_status_msg()
779  << ")";
780  m_elog.write(log::elevel::info,s.str());
781  callback(make_error_code(error::proxy_failed));
782  return;
783  }
784 
785  // we have successfully established a connection to the proxy, now
786  // we can continue and the proxy will transparently forward the
787  // WebSocket connection.
788 
789  // TODO: decide if we want an on_proxy callback that would allow
790  // access to the proxy response.
791 
792  // free the proxy buffers and req/res objects as they aren't needed
793  // anymore
794  m_proxy_data.reset();
795 
796  // Continue with post proxy initialization
797  post_init();
798  }
799  }
800 
802 
806  void async_read_at_least(size_t num_bytes, char *buf, size_t len,
807  read_handler handler)
808  {
809  if (m_alog.static_test(log::alevel::devel)) {
810  std::stringstream s;
811  s << "asio async_read_at_least: " << num_bytes;
812  m_alog.write(log::alevel::devel,s.str());
813  }
814 
815  if (!m_async_read_handler) {
816  m_alog.write(log::alevel::devel,
817  "async_read_at_least called after async_shutdown");
818  handler(make_error_code(transport::error::action_after_shutdown),0);
819  return;
820  }
821 
822  // TODO: safety vs speed ?
823  // maybe move into an if devel block
824  /*if (num_bytes > len) {
825  m_elog.write(log::elevel::devel,
826  "asio async_read_at_least error::invalid_num_bytes");
827  handler(make_error_code(transport::error::invalid_num_bytes),
828  size_t(0));
829  return;
830  }*/
831 
832  m_read_handler = handler;
833 
834  if (!m_read_handler) {
835  m_alog.write(log::alevel::devel,
836  "asio con async_read_at_least called with bad handler");
837  }
838 
839  boost::asio::async_read(
840  socket_con_type::get_socket(),
841  boost::asio::buffer(buf,len),
842  boost::asio::transfer_at_least(num_bytes),
843  make_custom_alloc_handler(
844  m_read_handler_allocator,
845  m_async_read_handler
846  )
847  );
848  }
849 
850  void handle_async_read(boost::system::error_code const & ec,
851  size_t bytes_transferred)
852  {
853  m_alog.write(log::alevel::devel, "asio con handle_async_read");
854 
855  // translate boost error codes into more lib::error_codes
856  lib::error_code tec;
857  if (ec == boost::asio::error::eof) {
858  tec = make_error_code(transport::error::eof);
859  } else if (ec) {
860  // We don't know much more about the error at this point. As our
861  // socket/security policy if it knows more:
862  tec = socket_con_type::translate_ec(ec);
863 
864  if (tec == transport::error::tls_error ||
866  {
867  // These are aggregate/catch all errors. Log some human readable
868  // information to the info channel to give library users some
869  // more details about why the upstream method may have failed.
870  log_err(log::elevel::info,"asio async_read_at_least",ec);
871  }
872  }
873  if (m_read_handler) {
874  m_read_handler(tec,bytes_transferred);
875  // TODO: why does this line break things?
876  //m_read_handler = _WEBSOCKETPP_NULL_FUNCTION_;
877  } else {
878  // This can happen in cases where the connection is terminated while
879  // the transport is waiting on a read.
880  m_alog.write(log::alevel::devel,
881  "handle_async_read called with null read handler");
882  }
883  }
884 
885  void async_write(const char* buf, size_t len, write_handler handler) {
886  if (!m_async_write_handler) {
887  m_alog.write(log::alevel::devel,
888  "async_write (single) called after async_shutdown");
889  handler(make_error_code(transport::error::action_after_shutdown));
890  return;
891  }
892 
893  m_bufs.push_back(boost::asio::buffer(buf,len));
894 
895  m_write_handler = handler;
896 
897  boost::asio::async_write(
898  socket_con_type::get_socket(),
899  m_bufs,
900  make_custom_alloc_handler(
901  m_write_handler_allocator,
902  m_async_write_handler
903  )
904  );
905  }
906 
907  void async_write(std::vector<buffer> const & bufs, write_handler handler) {
908  if (!m_async_write_handler) {
909  m_alog.write(log::alevel::devel,
910  "async_write (vector) called after async_shutdown");
911  handler(make_error_code(transport::error::action_after_shutdown));
912  return;
913  }
914  std::vector<buffer>::const_iterator it;
915 
916  for (it = bufs.begin(); it != bufs.end(); ++it) {
917  m_bufs.push_back(boost::asio::buffer((*it).buf,(*it).len));
918  }
919 
920  m_write_handler = handler;
921 
922  boost::asio::async_write(
923  socket_con_type::get_socket(),
924  m_bufs,
925  make_custom_alloc_handler(
926  m_write_handler_allocator,
927  m_async_write_handler
928  )
929  );
930  }
931 
933 
937  void handle_async_write(boost::system::error_code const & ec, size_t) {
938  m_bufs.clear();
939  lib::error_code tec;
940  if (ec) {
941  log_err(log::elevel::info,"asio async_write",ec);
942  tec = make_error_code(transport::error::pass_through);
943  }
944  if (m_write_handler) {
945  m_write_handler(tec);
946  // TODO: why does this line break things?
947  //m_write_handler = _WEBSOCKETPP_NULL_FUNCTION_;
948  } else {
949  // This can happen in cases where the connection is terminated while
950  // the transport is waiting on a read.
951  m_alog.write(log::alevel::devel,
952  "handle_async_write called with null write handler");
953  }
954  }
955 
957 
964  m_connection_hdl = hdl;
965  socket_con_type::set_handle(hdl);
966  }
967 
969 
972  lib::error_code interrupt(interrupt_handler handler) {
973  if (config::enable_multithreading) {
974  m_io_service->post(m_strand->wrap(handler));
975  } else {
976  m_io_service->post(handler);
977  }
978  return lib::error_code();
979  }
980 
981  lib::error_code dispatch(dispatch_handler handler) {
982  if (config::enable_multithreading) {
983  m_io_service->post(m_strand->wrap(handler));
984  } else {
985  m_io_service->post(handler);
986  }
987  return lib::error_code();
988  }
989 
990  /*void handle_interrupt(interrupt_handler handler) {
991  handler();
992  }*/
993 
996  if (m_alog.static_test(log::alevel::devel)) {
997  m_alog.write(log::alevel::devel,"asio connection async_shutdown");
998  }
999 
1000  // Reset cached handlers now that we won't be reading or writing anymore
1001  // These cached handlers store shared pointers to this connection and
1002  // will leak the connection if not destroyed.
1003  lib::clear_function(m_async_read_handler);
1004  lib::clear_function(m_async_write_handler);
1005  lib::clear_function(m_init_handler);
1006 
1007  lib::clear_function(m_read_handler);
1008  lib::clear_function(m_write_handler);
1009 
1010  timer_ptr shutdown_timer;
1011  shutdown_timer = set_timer(
1012  config::timeout_socket_shutdown,
1013  lib::bind(
1015  get_shared(),
1016  shutdown_timer,
1017  callback,
1018  lib::placeholders::_1
1019  )
1020  );
1021 
1022  socket_con_type::async_shutdown(
1023  lib::bind(
1024  &type::handle_async_shutdown,
1025  get_shared(),
1026  shutdown_timer,
1027  callback,
1028  lib::placeholders::_1
1029  )
1030  );
1031  }
1032 
1034 
1039  void handle_async_shutdown_timeout(timer_ptr, init_handler callback,
1040  lib::error_code const & ec)
1041  {
1042  lib::error_code ret_ec;
1043 
1044  if (ec) {
1046  m_alog.write(log::alevel::devel,
1047  "asio socket shutdown timer cancelled");
1048  return;
1049  }
1050 
1051  log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec);
1052  ret_ec = ec;
1053  } else {
1054  ret_ec = make_error_code(transport::error::timeout);
1055  }
1056 
1057  m_alog.write(log::alevel::devel,
1058  "Asio transport socket shutdown timed out");
1059  socket_con_type::cancel_socket();
1060  callback(ret_ec);
1061  }
1062 
1063  void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler
1064  callback, boost::system::error_code const & ec)
1065  {
1066  if (ec == boost::asio::error::operation_aborted ||
1067  shutdown_timer->expires_from_now().is_negative())
1068  {
1069  m_alog.write(log::alevel::devel,"async_shutdown cancelled");
1070  return;
1071  }
1072 
1073  shutdown_timer->cancel();
1074 
1075  lib::error_code tec;
1076  if (ec) {
1077  if (ec == boost::asio::error::not_connected) {
1078  // The socket was already closed when we tried to close it. This
1079  // happens periodically (usually if a read or write fails
1080  // earlier and if it is a real error will be caught at another
1081  // level of the stack.
1082  } else {
1083  // We don't know anything more about this error, give our
1084  // socket/security policy a crack at it.
1085  tec = socket_con_type::translate_ec(ec);
1086 
1087  if (tec == transport::error::tls_short_read) {
1088  // TLS short read at this point is somewhat expected if both
1089  // sides try and end the connection at the same time or if
1090  // SSLv2 is being used. In general there is nothing that can
1091  // be done here other than a low level development log.
1092  } else {
1093  // all other errors are effectively pass through errors of
1094  // some sort so print some detail on the info channel for
1095  // library users to look up if needed.
1096  log_err(log::elevel::info,"asio async_shutdown",ec);
1097  }
1098  }
1099  } else {
1100  if (m_alog.static_test(log::alevel::devel)) {
1101  m_alog.write(log::alevel::devel,
1102  "asio con handle_async_shutdown");
1103  }
1104  }
1105  callback(tec);
1106  }
1107 private:
1109  template <typename error_type>
1110  void log_err(log::level l, const char * msg, const error_type & ec) {
1111  std::stringstream s;
1112  s << msg << " error: " << ec << " (" << ec.message() << ")";
1113  m_elog.write(l,s.str());
1114  }
1115 
1116  // static settings
1117  const bool m_is_server;
1118  alog_type& m_alog;
1119  elog_type& m_elog;
1120 
1121  struct proxy_data {
1122  proxy_data() : timeout_proxy(config::timeout_proxy) {}
1123 
1124  request_type req;
1125  response_type res;
1126  std::string write_buf;
1127  boost::asio::streambuf read_buf;
1128  long timeout_proxy;
1129  timer_ptr timer;
1130  };
1131 
1132  std::string m_proxy;
1133  lib::shared_ptr<proxy_data> m_proxy_data;
1134 
1135  // transport resources
1136  io_service_ptr m_io_service;
1137  strand_ptr m_strand;
1138  connection_hdl m_connection_hdl;
1139 
1140  std::vector<boost::asio::const_buffer> m_bufs;
1141 
1142  // Handlers
1143  tcp_init_handler m_tcp_pre_init_handler;
1144  tcp_init_handler m_tcp_post_init_handler;
1145 
1146  handler_allocator m_read_handler_allocator;
1147  handler_allocator m_write_handler_allocator;
1148 
1149  read_handler m_read_handler;
1150  write_handler m_write_handler;
1151  init_handler m_init_handler;
1152 
1153  async_read_handler m_async_read_handler;
1154  async_write_handler m_async_write_handler;
1155 };
1156 
1157 
1158 } // namespace asio
1159 } // namespace transport
1160 } // namespace websocketpp
1161 
1162 #endif // WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
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:201
void handle_async_write(boost::system::error_code const &ec, size_t)
Async write callback.
Definition: connection.hpp:937
void set_tcp_pre_init_handler(tcp_init_handler h)
Sets the tcp pre init handler.
Definition: connection.hpp:128
strand_ptr get_strand()
Get a pointer to this connection's strand.
Definition: connection.hpp:349
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.
boost::asio::io_service * io_service_ptr
Type of a pointer to the ASIO io_service being used.
Definition: connection.hpp:89
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:518
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
lib::shared_ptr< boost::asio::io_service::strand > strand_ptr
Type of a pointer to the ASIO io_service::strand being used.
Definition: connection.hpp:91
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:370
connection_hdl get_handle() const
Get the connection handle.
Definition: connection.hpp:279
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:157
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:265
timer_ptr set_timer(long duration, timer_handler callback)
Call back a function after a period of time.
Definition: connection.hpp:297
void set_proxy(std::string const &uri, lib::error_code &ec)
Set the proxy to connect through (exception free)
Definition: connection.hpp:173
config::alog_type alog_type
Type of this transport's access logging policy.
Definition: connection.hpp:79
lib::function< void()> dispatch_handler
The type and signature of the callback passed to the dispatch method.
Definition: connection.hpp:135
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:972
void handle_proxy_read(init_handler callback, boost::system::error_code const &ec, size_t)
Proxy read callback.
Definition: connection.hpp:724
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:234
lib::shared_ptr< boost::asio::deadline_timer > timer_ptr
Type of a pointer to the ASIO timer class.
Definition: connection.hpp:93
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:190
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:245
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_timer(timer_ptr, timer_handler callback, boost::system::error_code const &ec)
Timer callback.
Definition: connection.hpp:333
Boost Asio based connection transport component.
Definition: connection.hpp:67
Boost Asio based endpoint transport component.
Definition: base.hpp:159
The connection to the requested proxy server failed.
Definition: base.hpp:193
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:554
void async_shutdown(shutdown_handler callback)
close and clean up the underlying socket
Definition: connection.hpp:995
lib::error_code proxy_init(std::string const &authority)
initialize the proxy buffers and http parsers
Definition: connection.hpp:397
lib::error_code init_asio(io_service_ptr io_service)
Finish constructing the transport.
Definition: connection.hpp:421
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:182
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:806
void set_tcp_init_handler(tcp_init_handler h)
Sets the tcp pre init handler (deprecated)
Definition: connection.hpp:142
static level const library
Definition: levels.hpp:66
void set_handle(connection_hdl hdl)
Set Connection Handle.
Definition: connection.hpp:963
void set_proxy_basic_auth(std::string const &username, std::string const &password)
Set the basic auth credentials to use (exception)
Definition: connection.hpp:216