websocketpp  0.3.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) 2013, 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(const std::string & 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.reset(new proxy_data());
173  ec = lib::error_code();
174  }
175 
177  void set_proxy(const std::string & uri) {
178  lib::error_code ec;
179  set_proxy(uri,ec);
180  if (ec) { throw ec; }
181  }
182 
184 
196  void set_proxy_basic_auth(const std::string & username, const
197  std::string & 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(const std::string & username, const
212  std::string & password)
213  {
214  lib::error_code ec;
215  set_proxy_basic_auth(username,password,ec);
216  if (ec) { throw 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 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(
294  new boost::asio::deadline_timer(
295  *m_io_service,
296  boost::posix_time::milliseconds(duration)
297  )
298  );
299 
300  if (config::enable_multithreading) {
301  new_timer->async_wait(m_strand->wrap(lib::bind(
303  new_timer,
304  callback,
305  lib::placeholders::_1
306  )));
307  } else {
308  new_timer->async_wait(lib::bind(
310  new_timer,
311  callback,
312  lib::placeholders::_1
313  ));
314  }
315 
316  return new_timer;
317  }
318 
320 
330  void handle_timer(timer_ptr t, timer_handler callback,
331  boost::system::error_code const & ec)
332  {
333  if (ec) {
334  if (ec == boost::asio::error::operation_aborted) {
335  callback(make_error_code(transport::error::operation_aborted));
336  } else {
337  log_err(log::elevel::info,"asio handle_timer",ec);
338  callback(make_error_code(error::pass_through));
339  }
340  } else {
341  callback(lib::error_code());
342  }
343  }
344 protected:
346  strand_ptr get_strand() {
347  return m_strand;
348  }
349 
351 
367  void init(init_handler callback) {
368  if (m_alog.static_test(log::alevel::devel)) {
369  m_alog.write(log::alevel::devel,"asio connection init");
370  }
371 
372  // TODO: pre-init timeout. Right now no implemented socket policies
373  // actually have an asyncronous pre-init
374 
375  m_init_handler = callback;
376 
377  socket_con_type::pre_init(
378  lib::bind(
379  &type::handle_pre_init,
380  get_shared(),
381  lib::placeholders::_1
382  )
383  );
384  }
385 
387 
394  lib::error_code proxy_init(std::string const & authority) {
395  if (!m_proxy_data) {
396  return websocketpp::error::make_error_code(
398  }
399  m_proxy_data->req.set_version("HTTP/1.1");
400  m_proxy_data->req.set_method("CONNECT");
401 
402  m_proxy_data->req.set_uri(authority);
403  m_proxy_data->req.replace_header("Host",authority);
404 
405  return lib::error_code();
406  }
407 
409 
418  lib::error_code init_asio (io_service_ptr io_service) {
419  m_io_service = io_service;
420 
421  if (config::enable_multithreading) {
422  m_strand.reset(new boost::asio::strand(*io_service));
423 
424  m_async_read_handler = m_strand->wrap(lib::bind(
425  &type::handle_async_read, get_shared(),lib::placeholders::_1,
426  lib::placeholders::_2));
427 
428  m_async_write_handler = m_strand->wrap(lib::bind(
429  &type::handle_async_write, get_shared(),lib::placeholders::_1,
430  lib::placeholders::_2));
431  } else {
432  m_async_read_handler = lib::bind(&type::handle_async_read,
433  get_shared(), lib::placeholders::_1, lib::placeholders::_2);
434 
435  m_async_write_handler = lib::bind(&type::handle_async_write,
436  get_shared(), lib::placeholders::_1, lib::placeholders::_2);
437  }
438 
439  lib::error_code ec = socket_con_type::init_asio(io_service, m_strand,
440  m_is_server);
441 
442  if (ec) {
443  // reset the handlers to break the circular reference:
444  // this->handler->this
445  m_async_read_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
446  m_async_write_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
447  }
448 
449  return ec;
450  }
451 
452  void handle_pre_init(lib::error_code const & ec) {
453  if (m_alog.static_test(log::alevel::devel)) {
454  m_alog.write(log::alevel::devel,"asio connection handle pre_init");
455  }
456 
457  if (m_tcp_pre_init_handler) {
458  m_tcp_pre_init_handler(m_connection_hdl);
459  }
460 
461  if (ec) {
462  m_init_handler(ec);
463  }
464 
465  // If we have a proxy set issue a proxy connect, otherwise skip to
466  // post_init
467  if (!m_proxy.empty()) {
468  proxy_write();
469  } else {
470  post_init();
471  }
472  }
473 
474  void post_init() {
475  if (m_alog.static_test(log::alevel::devel)) {
476  m_alog.write(log::alevel::devel,"asio connection post_init");
477  }
478 
479  timer_ptr post_timer;
480 
481  if (config::timeout_socket_post_init > 0) {
482  post_timer = set_timer(
483  config::timeout_socket_post_init,
484  lib::bind(
485  &type::handle_post_init_timeout,
486  get_shared(),
487  post_timer,
488  m_init_handler,
489  lib::placeholders::_1
490  )
491  );
492  }
493 
494  socket_con_type::post_init(
495  lib::bind(
496  &type::handle_post_init,
497  get_shared(),
498  post_timer,
499  m_init_handler,
500  lib::placeholders::_1
501  )
502  );
503  }
504 
505  void handle_post_init_timeout(timer_ptr post_timer, init_handler callback,
506  lib::error_code const & ec)
507  {
508  lib::error_code ret_ec;
509 
510  if (ec) {
512  m_alog.write(log::alevel::devel,
513  "asio post init timer cancelled");
514  return;
515  }
516 
517  log_err(log::elevel::devel,"asio handle_post_init_timeout",ec);
518  ret_ec = ec;
519  } else {
520  if (socket_con_type::get_ec()) {
521  ret_ec = socket_con_type::get_ec();
522  } else {
523  ret_ec = make_error_code(transport::error::timeout);
524  }
525  }
526 
527  m_alog.write(log::alevel::devel,"Asio transport post-init timed out");
528  socket_con_type::cancel_socket();
529  callback(ret_ec);
530  }
531 
532  void handle_post_init(timer_ptr post_timer, init_handler callback,
533  lib::error_code const & ec)
534  {
536  (post_timer && post_timer->expires_from_now().is_negative()))
537  {
538  m_alog.write(log::alevel::devel,"post_init cancelled");
539  return;
540  }
541 
542  if (post_timer) {
543  post_timer->cancel();
544  }
545 
546  if (m_alog.static_test(log::alevel::devel)) {
547  m_alog.write(log::alevel::devel,"asio connection handle_post_init");
548  }
549 
550  if (m_tcp_post_init_handler) {
551  m_tcp_post_init_handler(m_connection_hdl);
552  }
553 
554  callback(ec);
555  }
556 
557  void proxy_write() {
558  if (m_alog.static_test(log::alevel::devel)) {
559  m_alog.write(log::alevel::devel,"asio connection proxy_write");
560  }
561 
562  if (!m_proxy_data) {
563  m_elog.write(log::elevel::library,
564  "assertion failed: !m_proxy_data in asio::connection::proxy_write");
565  m_init_handler(make_error_code(error::general));
566  return;
567  }
568 
569  m_proxy_data->write_buf = m_proxy_data->req.raw();
570 
571  m_bufs.push_back(boost::asio::buffer(m_proxy_data->write_buf.data(),
572  m_proxy_data->write_buf.size()));
573 
574  m_alog.write(log::alevel::devel,m_proxy_data->write_buf);
575 
576  // Set a timer so we don't wait forever for the proxy to respond
577  m_proxy_data->timer = this->set_timer(
578  m_proxy_data->timeout_proxy,
579  lib::bind(
580  &type::handle_proxy_timeout,
581  get_shared(),
582  m_init_handler,
583  lib::placeholders::_1
584  )
585  );
586 
587  // Send proxy request
588  if (config::enable_multithreading) {
589  boost::asio::async_write(
590  socket_con_type::get_next_layer(),
591  m_bufs,
592  m_strand->wrap(lib::bind(
593  &type::handle_proxy_write, get_shared(),
594  m_init_handler,
595  lib::placeholders::_1
596  ))
597  );
598  } else {
599  boost::asio::async_write(
600  socket_con_type::get_next_layer(),
601  m_bufs,
602  lib::bind(
603  &type::handle_proxy_write, get_shared(),
604  m_init_handler,
605  lib::placeholders::_1
606  )
607  );
608  }
609  }
610 
611  void handle_proxy_timeout(init_handler callback, lib::error_code const & ec)
612  {
614  m_alog.write(log::alevel::devel,
615  "asio handle_proxy_write timer cancelled");
616  return;
617  } else if (ec) {
618  log_err(log::elevel::devel,"asio handle_proxy_write",ec);
619  callback(ec);
620  } else {
621  m_alog.write(log::alevel::devel,
622  "asio handle_proxy_write timer expired");
623  socket_con_type::cancel_socket();
624  callback(make_error_code(transport::error::timeout));
625  }
626  }
627 
628  void handle_proxy_write(init_handler callback,
629  boost::system::error_code const & ec)
630  {
631  if (m_alog.static_test(log::alevel::devel)) {
632  m_alog.write(log::alevel::devel,
633  "asio connection handle_proxy_write");
634  }
635 
636  m_bufs.clear();
637 
638  // Timer expired or the operation was aborted for some reason.
639  // Whatever aborted it will be issuing the callback so we are safe to
640  // return
641  if (ec == boost::asio::error::operation_aborted ||
642  m_proxy_data->timer->expires_from_now().is_negative())
643  {
644  m_elog.write(log::elevel::devel,"write operation aborted");
645  return;
646  }
647 
648  if (ec) {
649  log_err(log::elevel::info,"asio handle_proxy_write",ec);
650  m_proxy_data->timer->cancel();
651  callback(make_error_code(error::pass_through));
652  return;
653  }
654 
655  proxy_read(callback);
656  }
657 
658  void proxy_read(init_handler callback) {
659  if (m_alog.static_test(log::alevel::devel)) {
660  m_alog.write(log::alevel::devel,"asio connection proxy_read");
661  }
662 
663  if (!m_proxy_data) {
664  m_elog.write(log::elevel::library,
665  "assertion failed: !m_proxy_data in asio::connection::proxy_read");
666  m_proxy_data->timer->cancel();
667  callback(make_error_code(error::general));
668  return;
669  }
670 
671  if (config::enable_multithreading) {
672  boost::asio::async_read_until(
673  socket_con_type::get_next_layer(),
674  m_proxy_data->read_buf,
675  "\r\n\r\n",
676  m_strand->wrap(lib::bind(
677  &type::handle_proxy_read, get_shared(),
678  callback,
679  lib::placeholders::_1, lib::placeholders::_2
680  ))
681  );
682  } else {
683  boost::asio::async_read_until(
684  socket_con_type::get_next_layer(),
685  m_proxy_data->read_buf,
686  "\r\n\r\n",
687  lib::bind(
688  &type::handle_proxy_read, get_shared(),
689  callback,
690  lib::placeholders::_1, lib::placeholders::_2
691  )
692  );
693  }
694  }
695 
696  void handle_proxy_read(init_handler callback,
697  boost::system::error_code const & ec, size_t bytes_transferred)
698  {
699  if (m_alog.static_test(log::alevel::devel)) {
700  m_alog.write(log::alevel::devel,
701  "asio connection handle_proxy_read");
702  }
703 
704  // Timer expired or the operation was aborted for some reason.
705  // Whatever aborted it will be issuing the callback so we are safe to
706  // return
707  if (ec == boost::asio::error::operation_aborted ||
708  m_proxy_data->timer->expires_from_now().is_negative())
709  {
710  m_elog.write(log::elevel::devel,"read operation aborted");
711  return;
712  }
713 
714  // At this point there is no need to wait for the timer anymore
715  m_proxy_data->timer->cancel();
716 
717  if (ec) {
718  m_elog.write(log::elevel::info,
719  "asio handle_proxy_read error: "+ec.message());
720  callback(make_error_code(error::pass_through));
721  } else {
722  if (!m_proxy_data) {
723  m_elog.write(log::elevel::library,
724  "assertion failed: !m_proxy_data in asio::connection::handle_proxy_read");
725  callback(make_error_code(error::general));
726  return;
727  }
728 
729  std::istream input(&m_proxy_data->read_buf);
730 
731  m_proxy_data->res.consume(input);
732 
733  if (!m_proxy_data->res.headers_ready()) {
734  // we read until the headers were done in theory but apparently
735  // they aren't. Internal endpoint error.
736  callback(make_error_code(error::general));
737  return;
738  }
739 
740  m_alog.write(log::alevel::devel,m_proxy_data->res.raw());
741 
742  if (m_proxy_data->res.get_status_code() != http::status_code::ok) {
743  // got an error response back
744  // TODO: expose this error in a programmatically accessible way?
745  // if so, see below for an option on how to do this.
746  std::stringstream s;
747  s << "Proxy connection error: "
748  << m_proxy_data->res.get_status_code()
749  << " ("
750  << m_proxy_data->res.get_status_msg()
751  << ")";
752  m_elog.write(log::elevel::info,s.str());
753  callback(make_error_code(error::proxy_failed));
754  return;
755  }
756 
757  // we have successfully established a connection to the proxy, now
758  // we can continue and the proxy will transparently forward the
759  // WebSocket connection.
760 
761  // TODO: decide if we want an on_proxy callback that would allow
762  // access to the proxy response.
763 
764  // free the proxy buffers and req/res objects as they aren't needed
765  // anymore
766  m_proxy_data.reset();
767 
768  // Continue with post proxy initialization
769  post_init();
770  }
771  }
772 
774 
778  void async_read_at_least(size_t num_bytes, char *buf, size_t len,
779  read_handler handler)
780  {
781  if (m_alog.static_test(log::alevel::devel)) {
782  std::stringstream s;
783  s << "asio async_read_at_least: " << num_bytes;
784  m_alog.write(log::alevel::devel,s.str());
785  }
786 
787  if (!m_async_read_handler) {
788  m_alog.write(log::alevel::devel,
789  "async_read_at_least called after async_shutdown");
790  handler(make_error_code(transport::error::action_after_shutdown),0);
791  return;
792  }
793 
794  // TODO: safety vs speed ?
795  // maybe move into an if devel block
796  /*if (num_bytes > len) {
797  m_elog.write(log::elevel::devel,
798  "asio async_read_at_least error::invalid_num_bytes");
799  handler(make_error_code(transport::error::invalid_num_bytes),
800  size_t(0));
801  return;
802  }*/
803 
804  m_read_handler = handler;
805 
806  if (!m_read_handler) {
807  m_alog.write(log::alevel::devel,
808  "asio con async_read_at_least called with bad handler");
809  }
810 
811  boost::asio::async_read(
812  socket_con_type::get_socket(),
813  boost::asio::buffer(buf,len),
814  boost::asio::transfer_at_least(num_bytes),
815  make_custom_alloc_handler(
816  m_read_handler_allocator,
817  m_async_read_handler
818  )
819  );
820  }
821 
822  void handle_async_read(boost::system::error_code const & ec,
823  size_t bytes_transferred)
824  {
825  m_alog.write(log::alevel::devel, "asio con handle_async_read");
826 
827  // translate boost error codes into more lib::error_codes
828  lib::error_code tec;
829  if (ec == boost::asio::error::eof) {
830  tec = make_error_code(transport::error::eof);
831  } else if (ec) {
832  // We don't know much more about the error at this point. As our
833  // socket/security policy if it knows more:
834  tec = socket_con_type::translate_ec(ec);
835 
836  if (tec == transport::error::tls_error ||
838  {
839  // These are aggregate/catch all errors. Log some human readable
840  // information to the info channel to give library users some
841  // more details about why the upstream method may have failed.
842  log_err(log::elevel::info,"asio async_read_at_least",ec);
843  }
844  }
845  if (m_read_handler) {
846  m_read_handler(tec,bytes_transferred);
847  // TODO: why does this line break things?
848  //m_read_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
849  } else {
850  // This can happen in cases where the connection is terminated while
851  // the transport is waiting on a read.
852  m_alog.write(log::alevel::devel,
853  "handle_async_read called with null read handler");
854  }
855  }
856 
857  void async_write(const char* buf, size_t len, write_handler handler) {
858  if (!m_async_write_handler) {
859  m_alog.write(log::alevel::devel,
860  "async_write (single) called after async_shutdown");
861  handler(make_error_code(transport::error::action_after_shutdown));
862  return;
863  }
864 
865  m_bufs.push_back(boost::asio::buffer(buf,len));
866 
867  m_write_handler = handler;
868 
869  boost::asio::async_write(
870  socket_con_type::get_socket(),
871  m_bufs,
872  make_custom_alloc_handler(
873  m_write_handler_allocator,
874  m_async_write_handler
875  )
876  );
877  }
878 
879  void async_write(const std::vector<buffer>& bufs, write_handler handler) {
880  if (!m_async_write_handler) {
881  m_alog.write(log::alevel::devel,
882  "async_write (vector) called after async_shutdown");
883  handler(make_error_code(transport::error::action_after_shutdown));
884  return;
885  }
886  std::vector<buffer>::const_iterator it;
887 
888  for (it = bufs.begin(); it != bufs.end(); ++it) {
889  m_bufs.push_back(boost::asio::buffer((*it).buf,(*it).len));
890  }
891 
892  m_write_handler = handler;
893 
894  boost::asio::async_write(
895  socket_con_type::get_socket(),
896  m_bufs,
897  make_custom_alloc_handler(
898  m_write_handler_allocator,
899  m_async_write_handler
900  )
901  );
902  }
903 
904  void handle_async_write(boost::system::error_code const & ec,
905  size_t bytes_transferred)
906  {
907  m_bufs.clear();
908  lib::error_code tec;
909  if (ec) {
910  log_err(log::elevel::info,"asio async_write",ec);
911  tec = make_error_code(transport::error::pass_through);
912  }
913  if (m_write_handler) {
914  m_write_handler(tec);
915  // TODO: why does this line break things?
916  //m_write_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
917  } else {
918  // This can happen in cases where the connection is terminated while
919  // the transport is waiting on a read.
920  m_alog.write(log::alevel::devel,
921  "handle_async_write called with null write handler");
922  }
923  }
924 
926 
933  m_connection_hdl = hdl;
934  socket_con_type::set_handle(hdl);
935  }
936 
938 
941  lib::error_code interrupt(interrupt_handler handler) {
942  if (config::enable_multithreading) {
943  m_io_service->post(m_strand->wrap(handler));
944  } else {
945  m_io_service->post(handler);
946  }
947  return lib::error_code();
948  }
949 
950  lib::error_code dispatch(dispatch_handler handler) {
951  if (config::enable_multithreading) {
952  m_io_service->post(m_strand->wrap(handler));
953  } else {
954  m_io_service->post(handler);
955  }
956  return lib::error_code();
957  }
958 
959  /*void handle_interrupt(interrupt_handler handler) {
960  handler();
961  }*/
962 
965  if (m_alog.static_test(log::alevel::devel)) {
966  m_alog.write(log::alevel::devel,"asio connection async_shutdown");
967  }
968 
969  // Reset cached handlers now that we won't be reading or writing anymore
970  // These cached handlers store shared pointers to this connection and
971  // will leak the connection if not destroyed.
972  m_async_read_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
973  m_async_write_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
974  m_init_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
975 
976  m_read_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
977  m_write_handler = _WEBSOCKETPP_NULLPTR_TOKEN_;
978 
979  timer_ptr shutdown_timer;
980  shutdown_timer = set_timer(
981  config::timeout_socket_shutdown,
982  lib::bind(
983  &type::handle_async_shutdown_timeout,
984  get_shared(),
985  shutdown_timer,
986  callback,
987  lib::placeholders::_1
988  )
989  );
990 
991  socket_con_type::async_shutdown(
992  lib::bind(
993  &type::handle_async_shutdown,
994  get_shared(),
995  shutdown_timer,
996  callback,
997  lib::placeholders::_1
998  )
999  );
1000  }
1001 
1002  void handle_async_shutdown_timeout(timer_ptr shutdown_timer, init_handler
1003  callback, lib::error_code const & ec)
1004  {
1005  lib::error_code ret_ec;
1006 
1007  if (ec) {
1009  m_alog.write(log::alevel::devel,
1010  "asio socket shutdown timer cancelled");
1011  return;
1012  }
1013 
1014  log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec);
1015  ret_ec = ec;
1016  } else {
1017  ret_ec = make_error_code(transport::error::timeout);
1018  }
1019 
1020  m_alog.write(log::alevel::devel,
1021  "Asio transport socket shutdown timed out");
1022  socket_con_type::cancel_socket();
1023  callback(ret_ec);
1024  }
1025 
1026  void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler
1027  callback, boost::system::error_code const & ec)
1028  {
1029  if (ec == boost::asio::error::operation_aborted ||
1030  shutdown_timer->expires_from_now().is_negative())
1031  {
1032  m_alog.write(log::alevel::devel,"async_shutdown cancelled");
1033  return;
1034  }
1035 
1036  shutdown_timer->cancel();
1037 
1038  lib::error_code tec;
1039  if (ec) {
1040  if (ec == boost::asio::error::not_connected) {
1041  // The socket was already closed when we tried to close it. This
1042  // happens periodically (usually if a read or write fails
1043  // earlier and if it is a real error will be caught at another
1044  // level of the stack.
1045  } else {
1046  // We don't know anything more about this error, give our
1047  // socket/security policy a crack at it.
1048  tec = socket_con_type::translate_ec(ec);
1049 
1050  if (tec == transport::error::tls_short_read) {
1051  // TLS short read at this point is somewhat expected if both
1052  // sides try and end the connection at the same time or if
1053  // SSLv2 is being used. In general there is nothing that can
1054  // be done here other than a low level development log.
1055  } else {
1056  // all other errors are effectively pass through errors of
1057  // some sort so print some detail on the info channel for
1058  // library users to look up if needed.
1059  log_err(log::elevel::info,"asio async_shutdown",ec);
1060  }
1061  }
1062  } else {
1063  if (m_alog.static_test(log::alevel::devel)) {
1064  m_alog.write(log::alevel::devel,
1065  "asio con handle_async_shutdown");
1066  }
1067  }
1068  callback(tec);
1069  }
1070 private:
1072  template <typename error_type>
1073  void log_err(log::level l, const char * msg, const error_type & ec) {
1074  std::stringstream s;
1075  s << msg << " error: " << ec << " (" << ec.message() << ")";
1076  m_elog.write(l,s.str());
1077  }
1078 
1079  // static settings
1080  const bool m_is_server;
1081  alog_type& m_alog;
1082  elog_type& m_elog;
1083 
1084  struct proxy_data {
1085  proxy_data() : timeout_proxy(config::timeout_proxy) {}
1086 
1087  request_type req;
1088  response_type res;
1089  std::string write_buf;
1090  boost::asio::streambuf read_buf;
1091  long timeout_proxy;
1092  timer_ptr timer;
1093  };
1094 
1095  std::string m_proxy;
1096  lib::shared_ptr<proxy_data> m_proxy_data;
1097 
1098  // transport resources
1099  io_service_ptr m_io_service;
1100  strand_ptr m_strand;
1101  connection_hdl m_connection_hdl;
1102 
1103  std::vector<boost::asio::const_buffer> m_bufs;
1104 
1105  // Handlers
1106  tcp_init_handler m_tcp_pre_init_handler;
1107  tcp_init_handler m_tcp_post_init_handler;
1108 
1109  handler_allocator m_read_handler_allocator;
1110  handler_allocator m_write_handler_allocator;
1111 
1112  read_handler m_read_handler;
1113  write_handler m_write_handler;
1114  init_handler m_init_handler;
1115 
1116  async_read_handler m_async_read_handler;
1117  async_write_handler m_async_write_handler;
1118 };
1119 
1120 
1121 } // namespace asio
1122 } // namespace transport
1123 } // namespace websocketpp
1124 
1125 #endif // WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
void handle_timer(timer_ptr t, timer_handler callback, boost::system::error_code const &ec)
Timer callback.
Definition: connection.hpp:330
void set_proxy_basic_auth(const std::string &username, const std::string &password, lib::error_code &ec)
Set the basic auth credentials to use (exception free)
Definition: connection.hpp:196
config::elog_type elog_type
Type of this transport's error logging policy.
Definition: connection.hpp:76
void set_proxy(const std::string &uri, lib::error_code &ec)
Set the proxy to connect through (exception free)
Definition: connection.hpp:168
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:346
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:44
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
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
underlying transport pass through
Definition: connection.hpp:152
void init(init_handler callback)
Initialize transport for reading.
Definition: connection.hpp:367
connection_hdl get_handle() const
Get the connection handle.
Definition: connection.hpp:274
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:122
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
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:941
static level const info
Definition: levels.hpp:50
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
Boost Asio based connection transport component.
Definition: connection.hpp:62
void set_proxy(const std::string &uri)
Set the proxy to connect through (exception)
Definition: connection.hpp:177
Boost Asio based endpoint transport component.
Definition: base.hpp:159
void set_proxy_basic_auth(const std::string &username, const std::string &password)
Set the basic auth credentials to use (exception)
Definition: connection.hpp:211
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 async_shutdown(shutdown_handler callback)
close and clean up the underlying socket
Definition: connection.hpp:964
lib::error_code proxy_init(std::string const &authority)
initialize the proxy buffers and http parsers
Definition: connection.hpp:394
lib::error_code init_asio(io_service_ptr io_service)
Finish constructing the transport.
Definition: connection.hpp:418
config::socket_type::socket_con_type socket_con_type
Type of the socket connection component.
Definition: connection.hpp:70
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:778
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:47
void set_handle(connection_hdl hdl)
Set Connection Handle.
Definition: connection.hpp:932