websocketpp  0.4.0
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/common/cpp11.hpp>
32 #include <websocketpp/common/memory.hpp>
33 #include <websocketpp/common/functional.hpp>
34 #include <websocketpp/common/connection_hdl.hpp>
35 #include <websocketpp/logger/levels.hpp>
36 #include <websocketpp/http/constants.hpp>
37 #include <websocketpp/transport/asio/base.hpp>
38 #include <websocketpp/transport/base/connection.hpp>
39 
40 #include <websocketpp/base64/base64.hpp>
41 #include <websocketpp/error.hpp>
42 
43 #include <boost/asio.hpp>
44 #include <boost/system/error_code.hpp>
45 
46 #include <sstream>
47 #include <vector>
48 
49 namespace websocketpp {
50 namespace transport {
51 namespace asio {
52 
53 typedef lib::function<void(connection_hdl)> tcp_init_handler;
54 
56 
61 template <typename config>
62 class connection : public config::socket_type::socket_con_type {
63 public:
67  typedef lib::shared_ptr<type> ptr;
68 
70  typedef typename config::socket_type::socket_con_type socket_con_type;
72  typedef typename socket_con_type::ptr socket_con_ptr;
74  typedef typename config::alog_type alog_type;
76  typedef typename config::elog_type elog_type;
77 
78  typedef typename config::request_type request_type;
79  typedef typename request_type::ptr request_ptr;
80  typedef typename config::response_type response_type;
81  typedef typename response_type::ptr response_ptr;
82 
84  typedef boost::asio::io_service* io_service_ptr;
86  typedef lib::shared_ptr<boost::asio::io_service::strand> strand_ptr;
88  typedef lib::shared_ptr<boost::asio::deadline_timer> timer_ptr;
89 
90  // connection is friends with its associated endpoint to allow the endpoint
91  // to call private/protected utility methods that we don't want to expose
92  // to the public api.
93  friend class endpoint<config>;
94 
95  // generate and manage our own io_service
96  explicit connection(bool is_server, alog_type& alog, elog_type& elog)
97  : m_is_server(is_server)
98  , m_alog(alog)
99  , m_elog(elog)
100  {
101  m_alog.write(log::alevel::devel,"asio con transport constructor");
102  }
103 
105  ptr get_shared() {
106  return lib::static_pointer_cast<type>(socket_con_type::get_shared());
107  }
108 
109  bool is_secure() const {
110  return socket_con_type::is_secure();
111  }
112 
114 
123  void set_tcp_pre_init_handler(tcp_init_handler h) {
124  m_tcp_pre_init_handler = h;
125  }
126 
128 
137  void set_tcp_init_handler(tcp_init_handler h) {
139  }
140 
142 
152  void set_tcp_post_init_handler(tcp_init_handler h) {
153  m_tcp_post_init_handler = h;
154  }
155 
157 
168  void set_proxy(std::string const & uri, lib::error_code & ec) {
169  // TODO: return errors for illegal URIs here?
170  // TODO: should https urls be illegal for the moment?
171  m_proxy = uri;
172  m_proxy_data = lib::make_shared<proxy_data>();
173  ec = lib::error_code();
174  }
175 
177  void set_proxy(std::string const & uri) {
178  lib::error_code ec;
179  set_proxy(uri,ec);
180  if (ec) { throw exception(ec); }
181  }
182 
184 
196  void set_proxy_basic_auth(std::string const & username, std::string const &
197  password, lib::error_code & ec)
198  {
199  if (!m_proxy_data) {
200  ec = make_error_code(websocketpp::error::invalid_state);
201  return;
202  }
203 
204  // TODO: username can't contain ':'
205  std::string val = "Basic "+base64_encode(username + ":" + password);
206  m_proxy_data->req.replace_header("Proxy-Authorization",val);
207  ec = lib::error_code();
208  }
209 
211  void set_proxy_basic_auth(std::string const & username, std::string const &
212  password)
213  {
214  lib::error_code ec;
215  set_proxy_basic_auth(username,password,ec);
216  if (ec) { throw exception(ec); }
217  }
218 
220 
229  void set_proxy_timeout(long duration, lib::error_code & ec) {
230  if (!m_proxy_data) {
231  ec = make_error_code(websocketpp::error::invalid_state);
232  return;
233  }
234 
235  m_proxy_data->timeout_proxy = duration;
236  ec = lib::error_code();
237  }
238 
240  void set_proxy_timeout(long duration) {
241  lib::error_code ec;
242  set_proxy_timeout(duration,ec);
243  if (ec) { throw exception(ec); }
244  }
245 
246  const std::string & get_proxy() const {
247  return m_proxy;
248  }
249 
251 
260  std::string get_remote_endpoint() const {
261  lib::error_code ec;
262 
263  std::string ret = socket_con_type::get_remote_endpoint(ec);
264 
265  if (ec) {
266  m_elog.write(log::elevel::info,ret);
267  return "Unknown";
268  } else {
269  return ret;
270  }
271  }
272 
275  return m_connection_hdl;
276  }
277 
279 
292  timer_ptr set_timer(long duration, timer_handler callback) {
293  timer_ptr new_timer = lib::make_shared<boost::asio::deadline_timer>(
294  lib::ref(*m_io_service),
295  boost::posix_time::milliseconds(duration)
296  );
297 
298  if (config::enable_multithreading) {
299  new_timer->async_wait(m_strand->wrap(lib::bind(
301  new_timer,
302  callback,
303  lib::placeholders::_1
304  )));
305  } else {
306  new_timer->async_wait(lib::bind(
308  new_timer,
309  callback,
310  lib::placeholders::_1
311  ));
312  }
313 
314  return new_timer;
315  }
316 
318 
328  void handle_timer(timer_ptr, timer_handler callback,
329  boost::system::error_code const & ec)
330  {
331  if (ec) {
332  if (ec == boost::asio::error::operation_aborted) {
333  callback(make_error_code(transport::error::operation_aborted));
334  } else {
335  log_err(log::elevel::info,"asio handle_timer",ec);
336  callback(make_error_code(error::pass_through));
337  }
338  } else {
339  callback(lib::error_code());
340  }
341  }
342 protected:
344  strand_ptr get_strand() {
345  return m_strand;
346  }
347 
349 
365  void init(init_handler callback) {
366  if (m_alog.static_test(log::alevel::devel)) {
367  m_alog.write(log::alevel::devel,"asio connection init");
368  }
369 
370  // TODO: pre-init timeout. Right now no implemented socket policies
371  // actually have an asyncronous pre-init
372 
373  m_init_handler = callback;
374 
375  socket_con_type::pre_init(
376  lib::bind(
377  &type::handle_pre_init,
378  get_shared(),
379  lib::placeholders::_1
380  )
381  );
382  }
383 
385 
392  lib::error_code proxy_init(std::string const & authority) {
393  if (!m_proxy_data) {
394  return websocketpp::error::make_error_code(
396  }
397  m_proxy_data->req.set_version("HTTP/1.1");
398  m_proxy_data->req.set_method("CONNECT");
399 
400  m_proxy_data->req.set_uri(authority);
401  m_proxy_data->req.replace_header("Host",authority);
402 
403  return lib::error_code();
404  }
405 
407 
416  lib::error_code init_asio (io_service_ptr io_service) {
417  m_io_service = io_service;
418 
419  if (config::enable_multithreading) {
420  m_strand = lib::make_shared<boost::asio::strand>(
421  lib::ref(*io_service));
422 
423  m_async_read_handler = m_strand->wrap(lib::bind(
424  &type::handle_async_read, get_shared(),lib::placeholders::_1,
425  lib::placeholders::_2));
426 
427  m_async_write_handler = m_strand->wrap(lib::bind(
428  &type::handle_async_write, get_shared(),lib::placeholders::_1,
429  lib::placeholders::_2));
430  } else {
431  m_async_read_handler = lib::bind(&type::handle_async_read,
432  get_shared(), lib::placeholders::_1, lib::placeholders::_2);
433 
434  m_async_write_handler = lib::bind(&type::handle_async_write,
435  get_shared(), lib::placeholders::_1, lib::placeholders::_2);
436  }
437 
438  lib::error_code ec = socket_con_type::init_asio(io_service, m_strand,
439  m_is_server);
440 
441  if (ec) {
442  // reset the handlers to break the circular reference:
443  // this->handler->this
444  lib::clear_function(m_async_read_handler);
445  lib::clear_function(m_async_write_handler);
446  }
447 
448  return ec;
449  }
450 
451  void handle_pre_init(lib::error_code const & ec) {
452  if (m_alog.static_test(log::alevel::devel)) {
453  m_alog.write(log::alevel::devel,"asio connection handle pre_init");
454  }
455 
456  if (m_tcp_pre_init_handler) {
457  m_tcp_pre_init_handler(m_connection_hdl);
458  }
459 
460  if (ec) {
461  m_init_handler(ec);
462  }
463 
464  // If we have a proxy set issue a proxy connect, otherwise skip to
465  // post_init
466  if (!m_proxy.empty()) {
467  proxy_write();
468  } else {
469  post_init();
470  }
471  }
472 
473  void post_init() {
474  if (m_alog.static_test(log::alevel::devel)) {
475  m_alog.write(log::alevel::devel,"asio connection post_init");
476  }
477 
478  timer_ptr post_timer;
479 
480  if (config::timeout_socket_post_init > 0) {
481  post_timer = set_timer(
482  config::timeout_socket_post_init,
483  lib::bind(
485  get_shared(),
486  post_timer,
487  m_init_handler,
488  lib::placeholders::_1
489  )
490  );
491  }
492 
493  socket_con_type::post_init(
494  lib::bind(
496  get_shared(),
497  post_timer,
498  m_init_handler,
499  lib::placeholders::_1
500  )
501  );
502  }
503 
505 
513  void handle_post_init_timeout(timer_ptr, init_handler callback,
514  lib::error_code const & ec)
515  {
516  lib::error_code ret_ec;
517 
518  if (ec) {
520  m_alog.write(log::alevel::devel,
521  "asio post init timer cancelled");
522  return;
523  }
524 
525  log_err(log::elevel::devel,"asio handle_post_init_timeout",ec);
526  ret_ec = ec;
527  } else {
528  if (socket_con_type::get_ec()) {
529  ret_ec = socket_con_type::get_ec();
530  } else {
531  ret_ec = make_error_code(transport::error::timeout);
532  }
533  }
534 
535  m_alog.write(log::alevel::devel,"Asio transport post-init timed out");
536  socket_con_type::cancel_socket();
537  callback(ret_ec);
538  }
539 
541 
549  void handle_post_init(timer_ptr post_timer, init_handler callback,
550  lib::error_code const & ec)
551  {
553  (post_timer && post_timer->expires_from_now().is_negative()))
554  {
555  m_alog.write(log::alevel::devel,"post_init cancelled");
556  return;
557  }
558 
559  if (post_timer) {
560  post_timer->cancel();
561  }
562 
563  if (m_alog.static_test(log::alevel::devel)) {
564  m_alog.write(log::alevel::devel,"asio connection handle_post_init");
565  }
566 
567  if (m_tcp_post_init_handler) {
568  m_tcp_post_init_handler(m_connection_hdl);
569  }
570 
571  callback(ec);
572  }
573 
574  void proxy_write() {
575  if (m_alog.static_test(log::alevel::devel)) {
576  m_alog.write(log::alevel::devel,"asio connection proxy_write");
577  }
578 
579  if (!m_proxy_data) {
580  m_elog.write(log::elevel::library,
581  "assertion failed: !m_proxy_data in asio::connection::proxy_write");
582  m_init_handler(make_error_code(error::general));
583  return;
584  }
585 
586  m_proxy_data->write_buf = m_proxy_data->req.raw();
587 
588  m_bufs.push_back(boost::asio::buffer(m_proxy_data->write_buf.data(),
589  m_proxy_data->write_buf.size()));
590 
591  m_alog.write(log::alevel::devel,m_proxy_data->write_buf);
592 
593  // Set a timer so we don't wait forever for the proxy to respond
594  m_proxy_data->timer = this->set_timer(
595  m_proxy_data->timeout_proxy,
596  lib::bind(
597  &type::handle_proxy_timeout,
598  get_shared(),
599  m_init_handler,
600  lib::placeholders::_1
601  )
602  );
603 
604  // Send proxy request
605  if (config::enable_multithreading) {
606  boost::asio::async_write(
607  socket_con_type::get_next_layer(),
608  m_bufs,
609  m_strand->wrap(lib::bind(
610  &type::handle_proxy_write, get_shared(),
611  m_init_handler,
612  lib::placeholders::_1
613  ))
614  );
615  } else {
616  boost::asio::async_write(
617  socket_con_type::get_next_layer(),
618  m_bufs,
619  lib::bind(
620  &type::handle_proxy_write, get_shared(),
621  m_init_handler,
622  lib::placeholders::_1
623  )
624  );
625  }
626  }
627 
628  void handle_proxy_timeout(init_handler callback, lib::error_code const & ec)
629  {
631  m_alog.write(log::alevel::devel,
632  "asio handle_proxy_write timer cancelled");
633  return;
634  } else if (ec) {
635  log_err(log::elevel::devel,"asio handle_proxy_write",ec);
636  callback(ec);
637  } else {
638  m_alog.write(log::alevel::devel,
639  "asio handle_proxy_write timer expired");
640  socket_con_type::cancel_socket();
641  callback(make_error_code(transport::error::timeout));
642  }
643  }
644 
645  void handle_proxy_write(init_handler callback,
646  boost::system::error_code const & ec)
647  {
648  if (m_alog.static_test(log::alevel::devel)) {
649  m_alog.write(log::alevel::devel,
650  "asio connection handle_proxy_write");
651  }
652 
653  m_bufs.clear();
654 
655  // Timer expired or the operation was aborted for some reason.
656  // Whatever aborted it will be issuing the callback so we are safe to
657  // return
658  if (ec == boost::asio::error::operation_aborted ||
659  m_proxy_data->timer->expires_from_now().is_negative())
660  {
661  m_elog.write(log::elevel::devel,"write operation aborted");
662  return;
663  }
664 
665  if (ec) {
666  log_err(log::elevel::info,"asio handle_proxy_write",ec);
667  m_proxy_data->timer->cancel();
668  callback(make_error_code(error::pass_through));
669  return;
670  }
671 
672  proxy_read(callback);
673  }
674 
675  void proxy_read(init_handler callback) {
676  if (m_alog.static_test(log::alevel::devel)) {
677  m_alog.write(log::alevel::devel,"asio connection proxy_read");
678  }
679 
680  if (!m_proxy_data) {
681  m_elog.write(log::elevel::library,
682  "assertion failed: !m_proxy_data in asio::connection::proxy_read");
683  m_proxy_data->timer->cancel();
684  callback(make_error_code(error::general));
685  return;
686  }
687 
688  if (config::enable_multithreading) {
689  boost::asio::async_read_until(
690  socket_con_type::get_next_layer(),
691  m_proxy_data->read_buf,
692  "\r\n\r\n",
693  m_strand->wrap(lib::bind(
695  callback,
696  lib::placeholders::_1, lib::placeholders::_2
697  ))
698  );
699  } else {
700  boost::asio::async_read_until(
701  socket_con_type::get_next_layer(),
702  m_proxy_data->read_buf,
703  "\r\n\r\n",
704  lib::bind(
706  callback,
707  lib::placeholders::_1, lib::placeholders::_2
708  )
709  );
710  }
711  }
712 
714 
720  boost::system::error_code const & ec, size_t)
721  {
722  if (m_alog.static_test(log::alevel::devel)) {
723  m_alog.write(log::alevel::devel,
724  "asio connection handle_proxy_read");
725  }
726 
727  // Timer expired or the operation was aborted for some reason.
728  // Whatever aborted it will be issuing the callback so we are safe to
729  // return
730  if (ec == boost::asio::error::operation_aborted ||
731  m_proxy_data->timer->expires_from_now().is_negative())
732  {
733  m_elog.write(log::elevel::devel,"read operation aborted");
734  return;
735  }
736 
737  // At this point there is no need to wait for the timer anymore
738  m_proxy_data->timer->cancel();
739 
740  if (ec) {
741  m_elog.write(log::elevel::info,
742  "asio handle_proxy_read error: "+ec.message());
743  callback(make_error_code(error::pass_through));
744  } else {
745  if (!m_proxy_data) {
746  m_elog.write(log::elevel::library,
747  "assertion failed: !m_proxy_data in asio::connection::handle_proxy_read");
748  callback(make_error_code(error::general));
749  return;
750  }
751 
752  std::istream input(&m_proxy_data->read_buf);
753 
754  m_proxy_data->res.consume(input);
755 
756  if (!m_proxy_data->res.headers_ready()) {
757  // we read until the headers were done in theory but apparently
758  // they aren't. Internal endpoint error.
759  callback(make_error_code(error::general));
760  return;
761  }
762 
763  m_alog.write(log::alevel::devel,m_proxy_data->res.raw());
764 
765  if (m_proxy_data->res.get_status_code() != http::status_code::ok) {
766  // got an error response back
767  // TODO: expose this error in a programmatically accessible way?
768  // if so, see below for an option on how to do this.
769  std::stringstream s;
770  s << "Proxy connection error: "
771  << m_proxy_data->res.get_status_code()
772  << " ("
773  << m_proxy_data->res.get_status_msg()
774  << ")";
775  m_elog.write(log::elevel::info,s.str());
776  callback(make_error_code(error::proxy_failed));
777  return;
778  }
779 
780  // we have successfully established a connection to the proxy, now
781  // we can continue and the proxy will transparently forward the
782  // WebSocket connection.
783 
784  // TODO: decide if we want an on_proxy callback that would allow
785  // access to the proxy response.
786 
787  // free the proxy buffers and req/res objects as they aren't needed
788  // anymore
789  m_proxy_data.reset();
790 
791  // Continue with post proxy initialization
792  post_init();
793  }
794  }
795 
797 
801  void async_read_at_least(size_t num_bytes, char *buf, size_t len,
802  read_handler handler)
803  {
804  if (m_alog.static_test(log::alevel::devel)) {
805  std::stringstream s;
806  s << "asio async_read_at_least: " << num_bytes;
807  m_alog.write(log::alevel::devel,s.str());
808  }
809 
810  if (!m_async_read_handler) {
811  m_alog.write(log::alevel::devel,
812  "async_read_at_least called after async_shutdown");
813  handler(make_error_code(transport::error::action_after_shutdown),0);
814  return;
815  }
816 
817  // TODO: safety vs speed ?
818  // maybe move into an if devel block
819  /*if (num_bytes > len) {
820  m_elog.write(log::elevel::devel,
821  "asio async_read_at_least error::invalid_num_bytes");
822  handler(make_error_code(transport::error::invalid_num_bytes),
823  size_t(0));
824  return;
825  }*/
826 
827  m_read_handler = handler;
828 
829  if (!m_read_handler) {
830  m_alog.write(log::alevel::devel,
831  "asio con async_read_at_least called with bad handler");
832  }
833 
834  boost::asio::async_read(
835  socket_con_type::get_socket(),
836  boost::asio::buffer(buf,len),
837  boost::asio::transfer_at_least(num_bytes),
838  make_custom_alloc_handler(
839  m_read_handler_allocator,
840  m_async_read_handler
841  )
842  );
843  }
844 
845  void handle_async_read(boost::system::error_code const & ec,
846  size_t bytes_transferred)
847  {
848  m_alog.write(log::alevel::devel, "asio con handle_async_read");
849 
850  // translate boost error codes into more lib::error_codes
851  lib::error_code tec;
852  if (ec == boost::asio::error::eof) {
853  tec = make_error_code(transport::error::eof);
854  } else if (ec) {
855  // We don't know much more about the error at this point. As our
856  // socket/security policy if it knows more:
857  tec = socket_con_type::translate_ec(ec);
858 
859  if (tec == transport::error::tls_error ||
861  {
862  // These are aggregate/catch all errors. Log some human readable
863  // information to the info channel to give library users some
864  // more details about why the upstream method may have failed.
865  log_err(log::elevel::info,"asio async_read_at_least",ec);
866  }
867  }
868  if (m_read_handler) {
869  m_read_handler(tec,bytes_transferred);
870  // TODO: why does this line break things?
871  //m_read_handler = _WEBSOCKETPP_NULL_FUNCTION_;
872  } else {
873  // This can happen in cases where the connection is terminated while
874  // the transport is waiting on a read.
875  m_alog.write(log::alevel::devel,
876  "handle_async_read called with null read handler");
877  }
878  }
879 
880  void async_write(const char* buf, size_t len, write_handler handler) {
881  if (!m_async_write_handler) {
882  m_alog.write(log::alevel::devel,
883  "async_write (single) called after async_shutdown");
884  handler(make_error_code(transport::error::action_after_shutdown));
885  return;
886  }
887 
888  m_bufs.push_back(boost::asio::buffer(buf,len));
889 
890  m_write_handler = handler;
891 
892  boost::asio::async_write(
893  socket_con_type::get_socket(),
894  m_bufs,
895  make_custom_alloc_handler(
896  m_write_handler_allocator,
897  m_async_write_handler
898  )
899  );
900  }
901 
902  void async_write(const std::vector<buffer>& bufs, write_handler handler) {
903  if (!m_async_write_handler) {
904  m_alog.write(log::alevel::devel,
905  "async_write (vector) called after async_shutdown");
906  handler(make_error_code(transport::error::action_after_shutdown));
907  return;
908  }
909  std::vector<buffer>::const_iterator it;
910 
911  for (it = bufs.begin(); it != bufs.end(); ++it) {
912  m_bufs.push_back(boost::asio::buffer((*it).buf,(*it).len));
913  }
914 
915  m_write_handler = handler;
916 
917  boost::asio::async_write(
918  socket_con_type::get_socket(),
919  m_bufs,
920  make_custom_alloc_handler(
921  m_write_handler_allocator,
922  m_async_write_handler
923  )
924  );
925  }
926 
928 
932  void handle_async_write(boost::system::error_code const & ec, size_t) {
933  m_bufs.clear();
934  lib::error_code tec;
935  if (ec) {
936  log_err(log::elevel::info,"asio async_write",ec);
937  tec = make_error_code(transport::error::pass_through);
938  }
939  if (m_write_handler) {
940  m_write_handler(tec);
941  // TODO: why does this line break things?
942  //m_write_handler = _WEBSOCKETPP_NULL_FUNCTION_;
943  } else {
944  // This can happen in cases where the connection is terminated while
945  // the transport is waiting on a read.
946  m_alog.write(log::alevel::devel,
947  "handle_async_write called with null write handler");
948  }
949  }
950 
952 
959  m_connection_hdl = hdl;
960  socket_con_type::set_handle(hdl);
961  }
962 
964 
967  lib::error_code interrupt(interrupt_handler handler) {
968  if (config::enable_multithreading) {
969  m_io_service->post(m_strand->wrap(handler));
970  } else {
971  m_io_service->post(handler);
972  }
973  return lib::error_code();
974  }
975 
976  lib::error_code dispatch(dispatch_handler handler) {
977  if (config::enable_multithreading) {
978  m_io_service->post(m_strand->wrap(handler));
979  } else {
980  m_io_service->post(handler);
981  }
982  return lib::error_code();
983  }
984 
985  /*void handle_interrupt(interrupt_handler handler) {
986  handler();
987  }*/
988 
991  if (m_alog.static_test(log::alevel::devel)) {
992  m_alog.write(log::alevel::devel,"asio connection async_shutdown");
993  }
994 
995  // Reset cached handlers now that we won't be reading or writing anymore
996  // These cached handlers store shared pointers to this connection and
997  // will leak the connection if not destroyed.
998  lib::clear_function(m_async_read_handler);
999  lib::clear_function(m_async_write_handler);
1000  lib::clear_function(m_init_handler);
1001 
1002  lib::clear_function(m_read_handler);
1003  lib::clear_function(m_write_handler);
1004 
1005  timer_ptr shutdown_timer;
1006  shutdown_timer = set_timer(
1007  config::timeout_socket_shutdown,
1008  lib::bind(
1010  get_shared(),
1011  shutdown_timer,
1012  callback,
1013  lib::placeholders::_1
1014  )
1015  );
1016 
1017  socket_con_type::async_shutdown(
1018  lib::bind(
1019  &type::handle_async_shutdown,
1020  get_shared(),
1021  shutdown_timer,
1022  callback,
1023  lib::placeholders::_1
1024  )
1025  );
1026  }
1027 
1029 
1034  void handle_async_shutdown_timeout(timer_ptr, init_handler callback,
1035  lib::error_code const & ec)
1036  {
1037  lib::error_code ret_ec;
1038 
1039  if (ec) {
1041  m_alog.write(log::alevel::devel,
1042  "asio socket shutdown timer cancelled");
1043  return;
1044  }
1045 
1046  log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec);
1047  ret_ec = ec;
1048  } else {
1049  ret_ec = make_error_code(transport::error::timeout);
1050  }
1051 
1052  m_alog.write(log::alevel::devel,
1053  "Asio transport socket shutdown timed out");
1054  socket_con_type::cancel_socket();
1055  callback(ret_ec);
1056  }
1057 
1058  void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler
1059  callback, boost::system::error_code const & ec)
1060  {
1061  if (ec == boost::asio::error::operation_aborted ||
1062  shutdown_timer->expires_from_now().is_negative())
1063  {
1064  m_alog.write(log::alevel::devel,"async_shutdown cancelled");
1065  return;
1066  }
1067 
1068  shutdown_timer->cancel();
1069 
1070  lib::error_code tec;
1071  if (ec) {
1072  if (ec == boost::asio::error::not_connected) {
1073  // The socket was already closed when we tried to close it. This
1074  // happens periodically (usually if a read or write fails
1075  // earlier and if it is a real error will be caught at another
1076  // level of the stack.
1077  } else {
1078  // We don't know anything more about this error, give our
1079  // socket/security policy a crack at it.
1080  tec = socket_con_type::translate_ec(ec);
1081 
1082  if (tec == transport::error::tls_short_read) {
1083  // TLS short read at this point is somewhat expected if both
1084  // sides try and end the connection at the same time or if
1085  // SSLv2 is being used. In general there is nothing that can
1086  // be done here other than a low level development log.
1087  } else {
1088  // all other errors are effectively pass through errors of
1089  // some sort so print some detail on the info channel for
1090  // library users to look up if needed.
1091  log_err(log::elevel::info,"asio async_shutdown",ec);
1092  }
1093  }
1094  } else {
1095  if (m_alog.static_test(log::alevel::devel)) {
1096  m_alog.write(log::alevel::devel,
1097  "asio con handle_async_shutdown");
1098  }
1099  }
1100  callback(tec);
1101  }
1102 private:
1104  template <typename error_type>
1105  void log_err(log::level l, const char * msg, const error_type & ec) {
1106  std::stringstream s;
1107  s << msg << " error: " << ec << " (" << ec.message() << ")";
1108  m_elog.write(l,s.str());
1109  }
1110 
1111  // static settings
1112  const bool m_is_server;
1113  alog_type& m_alog;
1114  elog_type& m_elog;
1115 
1116  struct proxy_data {
1117  proxy_data() : timeout_proxy(config::timeout_proxy) {}
1118 
1119  request_type req;
1120  response_type res;
1121  std::string write_buf;
1122  boost::asio::streambuf read_buf;
1123  long timeout_proxy;
1124  timer_ptr timer;
1125  };
1126 
1127  std::string m_proxy;
1128  lib::shared_ptr<proxy_data> m_proxy_data;
1129 
1130  // transport resources
1131  io_service_ptr m_io_service;
1132  strand_ptr m_strand;
1133  connection_hdl m_connection_hdl;
1134 
1135  std::vector<boost::asio::const_buffer> m_bufs;
1136 
1137  // Handlers
1138  tcp_init_handler m_tcp_pre_init_handler;
1139  tcp_init_handler m_tcp_post_init_handler;
1140 
1141  handler_allocator m_read_handler_allocator;
1142  handler_allocator m_write_handler_allocator;
1143 
1144  read_handler m_read_handler;
1145  write_handler m_write_handler;
1146  init_handler m_init_handler;
1147 
1148  async_read_handler m_async_read_handler;
1149  async_write_handler m_async_write_handler;
1150 };
1151 
1152 
1153 } // namespace asio
1154 } // namespace transport
1155 } // namespace websocketpp
1156 
1157 #endif // WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
config::elog_type elog_type
Type of this transport's error logging policy.
Definition: connection.hpp:76
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:196
void handle_async_write(boost::system::error_code const &ec, size_t)
Async write callback.
Definition: connection.hpp:932
void set_tcp_pre_init_handler(tcp_init_handler h)
Sets the tcp pre init handler.
Definition: connection.hpp:123
strand_ptr get_strand()
Get a pointer to this connection's strand.
Definition: connection.hpp:344
lib::function< void(lib::error_code const &)> write_handler
The type and signature of the callback passed to the write method.
Definition: connection.hpp:122
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:84
socket_con_type::ptr socket_con_ptr
Type of a shared pointer to the socket connection component.
Definition: connection.hpp:72
void handle_post_init_timeout(timer_ptr, init_handler callback, lib::error_code const &ec)
Post init timeout callback.
Definition: connection.hpp:513
lib::function< void()> interrupt_handler
The type and signature of the callback passed to the interrupt method.
Definition: connection.hpp:131
lib::shared_ptr< type > ptr
Type of a shared pointer to this connection transport component.
Definition: connection.hpp:67
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:119
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:86
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:152
void init(init_handler callback)
Initialize transport for reading.
Definition: connection.hpp:365
connection_hdl get_handle() const
Get the connection handle.
Definition: connection.hpp:274
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:152
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:260
timer_ptr set_timer(long duration, timer_handler callback)
Call back a function after a period of time.
Definition: connection.hpp:292
void set_proxy(std::string const &uri, lib::error_code &ec)
Set the proxy to connect through (exception free)
Definition: connection.hpp:168
config::alog_type alog_type
Type of this transport's access logging policy.
Definition: connection.hpp:74
lib::function< void()> dispatch_handler
The type and signature of the callback passed to the dispatch method.
Definition: connection.hpp:134
The connection was in the wrong state for this operation.
Definition: error.hpp:72
lib::error_code interrupt(interrupt_handler handler)
Trigger the on_interrupt handler.
Definition: connection.hpp:967
void handle_proxy_read(init_handler callback, boost::system::error_code const &ec, size_t)
Proxy read callback.
Definition: connection.hpp:719
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:229
lib::shared_ptr< boost::asio::deadline_timer > timer_ptr
Type of a pointer to the ASIO timer class.
Definition: connection.hpp:88
lib::function< void(lib::error_code const &)> timer_handler
The type and signature of the callback passed to the read method.
Definition: connection.hpp:125
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:128
Namespace for the WebSocket++ project.
Definition: base64.hpp:41
void set_proxy_timeout(long duration)
Set the proxy timeout duration (exception)
Definition: connection.hpp:240
lib::function< void(lib::error_code const &)> init_handler
The type and signature of the callback passed to the init hook.
Definition: connection.hpp:116
void handle_timer(timer_ptr, timer_handler callback, boost::system::error_code const &ec)
Timer callback.
Definition: connection.hpp:328
Boost Asio based connection transport component.
Definition: connection.hpp:62
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:105
connection< config > type
Type of this connection transport component.
Definition: connection.hpp:65
void handle_post_init(timer_ptr post_timer, init_handler callback, lib::error_code const &ec)
Post init timeout callback.
Definition: connection.hpp:549
void async_shutdown(shutdown_handler callback)
close and clean up the underlying socket
Definition: connection.hpp:990
lib::error_code proxy_init(std::string const &authority)
initialize the proxy buffers and http parsers
Definition: connection.hpp:392
lib::error_code init_asio(io_service_ptr io_service)
Finish constructing the transport.
Definition: connection.hpp:416
config::socket_type::socket_con_type socket_con_type
Type of the socket connection component.
Definition: connection.hpp:70
void set_proxy(std::string const &uri)
Set the proxy to connect through (exception)
Definition: connection.hpp:177
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:801
void set_tcp_init_handler(tcp_init_handler h)
Sets the tcp pre init handler (deprecated)
Definition: connection.hpp:137
static level const library
Definition: levels.hpp:66
void set_handle(connection_hdl hdl)
Set Connection Handle.
Definition: connection.hpp:958
void set_proxy_basic_auth(std::string const &username, std::string const &password)
Set the basic auth credentials to use (exception)
Definition: connection.hpp:211