2 * Copyright (c) 2015, Peter Thorson. All rights reserved.
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.
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.
28 #ifndef WEBSOCKETPP_TRANSPORT_ASIO_HPP
29 #define WEBSOCKETPP_TRANSPORT_ASIO_HPP
31 #include <websocketpp/transport/base/endpoint.hpp>
32 #include <websocketpp/transport/asio/connection.hpp>
33 #include <websocketpp/transport/asio/security/none.hpp>
35 #include <websocketpp/uri.hpp>
36 #include <websocketpp/logger/levels.hpp>
38 #include <websocketpp/common/functional.hpp>
43 namespace websocketpp {
47 /// Asio based endpoint transport component
49 * transport::asio::endpoint implements an endpoint transport component using
52 template <typename config>
53 class endpoint : public config::socket_type {
55 /// Type of this endpoint transport component
56 typedef endpoint<config> type;
58 /// Type of the concurrency policy
59 typedef typename config::concurrency_type concurrency_type;
60 /// Type of the socket policy
61 typedef typename config::socket_type socket_type;
62 /// Type of the error logging policy
63 typedef typename config::elog_type elog_type;
64 /// Type of the access logging policy
65 typedef typename config::alog_type alog_type;
67 /// Type of the socket connection component
68 typedef typename socket_type::socket_con_type socket_con_type;
69 /// Type of a shared pointer to the socket connection component
70 typedef typename socket_con_type::ptr socket_con_ptr;
72 /// Type of the connection transport component associated with this
73 /// endpoint transport component
74 typedef asio::connection<config> transport_con_type;
75 /// Type of a shared pointer to the connection transport component
76 /// associated with this endpoint transport component
77 typedef typename transport_con_type::ptr transport_con_ptr;
79 /// Type of a pointer to the ASIO io_service being used
80 typedef lib::asio::io_service * io_service_ptr;
81 /// Type of a shared pointer to the acceptor being used
82 typedef lib::shared_ptr<lib::asio::ip::tcp::acceptor> acceptor_ptr;
83 /// Type of a shared pointer to the resolver being used
84 typedef lib::shared_ptr<lib::asio::ip::tcp::resolver> resolver_ptr;
85 /// Type of timer handle
86 typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
87 /// Type of a shared pointer to an io_service work object
88 typedef lib::shared_ptr<lib::asio::io_service::work> work_ptr;
90 // generate and manage our own io_service
93 , m_external_io_service(false)
96 , m_state(UNINITIALIZED)
98 //std::cout << "transport::asio::endpoint constructor" << std::endl;
102 // clean up our io_service if we were initialized with an internal one.
104 // Explicitly destroy local objects
108 if (m_state != UNINITIALIZED && !m_external_io_service) {
113 /// transport::asio objects are moveable but not copyable or assignable.
114 /// The following code sets this situation up based on whether or not we
115 /// have C++11 support or not
116 #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
117 endpoint(const endpoint & src) = delete;
118 endpoint& operator= (const endpoint & rhs) = delete;
121 endpoint(const endpoint & src);
122 endpoint & operator= (const endpoint & rhs);
124 #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
126 #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_
127 endpoint (endpoint && src)
128 : config::socket_type(std::move(src))
129 , m_tcp_pre_init_handler(src.m_tcp_pre_init_handler)
130 , m_tcp_post_init_handler(src.m_tcp_post_init_handler)
131 , m_io_service(src.m_io_service)
132 , m_external_io_service(src.m_external_io_service)
133 , m_acceptor(src.m_acceptor)
134 , m_listen_backlog(lib::asio::socket_base::max_connections)
135 , m_reuse_addr(src.m_reuse_addr)
138 , m_state(src.m_state)
140 src.m_io_service = NULL;
141 src.m_external_io_service = false;
142 src.m_acceptor = NULL;
143 src.m_state = UNINITIALIZED;
146 /*endpoint & operator= (const endpoint && rhs) {
148 m_io_service = rhs.m_io_service;
149 m_external_io_service = rhs.m_external_io_service;
150 m_acceptor = rhs.m_acceptor;
151 m_listen_backlog = rhs.m_listen_backlog;
152 m_reuse_addr = rhs.m_reuse_addr;
153 m_state = rhs.m_state;
155 rhs.m_io_service = NULL;
156 rhs.m_external_io_service = false;
157 rhs.m_acceptor = NULL;
158 rhs.m_listen_backlog = lib::asio::socket_base::max_connections;
159 rhs.m_state = UNINITIALIZED;
161 // TODO: this needs to be updated
165 #endif // _WEBSOCKETPP_MOVE_SEMANTICS_
167 /// Return whether or not the endpoint produces secure connections.
168 bool is_secure() const {
169 return socket_type::is_secure();
172 /// initialize asio transport with external io_service (exception free)
174 * Initialize the ASIO transport policy for this endpoint using the provided
175 * io_service object. asio_init must be called exactly once on any endpoint
176 * that uses transport::asio before it can be used.
178 * @param ptr A pointer to the io_service to use for asio events
179 * @param ec Set to indicate what error occurred, if any.
181 void init_asio(io_service_ptr ptr, lib::error_code & ec) {
182 if (m_state != UNINITIALIZED) {
183 m_elog->write(log::elevel::library,
184 "asio::init_asio called from the wrong state");
185 using websocketpp::error::make_error_code;
186 ec = make_error_code(websocketpp::error::invalid_state);
190 m_alog->write(log::alevel::devel,"asio::init_asio");
193 m_external_io_service = true;
194 m_acceptor = lib::make_shared<lib::asio::ip::tcp::acceptor>(
195 lib::ref(*m_io_service));
198 ec = lib::error_code();
201 /// initialize asio transport with external io_service
203 * Initialize the ASIO transport policy for this endpoint using the provided
204 * io_service object. asio_init must be called exactly once on any endpoint
205 * that uses transport::asio before it can be used.
207 * @param ptr A pointer to the io_service to use for asio events
209 void init_asio(io_service_ptr ptr) {
212 if (ec) { throw exception(ec); }
215 /// Initialize asio transport with internal io_service (exception free)
217 * This method of initialization will allocate and use an internally managed
220 * @see init_asio(io_service_ptr ptr)
222 * @param ec Set to indicate what error occurred, if any.
224 void init_asio(lib::error_code & ec) {
225 // Use a smart pointer until the call is successful and ownership has
226 // successfully been taken. Use unique_ptr when available.
227 // TODO: remove the use of auto_ptr when C++98/03 support is no longer
229 #ifdef _WEBSOCKETPP_CPP11_MEMORY_
230 lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
232 lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
234 init_asio(service.get(), ec);
235 if( !ec ) service.release(); // Call was successful, transfer ownership
236 m_external_io_service = false;
239 /// Initialize asio transport with internal io_service
241 * This method of initialization will allocate and use an internally managed
244 * @see init_asio(io_service_ptr ptr)
247 // Use a smart pointer until the call is successful and ownership has
248 // successfully been taken. Use unique_ptr when available.
249 // TODO: remove the use of auto_ptr when C++98/03 support is no longer
251 #ifdef _WEBSOCKETPP_CPP11_MEMORY_
252 lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
254 lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
256 init_asio( service.get() );
257 // If control got this far without an exception, then ownership has successfully been taken
259 m_external_io_service = false;
262 /// Sets the tcp pre init handler
264 * The tcp pre init handler is called after the raw tcp connection has been
265 * established but before any additional wrappers (proxy connects, TLS
266 * handshakes, etc) have been performed.
270 * @param h The handler to call on tcp pre init.
272 void set_tcp_pre_init_handler(tcp_init_handler h) {
273 m_tcp_pre_init_handler = h;
276 /// Sets the tcp pre init handler (deprecated)
278 * The tcp pre init handler is called after the raw tcp connection has been
279 * established but before any additional wrappers (proxy connects, TLS
280 * handshakes, etc) have been performed.
282 * @deprecated Use set_tcp_pre_init_handler instead
284 * @param h The handler to call on tcp pre init.
286 void set_tcp_init_handler(tcp_init_handler h) {
287 set_tcp_pre_init_handler(h);
290 /// Sets the tcp post init handler
292 * The tcp post init handler is called after the tcp connection has been
293 * established and all additional wrappers (proxy connects, TLS handshakes,
294 * etc have been performed. This is fired before any bytes are read or any
295 * WebSocket specific handshake logic has been performed.
299 * @param h The handler to call on tcp post init.
301 void set_tcp_post_init_handler(tcp_init_handler h) {
302 m_tcp_post_init_handler = h;
305 /// Sets the maximum length of the queue of pending connections.
307 * Sets the maximum length of the queue of pending connections. Increasing
308 * this will allow WebSocket++ to queue additional incoming connections.
309 * Setting it higher may prevent failed connections at high connection rates
310 * but may cause additional latency.
312 * For this value to take effect you may need to adjust operating system
315 * New values affect future calls to listen only.
317 * A value of zero will use the operating system default. This is the
322 * @param backlog The maximum length of the queue of pending connections
324 void set_listen_backlog(int backlog) {
325 m_listen_backlog = backlog;
328 /// Sets whether to use the SO_REUSEADDR flag when opening listening sockets
330 * Specifies whether or not to use the SO_REUSEADDR TCP socket option. What
331 * this flag does depends on your operating system. Please consult operating
332 * system documentation for more details.
334 * New values affect future calls to listen only.
336 * The default is false.
340 * @param value Whether or not to use the SO_REUSEADDR option
342 void set_reuse_addr(bool value) {
343 m_reuse_addr = value;
346 /// Retrieve a reference to the endpoint's io_service
348 * The io_service may be an internal or external one. This may be used to
349 * call methods of the io_service that are not explicitly wrapped by the
352 * This method is only valid after the endpoint has been initialized with
353 * `init_asio`. No error will be returned if it isn't.
355 * @return A reference to the endpoint's io_service
357 lib::asio::io_service & get_io_service() {
358 return *m_io_service;
361 /// Get local TCP endpoint
363 * Extracts the local endpoint from the acceptor. This represents the
364 * address that WebSocket++ is listening on.
366 * Sets a bad_descriptor error if the acceptor is not currently listening
367 * or otherwise unavailable.
371 * @param ec Set to indicate what error occurred, if any.
372 * @return The local endpoint
374 lib::asio::ip::tcp::endpoint get_local_endpoint(lib::asio::error_code & ec) {
376 return m_acceptor->local_endpoint(ec);
378 ec = lib::asio::error::make_error_code(lib::asio::error::bad_descriptor);
379 return lib::asio::ip::tcp::endpoint();
383 /// Set up endpoint for listening manually (exception free)
385 * Bind the internal acceptor using the specified settings. The endpoint
386 * must have been initialized by calling init_asio before listening.
388 * @param ep An endpoint to read settings from
389 * @param ec Set to indicate what error occurred, if any.
391 void listen(lib::asio::ip::tcp::endpoint const & ep, lib::error_code & ec)
393 if (m_state != READY) {
394 m_elog->write(log::elevel::library,
395 "asio::listen called from the wrong state");
396 using websocketpp::error::make_error_code;
397 ec = make_error_code(websocketpp::error::invalid_state);
401 m_alog->write(log::alevel::devel,"asio::listen");
403 lib::asio::error_code bec;
405 m_acceptor->open(ep.protocol(),bec);
407 m_acceptor->set_option(lib::asio::socket_base::reuse_address(m_reuse_addr),bec);
410 m_acceptor->bind(ep,bec);
413 m_acceptor->listen(m_listen_backlog,bec);
416 if (m_acceptor->is_open()) {
419 log_err(log::elevel::info,"asio listen",bec);
420 ec = make_error_code(error::pass_through);
423 ec = lib::error_code();
427 /// Set up endpoint for listening manually
429 * Bind the internal acceptor using the settings specified by the endpoint e
431 * @param ep An endpoint to read settings from
433 void listen(lib::asio::ip::tcp::endpoint const & ep) {
436 if (ec) { throw exception(ec); }
439 /// Set up endpoint for listening with protocol and port (exception free)
441 * Bind the internal acceptor using the given internet protocol and port.
442 * The endpoint must have been initialized by calling init_asio before
445 * Common options include:
446 * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6()
447 * - IPv4 only: lib::asio::ip::tcp::v4()
449 * @param internet_protocol The internet protocol to use.
450 * @param port The port to listen on.
451 * @param ec Set to indicate what error occurred, if any.
453 template <typename InternetProtocol>
454 void listen(InternetProtocol const & internet_protocol, uint16_t port,
455 lib::error_code & ec)
457 lib::asio::ip::tcp::endpoint ep(internet_protocol, port);
461 /// Set up endpoint for listening with protocol and port
463 * Bind the internal acceptor using the given internet protocol and port.
464 * The endpoint must have been initialized by calling init_asio before
467 * Common options include:
468 * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6()
469 * - IPv4 only: lib::asio::ip::tcp::v4()
471 * @param internet_protocol The internet protocol to use.
472 * @param port The port to listen on.
474 template <typename InternetProtocol>
475 void listen(InternetProtocol const & internet_protocol, uint16_t port)
477 lib::asio::ip::tcp::endpoint ep(internet_protocol, port);
481 /// Set up endpoint for listening on a port (exception free)
483 * Bind the internal acceptor using the given port. The IPv6 protocol with
484 * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use
485 * the overload that allows specifying the protocol explicitly.
487 * The endpoint must have been initialized by calling init_asio before
490 * @param port The port to listen on.
491 * @param ec Set to indicate what error occurred, if any.
493 void listen(uint16_t port, lib::error_code & ec) {
494 listen(lib::asio::ip::tcp::v6(), port, ec);
497 /// Set up endpoint for listening on a port
499 * Bind the internal acceptor using the given port. The IPv6 protocol with
500 * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use
501 * the overload that allows specifying the protocol explicitly.
503 * The endpoint must have been initialized by calling init_asio before
506 * @param port The port to listen on.
507 * @param ec Set to indicate what error occurred, if any.
509 void listen(uint16_t port) {
510 listen(lib::asio::ip::tcp::v6(), port);
513 /// Set up endpoint for listening on a host and service (exception free)
515 * Bind the internal acceptor using the given host and service. More details
516 * about what host and service can be are available in the Asio
517 * documentation for ip::basic_resolver_query::basic_resolver_query's
520 * The endpoint must have been initialized by calling init_asio before
523 * @param host A string identifying a location. May be a descriptive name or
524 * a numeric address string.
525 * @param service A string identifying the requested service. This may be a
526 * descriptive name or a numeric string corresponding to a port number.
527 * @param ec Set to indicate what error occurred, if any.
529 void listen(std::string const & host, std::string const & service,
530 lib::error_code & ec)
532 using lib::asio::ip::tcp;
533 tcp::resolver r(*m_io_service);
534 tcp::resolver::query query(host, service);
535 tcp::resolver::iterator endpoint_iterator = r.resolve(query);
536 tcp::resolver::iterator end;
537 if (endpoint_iterator == end) {
538 m_elog->write(log::elevel::library,
539 "asio::listen could not resolve the supplied host or service");
540 ec = make_error_code(error::invalid_host_service);
543 listen(*endpoint_iterator,ec);
546 /// Set up endpoint for listening on a host and service
548 * Bind the internal acceptor using the given host and service. More details
549 * about what host and service can be are available in the Asio
550 * documentation for ip::basic_resolver_query::basic_resolver_query's
553 * The endpoint must have been initialized by calling init_asio before
556 * @param host A string identifying a location. May be a descriptive name or
557 * a numeric address string.
558 * @param service A string identifying the requested service. This may be a
559 * descriptive name or a numeric string corresponding to a port number.
560 * @param ec Set to indicate what error occurred, if any.
562 void listen(std::string const & host, std::string const & service)
565 listen(host,service,ec);
566 if (ec) { throw exception(ec); }
569 /// Stop listening (exception free)
571 * Stop listening and accepting new connections. This will not end any
572 * existing connections.
574 * @since 0.3.0-alpha4
575 * @param ec A status code indicating an error, if any.
577 void stop_listening(lib::error_code & ec) {
578 if (m_state != LISTENING) {
579 m_elog->write(log::elevel::library,
580 "asio::listen called from the wrong state");
581 using websocketpp::error::make_error_code;
582 ec = make_error_code(websocketpp::error::invalid_state);
588 ec = lib::error_code();
593 * Stop listening and accepting new connections. This will not end any
594 * existing connections.
596 * @since 0.3.0-alpha4
598 void stop_listening() {
601 if (ec) { throw exception(ec); }
604 /// Check if the endpoint is listening
606 * @return Whether or not the endpoint is listening.
608 bool is_listening() const {
609 return (m_state == LISTENING);
612 /// wraps the run method of the internal io_service object
614 return m_io_service->run();
617 /// wraps the run_one method of the internal io_service object
619 * @since 0.3.0-alpha4
621 std::size_t run_one() {
622 return m_io_service->run_one();
625 /// wraps the stop method of the internal io_service object
627 m_io_service->stop();
630 /// wraps the poll method of the internal io_service object
632 return m_io_service->poll();
635 /// wraps the poll_one method of the internal io_service object
636 std::size_t poll_one() {
637 return m_io_service->poll_one();
640 /// wraps the reset method of the internal io_service object
642 m_io_service->reset();
645 /// wraps the stopped method of the internal io_service object
646 bool stopped() const {
647 return m_io_service->stopped();
650 /// Marks the endpoint as perpetual, stopping it from exiting when empty
652 * Marks the endpoint as perpetual. Perpetual endpoints will not
653 * automatically exit when they run out of connections to process. To stop
654 * a perpetual endpoint call `end_perpetual`.
656 * An endpoint may be marked perpetual at any time by any thread. It must be
657 * called either before the endpoint has run out of work or before it was
662 void start_perpetual() {
663 m_work = lib::make_shared<lib::asio::io_service::work>(
664 lib::ref(*m_io_service)
668 /// Clears the endpoint's perpetual flag, allowing it to exit when empty
670 * Clears the endpoint's perpetual flag. This will cause the endpoint's run
671 * method to exit normally when it runs out of connections. If there are
672 * currently active connections it will not end until they are complete.
676 void stop_perpetual() {
680 /// Call back a function after a period of time.
682 * Sets a timer that calls back a function after the specified period of
683 * milliseconds. Returns a handle that can be used to cancel the timer.
684 * A cancelled timer will return the error code error::operation_aborted
685 * A timer that expired will return no error.
687 * @param duration Length of time to wait in milliseconds
688 * @param callback The function to call back when the timer has expired
689 * @return A handle that can be used to cancel the timer if it is no longer
692 timer_ptr set_timer(long duration, timer_handler callback) {
693 timer_ptr new_timer = lib::make_shared<lib::asio::steady_timer>(
695 lib::asio::milliseconds(duration)
698 new_timer->async_wait(
704 lib::placeholders::_1
713 * The timer pointer is included to ensure the timer isn't destroyed until
714 * after it has expired.
716 * @param t Pointer to the timer in question
717 * @param callback The function to call back
718 * @param ec A status code indicating an error, if any.
720 void handle_timer(timer_ptr, timer_handler callback,
721 lib::asio::error_code const & ec)
724 if (ec == lib::asio::error::operation_aborted) {
725 callback(make_error_code(transport::error::operation_aborted));
727 m_elog->write(log::elevel::info,
728 "asio handle_timer error: "+ec.message());
729 log_err(log::elevel::info,"asio handle_timer",ec);
730 callback(make_error_code(error::pass_through));
733 callback(lib::error_code());
737 /// Accept the next connection attempt and assign it to con (exception free)
739 * @param tcon The connection to accept into.
740 * @param callback The function to call when the operation is complete.
741 * @param ec A status code indicating an error, if any.
743 void async_accept(transport_con_ptr tcon, accept_handler callback,
744 lib::error_code & ec)
746 if (m_state != LISTENING) {
747 using websocketpp::error::make_error_code;
748 ec = make_error_code(websocketpp::error::async_accept_not_listening);
752 m_alog->write(log::alevel::devel, "asio::async_accept");
754 if (config::enable_multithreading) {
755 m_acceptor->async_accept(
756 tcon->get_raw_socket(),
757 tcon->get_strand()->wrap(lib::bind(
758 &type::handle_accept,
761 lib::placeholders::_1
765 m_acceptor->async_accept(
766 tcon->get_raw_socket(),
768 &type::handle_accept,
771 lib::placeholders::_1
777 /// Accept the next connection attempt and assign it to con.
779 * @param tcon The connection to accept into.
780 * @param callback The function to call when the operation is complete.
782 void async_accept(transport_con_ptr tcon, accept_handler callback) {
784 async_accept(tcon,callback,ec);
785 if (ec) { throw exception(ec); }
788 /// Initialize logging
790 * The loggers are located in the main endpoint class. As such, the
791 * transport doesn't have direct access to them. This method is called
792 * by the endpoint constructor to allow shared logging from the transport
793 * component. These are raw pointers to member variables of the endpoint.
794 * In particular, they cannot be used in the transport constructor as they
795 * haven't been constructed yet, and cannot be used in the transport
796 * destructor as they will have been destroyed by then.
798 void init_logging(alog_type* a, elog_type* e) {
803 void handle_accept(accept_handler callback, lib::asio::error_code const &
806 lib::error_code ret_ec;
808 m_alog->write(log::alevel::devel, "asio::handle_accept");
811 if (asio_ec == lib::asio::errc::operation_canceled) {
812 ret_ec = make_error_code(websocketpp::error::operation_canceled);
814 log_err(log::elevel::info,"asio handle_accept",asio_ec);
815 ret_ec = make_error_code(error::pass_through);
822 /// Initiate a new connection
823 // TODO: there have to be some more failure conditions here
824 void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) {
825 using namespace lib::asio::ip;
829 m_resolver = lib::make_shared<lib::asio::ip::tcp::resolver>(
830 lib::ref(*m_io_service));
835 std::string proxy = tcon->get_proxy();
840 host = u->get_host();
841 port = u->get_port_str();
845 uri_ptr pu = lib::make_shared<uri>(proxy);
847 if (!pu->get_valid()) {
848 cb(make_error_code(error::proxy_invalid));
852 ec = tcon->proxy_init(u->get_authority());
858 host = pu->get_host();
859 port = pu->get_port_str();
862 tcp::resolver::query query(host,port);
864 if (m_alog->static_test(log::alevel::devel)) {
865 m_alog->write(log::alevel::devel,
866 "starting async DNS resolve for "+host+":"+port);
871 dns_timer = tcon->set_timer(
872 config::timeout_dns_resolve,
874 &type::handle_resolve_timeout,
878 lib::placeholders::_1
882 if (config::enable_multithreading) {
883 m_resolver->async_resolve(
885 tcon->get_strand()->wrap(lib::bind(
886 &type::handle_resolve,
891 lib::placeholders::_1,
892 lib::placeholders::_2
896 m_resolver->async_resolve(
899 &type::handle_resolve,
904 lib::placeholders::_1,
905 lib::placeholders::_2
911 /// DNS resolution timeout handler
913 * The timer pointer is included to ensure the timer isn't destroyed until
914 * after it has expired.
916 * @param dns_timer Pointer to the timer in question
917 * @param callback The function to call back
918 * @param ec A status code indicating an error, if any.
920 void handle_resolve_timeout(timer_ptr, connect_handler callback,
921 lib::error_code const & ec)
923 lib::error_code ret_ec;
926 if (ec == transport::error::operation_aborted) {
927 m_alog->write(log::alevel::devel,
928 "asio handle_resolve_timeout timer cancelled");
932 log_err(log::elevel::devel,"asio handle_resolve_timeout",ec);
935 ret_ec = make_error_code(transport::error::timeout);
938 m_alog->write(log::alevel::devel,"DNS resolution timed out");
939 m_resolver->cancel();
943 void handle_resolve(transport_con_ptr tcon, timer_ptr dns_timer,
944 connect_handler callback, lib::asio::error_code const & ec,
945 lib::asio::ip::tcp::resolver::iterator iterator)
947 if (ec == lib::asio::error::operation_aborted ||
948 lib::asio::is_neg(dns_timer->expires_from_now()))
950 m_alog->write(log::alevel::devel,"async_resolve cancelled");
957 log_err(log::elevel::info,"asio async_resolve",ec);
958 callback(make_error_code(error::pass_through));
962 if (m_alog->static_test(log::alevel::devel)) {
964 s << "Async DNS resolve successful. Results: ";
966 lib::asio::ip::tcp::resolver::iterator it, end;
967 for (it = iterator; it != end; ++it) {
968 s << (*it).endpoint() << " ";
971 m_alog->write(log::alevel::devel,s.str());
974 m_alog->write(log::alevel::devel,"Starting async connect");
978 con_timer = tcon->set_timer(
979 config::timeout_connect,
981 &type::handle_connect_timeout,
986 lib::placeholders::_1
990 if (config::enable_multithreading) {
991 lib::asio::async_connect(
992 tcon->get_raw_socket(),
994 tcon->get_strand()->wrap(lib::bind(
995 &type::handle_connect,
1000 lib::placeholders::_1
1004 lib::asio::async_connect(
1005 tcon->get_raw_socket(),
1008 &type::handle_connect,
1013 lib::placeholders::_1
1019 /// Asio connect timeout handler
1021 * The timer pointer is included to ensure the timer isn't destroyed until
1022 * after it has expired.
1024 * @param tcon Pointer to the transport connection that is being connected
1025 * @param con_timer Pointer to the timer in question
1026 * @param callback The function to call back
1027 * @param ec A status code indicating an error, if any.
1029 void handle_connect_timeout(transport_con_ptr tcon, timer_ptr,
1030 connect_handler callback, lib::error_code const & ec)
1032 lib::error_code ret_ec;
1035 if (ec == transport::error::operation_aborted) {
1036 m_alog->write(log::alevel::devel,
1037 "asio handle_connect_timeout timer cancelled");
1041 log_err(log::elevel::devel,"asio handle_connect_timeout",ec);
1044 ret_ec = make_error_code(transport::error::timeout);
1047 m_alog->write(log::alevel::devel,"TCP connect timed out");
1048 tcon->cancel_socket_checked();
1052 void handle_connect(transport_con_ptr tcon, timer_ptr con_timer,
1053 connect_handler callback, lib::asio::error_code const & ec)
1055 if (ec == lib::asio::error::operation_aborted ||
1056 lib::asio::is_neg(con_timer->expires_from_now()))
1058 m_alog->write(log::alevel::devel,"async_connect cancelled");
1062 con_timer->cancel();
1065 log_err(log::elevel::info,"asio async_connect",ec);
1066 callback(make_error_code(error::pass_through));
1070 if (m_alog->static_test(log::alevel::devel)) {
1071 m_alog->write(log::alevel::devel,
1072 "Async connect to "+tcon->get_remote_endpoint()+" successful.");
1075 callback(lib::error_code());
1078 /// Initialize a connection
1080 * init is called by an endpoint once for each newly created connection.
1081 * It's purpose is to give the transport policy the chance to perform any
1082 * transport specific initialization that couldn't be done via the default
1085 * @param tcon A pointer to the transport portion of the connection.
1087 * @return A status code indicating the success or failure of the operation
1089 lib::error_code init(transport_con_ptr tcon) {
1090 m_alog->write(log::alevel::devel, "transport::asio::init");
1092 // Initialize the connection socket component
1093 socket_type::init(lib::static_pointer_cast<socket_con_type,
1094 transport_con_type>(tcon));
1098 ec = tcon->init_asio(m_io_service);
1099 if (ec) {return ec;}
1101 tcon->set_tcp_pre_init_handler(m_tcp_pre_init_handler);
1102 tcon->set_tcp_post_init_handler(m_tcp_post_init_handler);
1104 return lib::error_code();
1107 /// Convenience method for logging the code and message for an error_code
1108 template <typename error_type>
1109 void log_err(log::level l, char const * msg, error_type const & ec) {
1110 std::stringstream s;
1111 s << msg << " error: " << ec << " (" << ec.message() << ")";
1112 m_elog->write(l,s.str());
1122 tcp_init_handler m_tcp_pre_init_handler;
1123 tcp_init_handler m_tcp_post_init_handler;
1125 // Network Resources
1126 io_service_ptr m_io_service;
1127 bool m_external_io_service;
1128 acceptor_ptr m_acceptor;
1129 resolver_ptr m_resolver;
1132 // Network constants
1133 int m_listen_backlog;
1144 } // namespace transport
1145 } // namespace websocketpp
1147 #endif // WEBSOCKETPP_TRANSPORT_ASIO_HPP