fixed adding file problem
[c11concurrency-benchmarks.git] / gdax-orderbook-hpp / demo / dependencies / websocketpp-0.7.0 / websocketpp / transport / asio / endpoint.hpp
1 /*
2  * Copyright (c) 2015, Peter Thorson. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *     * Redistributions of source code must retain the above copyright
7  *       notice, this list of conditions and the following disclaimer.
8  *     * Redistributions in binary form must reproduce the above copyright
9  *       notice, this list of conditions and the following disclaimer in the
10  *       documentation and/or other materials provided with the distribution.
11  *     * Neither the name of the WebSocket++ Project nor the
12  *       names of its contributors may be used to endorse or promote products
13  *       derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27
28 #ifndef WEBSOCKETPP_TRANSPORT_ASIO_HPP
29 #define WEBSOCKETPP_TRANSPORT_ASIO_HPP
30
31 #include <websocketpp/transport/base/endpoint.hpp>
32 #include <websocketpp/transport/asio/connection.hpp>
33 #include <websocketpp/transport/asio/security/none.hpp>
34
35 #include <websocketpp/uri.hpp>
36 #include <websocketpp/logger/levels.hpp>
37
38 #include <websocketpp/common/functional.hpp>
39
40 #include <sstream>
41 #include <string>
42
43 namespace websocketpp {
44 namespace transport {
45 namespace asio {
46
47 /// Asio based endpoint transport component
48 /**
49  * transport::asio::endpoint implements an endpoint transport component using
50  * Asio.
51  */
52 template <typename config>
53 class endpoint : public config::socket_type {
54 public:
55     /// Type of this endpoint transport component
56     typedef endpoint<config> type;
57
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;
66
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;
71
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;
78
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;
89
90     // generate and manage our own io_service
91     explicit endpoint()
92       : m_io_service(NULL)
93       , m_external_io_service(false)
94       , m_listen_backlog(0)
95       , m_reuse_addr(false)
96       , m_state(UNINITIALIZED)
97     {
98         //std::cout << "transport::asio::endpoint constructor" << std::endl;
99     }
100
101     ~endpoint() {
102         // clean up our io_service if we were initialized with an internal one.
103
104         // Explicitly destroy local objects
105         m_acceptor.reset();
106         m_resolver.reset();
107         m_work.reset();
108         if (m_state != UNINITIALIZED && !m_external_io_service) {
109             delete m_io_service;
110         }
111     }
112
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;
119 #else
120 private:
121     endpoint(const endpoint & src);
122     endpoint & operator= (const endpoint & rhs);
123 public:
124 #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
125
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)
136       , m_elog(src.m_elog)
137       , m_alog(src.m_alog)
138       , m_state(src.m_state)
139     {
140         src.m_io_service = NULL;
141         src.m_external_io_service = false;
142         src.m_acceptor = NULL;
143         src.m_state = UNINITIALIZED;
144     }
145
146     /*endpoint & operator= (const endpoint && rhs) {
147         if (this != &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;
154
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;
160             
161             // TODO: this needs to be updated
162         }
163         return *this;
164     }*/
165 #endif // _WEBSOCKETPP_MOVE_SEMANTICS_
166
167     /// Return whether or not the endpoint produces secure connections.
168     bool is_secure() const {
169         return socket_type::is_secure();
170     }
171
172     /// initialize asio transport with external io_service (exception free)
173     /**
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.
177      *
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.
180      */
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);
187             return;
188         }
189
190         m_alog->write(log::alevel::devel,"asio::init_asio");
191
192         m_io_service = ptr;
193         m_external_io_service = true;
194         m_acceptor = lib::make_shared<lib::asio::ip::tcp::acceptor>(
195             lib::ref(*m_io_service));
196
197         m_state = READY;
198         ec = lib::error_code();
199     }
200
201     /// initialize asio transport with external io_service
202     /**
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.
206      *
207      * @param ptr A pointer to the io_service to use for asio events
208      */
209     void init_asio(io_service_ptr ptr) {
210         lib::error_code ec;
211         init_asio(ptr,ec);
212         if (ec) { throw exception(ec); }
213     }
214
215     /// Initialize asio transport with internal io_service (exception free)
216     /**
217      * This method of initialization will allocate and use an internally managed
218      * io_service.
219      *
220      * @see init_asio(io_service_ptr ptr)
221      *
222      * @param ec Set to indicate what error occurred, if any.
223      */
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
228         //       necessary.
229 #ifdef _WEBSOCKETPP_CPP11_MEMORY_
230         lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
231 #else
232         lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
233 #endif
234         init_asio(service.get(), ec);
235         if( !ec ) service.release(); // Call was successful, transfer ownership
236         m_external_io_service = false;
237     }
238
239     /// Initialize asio transport with internal io_service
240     /**
241      * This method of initialization will allocate and use an internally managed
242      * io_service.
243      *
244      * @see init_asio(io_service_ptr ptr)
245      */
246     void init_asio() {
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
250         //       necessary.
251 #ifdef _WEBSOCKETPP_CPP11_MEMORY_
252         lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
253 #else
254         lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
255 #endif
256         init_asio( service.get() );
257         // If control got this far without an exception, then ownership has successfully been taken
258         service.release();
259         m_external_io_service = false;
260     }
261
262     /// Sets the tcp pre init handler
263     /**
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.
267      *
268      * @since 0.3.0
269      *
270      * @param h The handler to call on tcp pre init.
271      */
272     void set_tcp_pre_init_handler(tcp_init_handler h) {
273         m_tcp_pre_init_handler = h;
274     }
275
276     /// Sets the tcp pre init handler (deprecated)
277     /**
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.
281      *
282      * @deprecated Use set_tcp_pre_init_handler instead
283      *
284      * @param h The handler to call on tcp pre init.
285      */
286     void set_tcp_init_handler(tcp_init_handler h) {
287         set_tcp_pre_init_handler(h);
288     }
289
290     /// Sets the tcp post init handler
291     /**
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.
296      *
297      * @since 0.3.0
298      *
299      * @param h The handler to call on tcp post init.
300      */
301     void set_tcp_post_init_handler(tcp_init_handler h) {
302         m_tcp_post_init_handler = h;
303     }
304
305     /// Sets the maximum length of the queue of pending connections.
306     /**
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.
311      *
312      * For this value to take effect you may need to adjust operating system
313      * settings.
314      *
315      * New values affect future calls to listen only.
316      *
317      * A value of zero will use the operating system default. This is the
318      * default value.
319      *
320      * @since 0.3.0
321      *
322      * @param backlog The maximum length of the queue of pending connections
323      */
324     void set_listen_backlog(int backlog) {
325         m_listen_backlog = backlog;
326     }
327
328     /// Sets whether to use the SO_REUSEADDR flag when opening listening sockets
329     /**
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.
333      *
334      * New values affect future calls to listen only.
335      *
336      * The default is false.
337      *
338      * @since 0.3.0
339      *
340      * @param value Whether or not to use the SO_REUSEADDR option
341      */
342     void set_reuse_addr(bool value) {
343         m_reuse_addr = value;
344     }
345
346     /// Retrieve a reference to the endpoint's io_service
347     /**
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
350      * endpoint.
351      *
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.
354      *
355      * @return A reference to the endpoint's io_service
356      */
357     lib::asio::io_service & get_io_service() {
358         return *m_io_service;
359     }
360     
361     /// Get local TCP endpoint
362     /**
363      * Extracts the local endpoint from the acceptor. This represents the
364      * address that WebSocket++ is listening on.
365      *
366      * Sets a bad_descriptor error if the acceptor is not currently listening
367      * or otherwise unavailable.
368      * 
369      * @since 0.7.0
370      *
371      * @param ec Set to indicate what error occurred, if any.
372      * @return The local endpoint
373      */
374     lib::asio::ip::tcp::endpoint get_local_endpoint(lib::asio::error_code & ec) {
375         if (m_acceptor) {
376             return m_acceptor->local_endpoint(ec);
377         } else {
378             ec = lib::asio::error::make_error_code(lib::asio::error::bad_descriptor);
379             return lib::asio::ip::tcp::endpoint();
380         }
381     }
382
383     /// Set up endpoint for listening manually (exception free)
384     /**
385      * Bind the internal acceptor using the specified settings. The endpoint
386      * must have been initialized by calling init_asio before listening.
387      *
388      * @param ep An endpoint to read settings from
389      * @param ec Set to indicate what error occurred, if any.
390      */
391     void listen(lib::asio::ip::tcp::endpoint const & ep, lib::error_code & ec)
392     {
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);
398             return;
399         }
400
401         m_alog->write(log::alevel::devel,"asio::listen");
402
403         lib::asio::error_code bec;
404
405         m_acceptor->open(ep.protocol(),bec);
406         if (!bec) {
407             m_acceptor->set_option(lib::asio::socket_base::reuse_address(m_reuse_addr),bec);
408         }
409         if (!bec) {
410             m_acceptor->bind(ep,bec);
411         }
412         if (!bec) {
413             m_acceptor->listen(m_listen_backlog,bec);
414         }
415         if (bec) {
416             if (m_acceptor->is_open()) {
417                 m_acceptor->close();
418             }
419             log_err(log::elevel::info,"asio listen",bec);
420             ec = make_error_code(error::pass_through);
421         } else {
422             m_state = LISTENING;
423             ec = lib::error_code();
424         }
425     }
426
427     /// Set up endpoint for listening manually
428     /**
429      * Bind the internal acceptor using the settings specified by the endpoint e
430      *
431      * @param ep An endpoint to read settings from
432      */
433     void listen(lib::asio::ip::tcp::endpoint const & ep) {
434         lib::error_code ec;
435         listen(ep,ec);
436         if (ec) { throw exception(ec); }
437     }
438
439     /// Set up endpoint for listening with protocol and port (exception free)
440     /**
441      * Bind the internal acceptor using the given internet protocol and port.
442      * The endpoint must have been initialized by calling init_asio before
443      * listening.
444      *
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()
448      *
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.
452      */
453     template <typename InternetProtocol>
454     void listen(InternetProtocol const & internet_protocol, uint16_t port,
455         lib::error_code & ec)
456     {
457         lib::asio::ip::tcp::endpoint ep(internet_protocol, port);
458         listen(ep,ec);
459     }
460
461     /// Set up endpoint for listening with protocol and port
462     /**
463      * Bind the internal acceptor using the given internet protocol and port.
464      * The endpoint must have been initialized by calling init_asio before
465      * listening.
466      *
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()
470      *
471      * @param internet_protocol The internet protocol to use.
472      * @param port The port to listen on.
473      */
474     template <typename InternetProtocol>
475     void listen(InternetProtocol const & internet_protocol, uint16_t port)
476     {
477         lib::asio::ip::tcp::endpoint ep(internet_protocol, port);
478         listen(ep);
479     }
480
481     /// Set up endpoint for listening on a port (exception free)
482     /**
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.
486      *
487      * The endpoint must have been initialized by calling init_asio before
488      * listening.
489      *
490      * @param port The port to listen on.
491      * @param ec Set to indicate what error occurred, if any.
492      */
493     void listen(uint16_t port, lib::error_code & ec) {
494         listen(lib::asio::ip::tcp::v6(), port, ec);
495     }
496
497     /// Set up endpoint for listening on a port
498     /**
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.
502      *
503      * The endpoint must have been initialized by calling init_asio before
504      * listening.
505      *
506      * @param port The port to listen on.
507      * @param ec Set to indicate what error occurred, if any.
508      */
509     void listen(uint16_t port) {
510         listen(lib::asio::ip::tcp::v6(), port);
511     }
512
513     /// Set up endpoint for listening on a host and service (exception free)
514     /**
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
518      * constructors.
519      *
520      * The endpoint must have been initialized by calling init_asio before
521      * listening.
522      *
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.
528      */
529     void listen(std::string const & host, std::string const & service,
530         lib::error_code & ec)
531     {
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);
541             return;
542         }
543         listen(*endpoint_iterator,ec);
544     }
545
546     /// Set up endpoint for listening on a host and service
547     /**
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
551      * constructors.
552      *
553      * The endpoint must have been initialized by calling init_asio before
554      * listening.
555      *
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.
561      */
562     void listen(std::string const & host, std::string const & service)
563     {
564         lib::error_code ec;
565         listen(host,service,ec);
566         if (ec) { throw exception(ec); }
567     }
568
569     /// Stop listening (exception free)
570     /**
571      * Stop listening and accepting new connections. This will not end any
572      * existing connections.
573      *
574      * @since 0.3.0-alpha4
575      * @param ec A status code indicating an error, if any.
576      */
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);
583             return;
584         }
585
586         m_acceptor->close();
587         m_state = READY;
588         ec = lib::error_code();
589     }
590
591     /// Stop listening
592     /**
593      * Stop listening and accepting new connections. This will not end any
594      * existing connections.
595      *
596      * @since 0.3.0-alpha4
597      */
598     void stop_listening() {
599         lib::error_code ec;
600         stop_listening(ec);
601         if (ec) { throw exception(ec); }
602     }
603
604     /// Check if the endpoint is listening
605     /**
606      * @return Whether or not the endpoint is listening.
607      */
608     bool is_listening() const {
609         return (m_state == LISTENING);
610     }
611
612     /// wraps the run method of the internal io_service object
613     std::size_t run() {
614         return m_io_service->run();
615     }
616
617     /// wraps the run_one method of the internal io_service object
618     /**
619      * @since 0.3.0-alpha4
620      */
621     std::size_t run_one() {
622         return m_io_service->run_one();
623     }
624
625     /// wraps the stop method of the internal io_service object
626     void stop() {
627         m_io_service->stop();
628     }
629
630     /// wraps the poll method of the internal io_service object
631     std::size_t poll() {
632         return m_io_service->poll();
633     }
634
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();
638     }
639
640     /// wraps the reset method of the internal io_service object
641     void reset() {
642         m_io_service->reset();
643     }
644
645     /// wraps the stopped method of the internal io_service object
646     bool stopped() const {
647         return m_io_service->stopped();
648     }
649
650     /// Marks the endpoint as perpetual, stopping it from exiting when empty
651     /**
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`.
655      *
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
658      * started
659      *
660      * @since 0.3.0
661      */
662     void start_perpetual() {
663         m_work = lib::make_shared<lib::asio::io_service::work>(
664             lib::ref(*m_io_service)
665         );
666     }
667
668     /// Clears the endpoint's perpetual flag, allowing it to exit when empty
669     /**
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.
673      *
674      * @since 0.3.0
675      */
676     void stop_perpetual() {
677         m_work.reset();
678     }
679
680     /// Call back a function after a period of time.
681     /**
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.
686      *
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
690      * needed.
691      */
692     timer_ptr set_timer(long duration, timer_handler callback) {
693         timer_ptr new_timer = lib::make_shared<lib::asio::steady_timer>(
694             *m_io_service,
695              lib::asio::milliseconds(duration)
696         );
697
698         new_timer->async_wait(
699             lib::bind(
700                 &type::handle_timer,
701                 this,
702                 new_timer,
703                 callback,
704                 lib::placeholders::_1
705             )
706         );
707
708         return new_timer;
709     }
710
711     /// Timer handler
712     /**
713      * The timer pointer is included to ensure the timer isn't destroyed until
714      * after it has expired.
715      *
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.
719      */
720     void handle_timer(timer_ptr, timer_handler callback,
721         lib::asio::error_code const & ec)
722     {
723         if (ec) {
724             if (ec == lib::asio::error::operation_aborted) {
725                 callback(make_error_code(transport::error::operation_aborted));
726             } else {
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));
731             }
732         } else {
733             callback(lib::error_code());
734         }
735     }
736
737     /// Accept the next connection attempt and assign it to con (exception free)
738     /**
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.
742      */
743     void async_accept(transport_con_ptr tcon, accept_handler callback,
744         lib::error_code & ec)
745     {
746         if (m_state != LISTENING) {
747             using websocketpp::error::make_error_code;
748             ec = make_error_code(websocketpp::error::async_accept_not_listening);
749             return;
750         }
751
752         m_alog->write(log::alevel::devel, "asio::async_accept");
753
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,
759                     this,
760                     callback,
761                     lib::placeholders::_1
762                 ))
763             );
764         } else {
765             m_acceptor->async_accept(
766                 tcon->get_raw_socket(),
767                 lib::bind(
768                     &type::handle_accept,
769                     this,
770                     callback,
771                     lib::placeholders::_1
772                 )
773             );
774         }
775     }
776
777     /// Accept the next connection attempt and assign it to con.
778     /**
779      * @param tcon The connection to accept into.
780      * @param callback The function to call when the operation is complete.
781      */
782     void async_accept(transport_con_ptr tcon, accept_handler callback) {
783         lib::error_code ec;
784         async_accept(tcon,callback,ec);
785         if (ec) { throw exception(ec); }
786     }
787 protected:
788     /// Initialize logging
789     /**
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.
797      */
798     void init_logging(alog_type* a, elog_type* e) {
799         m_alog = a;
800         m_elog = e;
801     }
802
803     void handle_accept(accept_handler callback, lib::asio::error_code const & 
804         asio_ec)
805     {
806         lib::error_code ret_ec;
807
808         m_alog->write(log::alevel::devel, "asio::handle_accept");
809
810         if (asio_ec) {
811             if (asio_ec == lib::asio::errc::operation_canceled) {
812                 ret_ec = make_error_code(websocketpp::error::operation_canceled);
813             } else {
814                 log_err(log::elevel::info,"asio handle_accept",asio_ec);
815                 ret_ec = make_error_code(error::pass_through);
816             }
817         }
818
819         callback(ret_ec);
820     }
821
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;
826
827         // Create a resolver
828         if (!m_resolver) {
829             m_resolver = lib::make_shared<lib::asio::ip::tcp::resolver>(
830                 lib::ref(*m_io_service));
831         }
832
833         tcon->set_uri(u);
834
835         std::string proxy = tcon->get_proxy();
836         std::string host;
837         std::string port;
838
839         if (proxy.empty()) {
840             host = u->get_host();
841             port = u->get_port_str();
842         } else {
843             lib::error_code ec;
844
845             uri_ptr pu = lib::make_shared<uri>(proxy);
846
847             if (!pu->get_valid()) {
848                 cb(make_error_code(error::proxy_invalid));
849                 return;
850             }
851
852             ec = tcon->proxy_init(u->get_authority());
853             if (ec) {
854                 cb(ec);
855                 return;
856             }
857
858             host = pu->get_host();
859             port = pu->get_port_str();
860         }
861
862         tcp::resolver::query query(host,port);
863
864         if (m_alog->static_test(log::alevel::devel)) {
865             m_alog->write(log::alevel::devel,
866                 "starting async DNS resolve for "+host+":"+port);
867         }
868
869         timer_ptr dns_timer;
870
871         dns_timer = tcon->set_timer(
872             config::timeout_dns_resolve,
873             lib::bind(
874                 &type::handle_resolve_timeout,
875                 this,
876                 dns_timer,
877                 cb,
878                 lib::placeholders::_1
879             )
880         );
881
882         if (config::enable_multithreading) {
883             m_resolver->async_resolve(
884                 query,
885                 tcon->get_strand()->wrap(lib::bind(
886                     &type::handle_resolve,
887                     this,
888                     tcon,
889                     dns_timer,
890                     cb,
891                     lib::placeholders::_1,
892                     lib::placeholders::_2
893                 ))
894             );
895         } else {
896             m_resolver->async_resolve(
897                 query,
898                 lib::bind(
899                     &type::handle_resolve,
900                     this,
901                     tcon,
902                     dns_timer,
903                     cb,
904                     lib::placeholders::_1,
905                     lib::placeholders::_2
906                 )
907             );
908         }
909     }
910
911     /// DNS resolution timeout handler
912     /**
913      * The timer pointer is included to ensure the timer isn't destroyed until
914      * after it has expired.
915      *
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.
919      */
920     void handle_resolve_timeout(timer_ptr, connect_handler callback,
921         lib::error_code const & ec)
922     {
923         lib::error_code ret_ec;
924
925         if (ec) {
926             if (ec == transport::error::operation_aborted) {
927                 m_alog->write(log::alevel::devel,
928                     "asio handle_resolve_timeout timer cancelled");
929                 return;
930             }
931
932             log_err(log::elevel::devel,"asio handle_resolve_timeout",ec);
933             ret_ec = ec;
934         } else {
935             ret_ec = make_error_code(transport::error::timeout);
936         }
937
938         m_alog->write(log::alevel::devel,"DNS resolution timed out");
939         m_resolver->cancel();
940         callback(ret_ec);
941     }
942
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)
946     {
947         if (ec == lib::asio::error::operation_aborted ||
948             lib::asio::is_neg(dns_timer->expires_from_now()))
949         {
950             m_alog->write(log::alevel::devel,"async_resolve cancelled");
951             return;
952         }
953
954         dns_timer->cancel();
955
956         if (ec) {
957             log_err(log::elevel::info,"asio async_resolve",ec);
958             callback(make_error_code(error::pass_through));
959             return;
960         }
961
962         if (m_alog->static_test(log::alevel::devel)) {
963             std::stringstream s;
964             s << "Async DNS resolve successful. Results: ";
965
966             lib::asio::ip::tcp::resolver::iterator it, end;
967             for (it = iterator; it != end; ++it) {
968                 s << (*it).endpoint() << " ";
969             }
970
971             m_alog->write(log::alevel::devel,s.str());
972         }
973
974         m_alog->write(log::alevel::devel,"Starting async connect");
975
976         timer_ptr con_timer;
977
978         con_timer = tcon->set_timer(
979             config::timeout_connect,
980             lib::bind(
981                 &type::handle_connect_timeout,
982                 this,
983                 tcon,
984                 con_timer,
985                 callback,
986                 lib::placeholders::_1
987             )
988         );
989
990         if (config::enable_multithreading) {
991             lib::asio::async_connect(
992                 tcon->get_raw_socket(),
993                 iterator,
994                 tcon->get_strand()->wrap(lib::bind(
995                     &type::handle_connect,
996                     this,
997                     tcon,
998                     con_timer,
999                     callback,
1000                     lib::placeholders::_1
1001                 ))
1002             );
1003         } else {
1004             lib::asio::async_connect(
1005                 tcon->get_raw_socket(),
1006                 iterator,
1007                 lib::bind(
1008                     &type::handle_connect,
1009                     this,
1010                     tcon,
1011                     con_timer,
1012                     callback,
1013                     lib::placeholders::_1
1014                 )
1015             );
1016         }
1017     }
1018
1019     /// Asio connect timeout handler
1020     /**
1021      * The timer pointer is included to ensure the timer isn't destroyed until
1022      * after it has expired.
1023      *
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.
1028      */
1029     void handle_connect_timeout(transport_con_ptr tcon, timer_ptr,
1030         connect_handler callback, lib::error_code const & ec)
1031     {
1032         lib::error_code ret_ec;
1033
1034         if (ec) {
1035             if (ec == transport::error::operation_aborted) {
1036                 m_alog->write(log::alevel::devel,
1037                     "asio handle_connect_timeout timer cancelled");
1038                 return;
1039             }
1040
1041             log_err(log::elevel::devel,"asio handle_connect_timeout",ec);
1042             ret_ec = ec;
1043         } else {
1044             ret_ec = make_error_code(transport::error::timeout);
1045         }
1046
1047         m_alog->write(log::alevel::devel,"TCP connect timed out");
1048         tcon->cancel_socket_checked();
1049         callback(ret_ec);
1050     }
1051
1052     void handle_connect(transport_con_ptr tcon, timer_ptr con_timer,
1053         connect_handler callback, lib::asio::error_code const & ec)
1054     {
1055         if (ec == lib::asio::error::operation_aborted ||
1056             lib::asio::is_neg(con_timer->expires_from_now()))
1057         {
1058             m_alog->write(log::alevel::devel,"async_connect cancelled");
1059             return;
1060         }
1061
1062         con_timer->cancel();
1063
1064         if (ec) {
1065             log_err(log::elevel::info,"asio async_connect",ec);
1066             callback(make_error_code(error::pass_through));
1067             return;
1068         }
1069
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.");
1073         }
1074
1075         callback(lib::error_code());
1076     }
1077
1078     /// Initialize a connection
1079     /**
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
1083      * constructor.
1084      *
1085      * @param tcon A pointer to the transport portion of the connection.
1086      *
1087      * @return A status code indicating the success or failure of the operation
1088      */
1089     lib::error_code init(transport_con_ptr tcon) {
1090         m_alog->write(log::alevel::devel, "transport::asio::init");
1091
1092         // Initialize the connection socket component
1093         socket_type::init(lib::static_pointer_cast<socket_con_type,
1094             transport_con_type>(tcon));
1095
1096         lib::error_code ec;
1097
1098         ec = tcon->init_asio(m_io_service);
1099         if (ec) {return ec;}
1100
1101         tcon->set_tcp_pre_init_handler(m_tcp_pre_init_handler);
1102         tcon->set_tcp_post_init_handler(m_tcp_post_init_handler);
1103
1104         return lib::error_code();
1105     }
1106 private:
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());
1113     }
1114
1115     enum state {
1116         UNINITIALIZED = 0,
1117         READY = 1,
1118         LISTENING = 2
1119     };
1120
1121     // Handlers
1122     tcp_init_handler    m_tcp_pre_init_handler;
1123     tcp_init_handler    m_tcp_post_init_handler;
1124
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;
1130     work_ptr            m_work;
1131
1132     // Network constants
1133     int                 m_listen_backlog;
1134     bool                m_reuse_addr;
1135
1136     elog_type* m_elog;
1137     alog_type* m_alog;
1138
1139     // Transport state
1140     state               m_state;
1141 };
1142
1143 } // namespace asio
1144 } // namespace transport
1145 } // namespace websocketpp
1146
1147 #endif // WEBSOCKETPP_TRANSPORT_ASIO_HPP