/Casablanca/Release/libs/websocketpp/websocketpp/transport/asio/endpoint.hpp

https://github.com/MattPeterson1/MyCasablancaSample · C++ Header · 1074 lines · 590 code · 114 blank · 370 comment · 67 complexity · ffcaf0e235ac6e3f4801ba29d65a4140 MD5 · raw file

  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. #ifndef WEBSOCKETPP_TRANSPORT_ASIO_HPP
  28. #define WEBSOCKETPP_TRANSPORT_ASIO_HPP
  29. #include <websocketpp/common/functional.hpp>
  30. #include <websocketpp/logger/levels.hpp>
  31. #include <websocketpp/transport/base/endpoint.hpp>
  32. #include <websocketpp/transport/asio/connection.hpp>
  33. #include <websocketpp/transport/asio/security/none.hpp>
  34. #include <boost/asio.hpp>
  35. #include <boost/bind.hpp>
  36. #include <boost/system/error_code.hpp>
  37. #include <iostream>
  38. namespace websocketpp {
  39. namespace transport {
  40. namespace asio {
  41. /// Boost Asio based endpoint transport component
  42. /**
  43. * transport::asio::endpoint implements an endpoint transport component using
  44. * Boost ASIO.
  45. */
  46. template <typename config>
  47. class endpoint : public config::socket_type {
  48. public:
  49. /// Type of this endpoint transport component
  50. typedef endpoint<config> type;
  51. /// Type of the concurrency policy
  52. typedef typename config::concurrency_type concurrency_type;
  53. /// Type of the socket policy
  54. typedef typename config::socket_type socket_type;
  55. /// Type of the error logging policy
  56. typedef typename config::elog_type elog_type;
  57. /// Type of the access logging policy
  58. typedef typename config::alog_type alog_type;
  59. /// Type of the socket connection component
  60. typedef typename socket_type::socket_con_type socket_con_type;
  61. /// Type of a shared pointer to the socket connection component
  62. typedef typename socket_con_type::ptr socket_con_ptr;
  63. /// Type of the connection transport component associated with this
  64. /// endpoint transport component
  65. typedef asio::connection<config> transport_con_type;
  66. /// Type of a shared pointer to the connection transport component
  67. /// associated with this endpoint transport component
  68. typedef typename transport_con_type::ptr transport_con_ptr;
  69. /// Type of a pointer to the ASIO io_service being used
  70. typedef boost::asio::io_service* io_service_ptr;
  71. /// Type of a shared pointer to the acceptor being used
  72. typedef lib::shared_ptr<boost::asio::ip::tcp::acceptor> acceptor_ptr;
  73. /// Type of a shared pointer to the resolver being used
  74. typedef lib::shared_ptr<boost::asio::ip::tcp::resolver> resolver_ptr;
  75. /// Type of timer handle
  76. typedef lib::shared_ptr<boost::asio::deadline_timer> timer_ptr;
  77. /// Type of a shared pointer to an io_service work object
  78. typedef lib::shared_ptr<boost::asio::io_service::work> work_ptr;
  79. // generate and manage our own io_service
  80. explicit endpoint()
  81. : m_external_io_service(false)
  82. , m_listen_backlog(0)
  83. , m_reuse_addr(false)
  84. , m_state(UNINITIALIZED)
  85. {
  86. //std::cout << "transport::asio::endpoint constructor" << std::endl;
  87. }
  88. ~endpoint() {
  89. // clean up our io_service if we were initialized with an internal one.
  90. m_acceptor.reset();
  91. if (m_state != UNINITIALIZED && !m_external_io_service) {
  92. delete m_io_service;
  93. }
  94. }
  95. /// transport::asio objects are moveable but not copyable or assignable.
  96. /// The following code sets this situation up based on whether or not we
  97. /// have C++11 support or not
  98. #ifdef _WEBSOCKETPP_DELETED_FUNCTIONS_
  99. endpoint(const endpoint& src) = delete;
  100. endpoint& operator= (const endpoint & rhs) = delete;
  101. #else
  102. private:
  103. endpoint(const endpoint& src);
  104. endpoint& operator= (const endpoint & rhs);
  105. public:
  106. #endif
  107. #ifdef _WEBSOCKETPP_RVALUE_REFERENCES_
  108. endpoint (endpoint&& src)
  109. : m_io_service(src.m_io_service)
  110. , m_external_io_service(src.m_external_io_service)
  111. , m_acceptor(src.m_acceptor)
  112. , m_listen_backlog(boost::asio::socket_base::max_connections)
  113. , m_reuse_addr(src.m_reuse_addr)
  114. , m_state(src.m_state)
  115. {
  116. src.m_io_service = NULL;
  117. src.m_external_io_service = false;
  118. src.m_acceptor = NULL;
  119. src.m_state = UNINITIALIZED;
  120. }
  121. endpoint& operator= (const endpoint && rhs) {
  122. if (this != &rhs) {
  123. m_io_service = rhs.m_io_service;
  124. m_external_io_service = rhs.m_external_io_service;
  125. m_acceptor = rhs.m_acceptor;
  126. m_listen_backlog = rhs.m_listen_backlog
  127. m_reuse_addr = rhs.m_reuse_addr;
  128. m_state = rhs.m_state;
  129. rhs.m_io_service = NULL;
  130. rhs.m_external_io_service = false;
  131. rhs.m_acceptor = NULL;
  132. rhs.m_listen_backlog = boost::asio::socket_base::max_connections;
  133. rhs.m_state = UNINITIALIZED;
  134. }
  135. return *this;
  136. }
  137. #endif
  138. /// Return whether or not the endpoint produces secure connections.
  139. bool is_secure() const {
  140. return socket_type::is_secure();
  141. }
  142. /// initialize asio transport with external io_service (exception free)
  143. /**
  144. * Initialize the ASIO transport policy for this endpoint using the provided
  145. * io_service object. asio_init must be called exactly once on any endpoint
  146. * that uses transport::asio before it can be used.
  147. *
  148. * @param ptr A pointer to the io_service to use for asio events
  149. * @param ec Set to indicate what error occurred, if any.
  150. */
  151. void init_asio(io_service_ptr ptr, lib::error_code & ec) {
  152. if (m_state != UNINITIALIZED) {
  153. m_elog->write(log::elevel::library,
  154. "asio::init_asio called from the wrong state");
  155. using websocketpp::error::make_error_code;
  156. ec = make_error_code(websocketpp::error::invalid_state);
  157. return;
  158. }
  159. m_alog->write(log::alevel::devel,"asio::init_asio");
  160. m_io_service = ptr;
  161. m_external_io_service = true;
  162. m_acceptor.reset(new boost::asio::ip::tcp::acceptor(*m_io_service));
  163. m_state = READY;
  164. ec = lib::error_code();
  165. }
  166. /// initialize asio transport with external io_service
  167. /**
  168. * Initialize the ASIO transport policy for this endpoint using the provided
  169. * io_service object. asio_init must be called exactly once on any endpoint
  170. * that uses transport::asio before it can be used.
  171. *
  172. * @param ptr A pointer to the io_service to use for asio events
  173. */
  174. void init_asio(io_service_ptr ptr) {
  175. lib::error_code ec;
  176. init_asio(ptr,ec);
  177. if (ec) {
  178. throw ec;
  179. }
  180. }
  181. /// Initialize asio transport with internal io_service (exception free)
  182. /**
  183. * This method of initialization will allocate and use an internally managed
  184. * io_service.
  185. *
  186. * @see init_asio(io_service_ptr ptr)
  187. *
  188. * @param ec Set to indicate what error occurred, if any.
  189. */
  190. void init_asio(lib::error_code & ec) {
  191. init_asio(new boost::asio::io_service(),ec);
  192. m_external_io_service = false;
  193. }
  194. /// Initialize asio transport with internal io_service
  195. /**
  196. * This method of initialization will allocate and use an internally managed
  197. * io_service.
  198. *
  199. * @see init_asio(io_service_ptr ptr)
  200. */
  201. void init_asio() {
  202. init_asio(new boost::asio::io_service());
  203. m_external_io_service = false;
  204. }
  205. /// Sets the tcp pre init handler
  206. /**
  207. * The tcp pre init handler is called after the raw tcp connection has been
  208. * established but before any additional wrappers (proxy connects, TLS
  209. * handshakes, etc) have been performed.
  210. *
  211. * @since 0.4.0-alpha1
  212. *
  213. * @param h The handler to call on tcp pre init.
  214. */
  215. void set_tcp_pre_init_handler(tcp_init_handler h) {
  216. m_tcp_pre_init_handler = h;
  217. }
  218. /// Sets the tcp pre init handler (deprecated)
  219. /**
  220. * The tcp pre init handler is called after the raw tcp connection has been
  221. * established but before any additional wrappers (proxy connects, TLS
  222. * handshakes, etc) have been performed.
  223. *
  224. * @deprecated Use set_tcp_pre_init_handler instead
  225. *
  226. * @param h The handler to call on tcp pre init.
  227. */
  228. void set_tcp_init_handler(tcp_init_handler h) {
  229. set_tcp_pre_init_handler(h);
  230. }
  231. /// Sets the tcp post init handler
  232. /**
  233. * The tcp post init handler is called after the tcp connection has been
  234. * established and all additional wrappers (proxy connects, TLS handshakes,
  235. * etc have been performed. This is fired before any bytes are read or any
  236. * WebSocket specific handshake logic has been performed.
  237. *
  238. * @since 0.4.0-alpha1
  239. *
  240. * @param h The handler to call on tcp post init.
  241. */
  242. void set_tcp_post_init_handler(tcp_init_handler h) {
  243. m_tcp_post_init_handler = h;
  244. }
  245. /// Sets the maximum length of the queue of pending connections.
  246. /**
  247. * Sets the maximum length of the queue of pending connections. Increasing
  248. * this will allow WebSocket++ to queue additional incoming connections.
  249. * Setting it higher may prevent failed connections at high connection rates
  250. * but may cause additional latency.
  251. *
  252. * For this value to take effect you may need to adjust operating system
  253. * settings.
  254. *
  255. * New values affect future calls to listen only.
  256. *
  257. * A value of zero will use the operating system default. This is the
  258. * default value.
  259. *
  260. * @since 0.4.0-alpha1
  261. *
  262. * @param backlog The maximum length of the queue of pending connections
  263. */
  264. void set_listen_backlog(int backlog) {
  265. m_listen_backlog = backlog;
  266. }
  267. /// Sets whether or not to use the SO_REUSEADDR flag when opening a listening socket
  268. /**
  269. * Specifies whether or not to use the SO_REUSEADDR TCP socket option. What this flag
  270. * does depends on your operating system. Please consult operating system
  271. * documentation for more details.
  272. *
  273. * New values affect future calls to listen only.
  274. *
  275. * The default is false.
  276. *
  277. * @since 0.4.0-alpha1
  278. *
  279. * @param value Whether or not to use the SO_REUSEADDR option
  280. */
  281. void set_reuse_addr(bool value) {
  282. m_reuse_addr = value;
  283. }
  284. /// Retrieve a reference to the endpoint's io_service
  285. /**
  286. * The io_service may be an internal or external one. This may be used to
  287. * call methods of the io_service that are not explicitly wrapped by the
  288. * endpoint.
  289. *
  290. * This method is only valid after the endpoint has been initialized with
  291. * `init_asio`. No error will be returned if it isn't.
  292. *
  293. * @return A reference to the endpoint's io_service
  294. */
  295. boost::asio::io_service & get_io_service() {
  296. return *m_io_service;
  297. }
  298. /// Set up endpoint for listening manually (exception free)
  299. /**
  300. * Bind the internal acceptor using the specified settings. The endpoint
  301. * must have been initialized by calling init_asio before listening.
  302. *
  303. * @param ep An endpoint to read settings from
  304. * @param ec Set to indicate what error occurred, if any.
  305. */
  306. void listen(boost::asio::ip::tcp::endpoint const & ep, lib::error_code & ec)
  307. {
  308. if (m_state != READY) {
  309. m_elog->write(log::elevel::library,
  310. "asio::listen called from the wrong state");
  311. using websocketpp::error::make_error_code;
  312. ec = make_error_code(websocketpp::error::invalid_state);
  313. return;
  314. }
  315. m_alog->write(log::alevel::devel,"asio::listen");
  316. boost::system::error_code bec;
  317. m_acceptor->open(ep.protocol(),bec);
  318. if (!bec) {
  319. m_acceptor->set_option(boost::asio::socket_base::reuse_address(m_reuse_addr),bec);
  320. }
  321. if (!bec) {
  322. m_acceptor->bind(ep,bec);
  323. }
  324. if (!bec) {
  325. m_acceptor->listen(m_listen_backlog,bec);
  326. }
  327. if (bec) {
  328. log_err(log::elevel::info,"asio listen",bec);
  329. ec = make_error_code(error::pass_through);
  330. } else {
  331. m_state = LISTENING;
  332. ec = lib::error_code();
  333. }
  334. }
  335. /// Set up endpoint for listening manually
  336. /**
  337. * Bind the internal acceptor using the settings specified by the endpoint e
  338. *
  339. * @param ep An endpoint to read settings from
  340. */
  341. void listen(boost::asio::ip::tcp::endpoint const & ep) {
  342. lib::error_code ec;
  343. listen(ep,ec);
  344. if (ec) {
  345. throw ec;
  346. }
  347. }
  348. /// Set up endpoint for listening with protocol and port (exception free)
  349. /**
  350. * Bind the internal acceptor using the given internet protocol and port.
  351. * The endpoint must have been initialized by calling init_asio before
  352. * listening.
  353. *
  354. * Common options include:
  355. * - IPv6 with mapped IPv4 for dual stack hosts boost::asio::ip::tcp::v6()
  356. * - IPv4 only: boost::asio::ip::tcp::v4()
  357. *
  358. * @param internet_protocol The internet protocol to use.
  359. * @param port The port to listen on.
  360. * @param ec Set to indicate what error occurred, if any.
  361. */
  362. template <typename InternetProtocol>
  363. void listen(InternetProtocol const & internet_protocol, uint16_t port,
  364. lib::error_code & ec)
  365. {
  366. boost::asio::ip::tcp::endpoint ep(internet_protocol, port);
  367. listen(ep,ec);
  368. }
  369. /// Set up endpoint for listening with protocol and port
  370. /**
  371. * Bind the internal acceptor using the given internet protocol and port.
  372. * The endpoint must have been initialized by calling init_asio before
  373. * listening.
  374. *
  375. * Common options include:
  376. * - IPv6 with mapped IPv4 for dual stack hosts boost::asio::ip::tcp::v6()
  377. * - IPv4 only: boost::asio::ip::tcp::v4()
  378. *
  379. * @param internet_protocol The internet protocol to use.
  380. * @param port The port to listen on.
  381. */
  382. template <typename InternetProtocol>
  383. void listen(InternetProtocol const & internet_protocol, uint16_t port)
  384. {
  385. boost::asio::ip::tcp::endpoint ep(internet_protocol, port);
  386. listen(ep);
  387. }
  388. /// Set up endpoint for listening on a port (exception free)
  389. /**
  390. * Bind the internal acceptor using the given port. The IPv6 protocol with
  391. * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use
  392. * the overload that allows specifying the protocol explicitly.
  393. *
  394. * The endpoint must have been initialized by calling init_asio before
  395. * listening.
  396. *
  397. * @param port The port to listen on.
  398. * @param ec Set to indicate what error occurred, if any.
  399. */
  400. void listen(uint16_t port, lib::error_code & ec) {
  401. listen(boost::asio::ip::tcp::v6(), port, ec);
  402. }
  403. /// Set up endpoint for listening on a port
  404. /**
  405. * Bind the internal acceptor using the given port. The IPv6 protocol with
  406. * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use
  407. * the overload that allows specifying the protocol explicitly.
  408. *
  409. * The endpoint must have been initialized by calling init_asio before
  410. * listening.
  411. *
  412. * @param port The port to listen on.
  413. * @param ec Set to indicate what error occurred, if any.
  414. */
  415. void listen(uint16_t port) {
  416. listen(boost::asio::ip::tcp::v6(), port);
  417. }
  418. /// Set up endpoint for listening on a host and service (exception free)
  419. /**
  420. * Bind the internal acceptor using the given host and service. More details
  421. * about what host and service can be are available in the boost asio
  422. * documentation for ip::basic_resolver_query::basic_resolver_query's
  423. * constructors.
  424. *
  425. * The endpoint must have been initialized by calling init_asio before
  426. * listening.
  427. *
  428. * @param host A string identifying a location. May be a descriptive name or
  429. * a numeric address string.
  430. * @param service A string identifying the requested service. This may be a
  431. * descriptive name or a numeric string corresponding to a port number.
  432. * @param ec Set to indicate what error occurred, if any.
  433. */
  434. void listen(std::string const & host, std::string const & service,
  435. lib::error_code & ec)
  436. {
  437. using boost::asio::ip::tcp;
  438. tcp::resolver r(*m_io_service);
  439. tcp::resolver::query query(host, service);
  440. tcp::resolver::iterator endpoint_iterator = r.resolve(query);
  441. tcp::resolver::iterator end;
  442. if (endpoint_iterator == end) {
  443. m_elog->write(log::elevel::library,
  444. "asio::listen could not resolve the supplied host or service");
  445. ec = make_error_code(error::invalid_host_service);
  446. return;
  447. }
  448. listen(*endpoint_iterator,ec);
  449. }
  450. /// Set up endpoint for listening on a host and service
  451. /**
  452. * Bind the internal acceptor using the given host and service. More details
  453. * about what host and service can be are available in the boost asio
  454. * documentation for ip::basic_resolver_query::basic_resolver_query's
  455. * constructors.
  456. *
  457. * The endpoint must have been initialized by calling init_asio before
  458. * listening.
  459. *
  460. * @param host A string identifying a location. May be a descriptive name or
  461. * a numeric address string.
  462. * @param service A string identifying the requested service. This may be a
  463. * descriptive name or a numeric string corresponding to a port number.
  464. * @param ec Set to indicate what error occurred, if any.
  465. */
  466. void listen(std::string const & host, std::string const & service)
  467. {
  468. lib::error_code ec;
  469. listen(host,service,ec);
  470. if (ec) {
  471. throw ec;
  472. }
  473. }
  474. /// Stop listening (exception free)
  475. /**
  476. * Stop listening and accepting new connections. This will not end any
  477. * existing connections.
  478. *
  479. * @since 0.3.0-alpha4
  480. * @param ec A status code indicating an error, if any.
  481. */
  482. void stop_listening(lib::error_code & ec) {
  483. if (m_state != LISTENING) {
  484. m_elog->write(log::elevel::library,
  485. "asio::listen called from the wrong state");
  486. using websocketpp::error::make_error_code;
  487. ec = make_error_code(websocketpp::error::invalid_state);
  488. return;
  489. }
  490. m_acceptor->close();
  491. m_state = READY;
  492. ec = lib::error_code();
  493. }
  494. /// Stop listening
  495. /**
  496. * Stop listening and accepting new connections. This will not end any
  497. * existing connections.
  498. *
  499. * @since 0.3.0-alpha4
  500. */
  501. void stop_listening() {
  502. lib::error_code ec;
  503. stop_listening(ec);
  504. if (ec) {
  505. throw ec;
  506. }
  507. }
  508. /// Check if the endpoint is listening
  509. /**
  510. * @return Whether or not the endpoint is listening.
  511. */
  512. bool is_listening() const {
  513. return (m_state == LISTENING);
  514. }
  515. /// wraps the run method of the internal io_service object
  516. std::size_t run() {
  517. return m_io_service->run();
  518. }
  519. /// wraps the run_one method of the internal io_service object
  520. /**
  521. * @since 0.3.0-alpha4
  522. */
  523. std::size_t run_one() {
  524. return m_io_service->run_one();
  525. }
  526. /// wraps the stop method of the internal io_service object
  527. void stop() {
  528. m_io_service->stop();
  529. }
  530. /// wraps the poll method of the internal io_service object
  531. std::size_t poll() {
  532. return m_io_service->poll();
  533. }
  534. /// wraps the poll_one method of the internal io_service object
  535. std::size_t poll_one() {
  536. return m_io_service->poll_one();
  537. }
  538. /// wraps the reset method of the internal io_service object
  539. void reset() {
  540. m_io_service->reset();
  541. }
  542. /// wraps the stopped method of the internal io_service object
  543. bool stopped() const {
  544. return m_io_service->stopped();
  545. }
  546. /// Marks the endpoint as perpetual, stopping it from exiting when empty
  547. /**
  548. * Marks the endpoint as perpetual. Perpetual endpoints will not
  549. * automatically exit when they run out of connections to process. To stop
  550. * a perpetual endpoint call `end_perpetual`.
  551. *
  552. * An endpoint may be marked perpetual at any time by any thread. It must be
  553. * called either before the endpoint has run out of work or before it was
  554. * started
  555. *
  556. * @since 0.4.0-alpha1
  557. */
  558. void start_perpetual() {
  559. m_work.reset(new boost::asio::io_service::work(*m_io_service));
  560. }
  561. /// Clears the endpoint's perpetual flag, allowing it to exit when empty
  562. /**
  563. * Clears the endpoint's perpetual flag. This will cause the endpoint's run
  564. * method to exit normally when it runs out of connections. If there are
  565. * currently active connections it will not end until they are complete.
  566. *
  567. * @since 0.4.0-alpha1
  568. */
  569. void stop_perpetual() {
  570. m_work.reset();
  571. }
  572. /// Call back a function after a period of time.
  573. /**
  574. * Sets a timer that calls back a function after the specified period of
  575. * milliseconds. Returns a handle that can be used to cancel the timer.
  576. * A cancelled timer will return the error code error::operation_aborted
  577. * A timer that expired will return no error.
  578. *
  579. * @param duration Length of time to wait in milliseconds
  580. * @param callback The function to call back when the timer has expired
  581. * @return A handle that can be used to cancel the timer if it is no longer
  582. * needed.
  583. */
  584. timer_ptr set_timer(long duration, timer_handler callback) {
  585. timer_ptr new_timer(
  586. new boost::asio::deadline_timer(
  587. *m_io_service,
  588. boost::posix_time::milliseconds(duration)
  589. )
  590. );
  591. new_timer->async_wait(
  592. lib::bind(
  593. &type::handle_timer,
  594. this,
  595. new_timer,
  596. callback,
  597. lib::placeholders::_1
  598. )
  599. );
  600. return new_timer;
  601. }
  602. /// Timer callback
  603. /**
  604. * The timer pointer is included to ensure the timer isn't destroyed until
  605. * after it has expired.
  606. *
  607. * @param t Pointer to the timer in question
  608. * @param callback The function to call back
  609. * @param ec A status code indicating an error, if any.
  610. */
  611. void handle_timer(timer_ptr t, timer_handler callback,
  612. boost::system::error_code const & ec)
  613. {
  614. if (ec) {
  615. if (ec == boost::asio::error::operation_aborted) {
  616. callback(make_error_code(transport::error::operation_aborted));
  617. } else {
  618. m_elog->write(log::elevel::info,
  619. "asio handle_timer error: "+ec.message());
  620. log_err(log::elevel::info,"asio handle_timer",ec);
  621. callback(make_error_code(error::pass_through));
  622. }
  623. } else {
  624. callback(lib::error_code());
  625. }
  626. }
  627. /// Accept the next connection attempt and assign it to con (exception free)
  628. /**
  629. * @param tcon The connection to accept into.
  630. * @param callback The function to call when the operation is complete.
  631. * @param ec A status code indicating an error, if any.
  632. */
  633. void async_accept(transport_con_ptr tcon, accept_handler callback,
  634. lib::error_code & ec)
  635. {
  636. if (m_state != LISTENING) {
  637. using websocketpp::error::make_error_code;
  638. ec = make_error_code(websocketpp::error::async_accept_not_listening);
  639. return;
  640. }
  641. m_alog->write(log::alevel::devel, "asio::async_accept");
  642. if (config::enable_multithreading) {
  643. m_acceptor->async_accept(
  644. tcon->get_raw_socket(),
  645. tcon->get_strand()->wrap(lib::bind(
  646. &type::handle_accept,
  647. this,
  648. callback,
  649. lib::placeholders::_1
  650. ))
  651. );
  652. } else {
  653. m_acceptor->async_accept(
  654. tcon->get_raw_socket(),
  655. lib::bind(
  656. &type::handle_accept,
  657. this,
  658. callback,
  659. lib::placeholders::_1
  660. )
  661. );
  662. }
  663. }
  664. /// Accept the next connection attempt and assign it to con.
  665. /**
  666. * @param tcon The connection to accept into.
  667. * @param callback The function to call when the operation is complete.
  668. */
  669. void async_accept(transport_con_ptr tcon, accept_handler callback) {
  670. lib::error_code ec;
  671. async_accept(tcon,callback,ec);
  672. if (ec) {
  673. throw ec;
  674. }
  675. }
  676. protected:
  677. /// Initialize logging
  678. /**
  679. * The loggers are located in the main endpoint class. As such, the
  680. * transport doesn't have direct access to them. This method is called
  681. * by the endpoint constructor to allow shared logging from the transport
  682. * component. These are raw pointers to member variables of the endpoint.
  683. * In particular, they cannot be used in the transport constructor as they
  684. * haven't been constructed yet, and cannot be used in the transport
  685. * destructor as they will have been destroyed by then.
  686. */
  687. void init_logging(alog_type* a, elog_type* e) {
  688. m_alog = a;
  689. m_elog = e;
  690. }
  691. void handle_accept(accept_handler callback, boost::system::error_code const
  692. & boost_ec)
  693. {
  694. lib::error_code ret_ec;
  695. m_alog->write(log::alevel::devel, "asio::handle_accept");
  696. if (boost_ec) {
  697. if (boost_ec == boost::system::errc::operation_canceled) {
  698. ret_ec = make_error_code(websocketpp::error::operation_canceled);
  699. } else {
  700. log_err(log::elevel::info,"asio handle_accept",boost_ec);
  701. ret_ec = make_error_code(error::pass_through);
  702. }
  703. }
  704. callback(ret_ec);
  705. }
  706. /// Initiate a new connection
  707. // TODO: there have to be some more failure conditions here
  708. void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) {
  709. using namespace boost::asio::ip;
  710. // Create a resolver
  711. if (!m_resolver) {
  712. m_resolver.reset(new boost::asio::ip::tcp::resolver(*m_io_service));
  713. }
  714. std::string proxy = tcon->get_proxy();
  715. std::string host;
  716. std::string port;
  717. if (proxy.empty()) {
  718. host = u->get_host();
  719. port = u->get_port_str();
  720. } else {
  721. lib::error_code ec;
  722. uri_ptr pu(new uri(proxy));
  723. if (!pu->get_valid()) {
  724. cb(make_error_code(error::proxy_invalid));
  725. return;
  726. }
  727. ec = tcon->proxy_init(u->get_authority());
  728. if (ec) {
  729. cb(ec);
  730. return;
  731. }
  732. host = pu->get_host();
  733. port = pu->get_port_str();
  734. }
  735. tcp::resolver::query query(host,port);
  736. if (m_alog->static_test(log::alevel::devel)) {
  737. m_alog->write(log::alevel::devel,
  738. "starting async DNS resolve for "+host+":"+port);
  739. }
  740. timer_ptr dns_timer;
  741. dns_timer = tcon->set_timer(
  742. config::timeout_dns_resolve,
  743. lib::bind(
  744. &type::handle_resolve_timeout,
  745. this,
  746. dns_timer,
  747. cb,
  748. lib::placeholders::_1
  749. )
  750. );
  751. if (config::enable_multithreading) {
  752. m_resolver->async_resolve(
  753. query,
  754. tcon->get_strand()->wrap(lib::bind(
  755. &type::handle_resolve,
  756. this,
  757. tcon,
  758. dns_timer,
  759. cb,
  760. lib::placeholders::_1,
  761. lib::placeholders::_2
  762. ))
  763. );
  764. } else {
  765. m_resolver->async_resolve(
  766. query,
  767. lib::bind(
  768. &type::handle_resolve,
  769. this,
  770. tcon,
  771. dns_timer,
  772. cb,
  773. lib::placeholders::_1,
  774. lib::placeholders::_2
  775. )
  776. );
  777. }
  778. }
  779. void handle_resolve_timeout(timer_ptr dns_timer, connect_handler callback,
  780. lib::error_code const & ec)
  781. {
  782. lib::error_code ret_ec;
  783. if (ec) {
  784. if (ec == transport::error::operation_aborted) {
  785. m_alog->write(log::alevel::devel,
  786. "asio handle_resolve_timeout timer cancelled");
  787. return;
  788. }
  789. log_err(log::elevel::devel,"asio handle_resolve_timeout",ec);
  790. ret_ec = ec;
  791. } else {
  792. ret_ec = make_error_code(transport::error::timeout);
  793. }
  794. m_alog->write(log::alevel::devel,"DNS resolution timed out");
  795. m_resolver->cancel();
  796. callback(ret_ec);
  797. }
  798. void handle_resolve(transport_con_ptr tcon, timer_ptr dns_timer,
  799. connect_handler callback, boost::system::error_code const & ec,
  800. boost::asio::ip::tcp::resolver::iterator iterator)
  801. {
  802. if (ec == boost::asio::error::operation_aborted ||
  803. dns_timer->expires_from_now().is_negative())
  804. {
  805. m_alog->write(log::alevel::devel,"async_resolve cancelled");
  806. return;
  807. }
  808. dns_timer->cancel();
  809. if (ec) {
  810. log_err(log::elevel::info,"asio async_resolve",ec);
  811. callback(make_error_code(error::pass_through));
  812. return;
  813. }
  814. if (m_alog->static_test(log::alevel::devel)) {
  815. std::stringstream s;
  816. s << "Async DNS resolve successful. Results: ";
  817. boost::asio::ip::tcp::resolver::iterator it, end;
  818. for (it = iterator; it != end; ++it) {
  819. s << (*it).endpoint() << " ";
  820. }
  821. m_alog->write(log::alevel::devel,s.str());
  822. }
  823. m_alog->write(log::alevel::devel,"Starting async connect");
  824. timer_ptr con_timer;
  825. con_timer = tcon->set_timer(
  826. config::timeout_connect,
  827. lib::bind(
  828. &type::handle_connect_timeout,
  829. this,
  830. tcon,
  831. con_timer,
  832. callback,
  833. lib::placeholders::_1
  834. )
  835. );
  836. if (config::enable_multithreading) {
  837. boost::asio::async_connect(
  838. tcon->get_raw_socket(),
  839. iterator,
  840. tcon->get_strand()->wrap(lib::bind(
  841. &type::handle_connect,
  842. this,
  843. tcon,
  844. con_timer,
  845. callback,
  846. lib::placeholders::_1
  847. ))
  848. );
  849. } else {
  850. boost::asio::async_connect(
  851. tcon->get_raw_socket(),
  852. iterator,
  853. lib::bind(
  854. &type::handle_connect,
  855. this,
  856. tcon,
  857. con_timer,
  858. callback,
  859. lib::placeholders::_1
  860. )
  861. );
  862. }
  863. }
  864. void handle_connect_timeout(transport_con_ptr tcon, timer_ptr con_timer,
  865. connect_handler callback, lib::error_code const & ec)
  866. {
  867. lib::error_code ret_ec;
  868. if (ec) {
  869. if (ec == transport::error::operation_aborted) {
  870. m_alog->write(log::alevel::devel,
  871. "asio handle_connect_timeout timer cancelled");
  872. return;
  873. }
  874. log_err(log::elevel::devel,"asio handle_connect_timeout",ec);
  875. ret_ec = ec;
  876. } else {
  877. ret_ec = make_error_code(transport::error::timeout);
  878. }
  879. m_alog->write(log::alevel::devel,"TCP connect timed out");
  880. tcon->cancel_socket();
  881. callback(ret_ec);
  882. }
  883. void handle_connect(transport_con_ptr tcon, timer_ptr con_timer,
  884. connect_handler callback, boost::system::error_code const & ec)
  885. {
  886. if (ec == boost::asio::error::operation_aborted ||
  887. con_timer->expires_from_now().is_negative())
  888. {
  889. m_alog->write(log::alevel::devel,"async_connect cancelled");
  890. return;
  891. }
  892. con_timer->cancel();
  893. if (ec) {
  894. log_err(log::elevel::info,"asio async_connect",ec);
  895. callback(make_error_code(error::pass_through));
  896. return;
  897. }
  898. if (m_alog->static_test(log::alevel::devel)) {
  899. m_alog->write(log::alevel::devel,
  900. "Async connect to "+tcon->get_remote_endpoint()+" successful.");
  901. }
  902. callback(lib::error_code());
  903. }
  904. /// Initialize a connection
  905. /**
  906. * init is called by an endpoint once for each newly created connection.
  907. * It's purpose is to give the transport policy the chance to perform any
  908. * transport specific initialization that couldn't be done via the default
  909. * constructor.
  910. *
  911. * @param tcon A pointer to the transport portion of the connection.
  912. *
  913. * @return A status code indicating the success or failure of the operation
  914. */
  915. lib::error_code init(transport_con_ptr tcon) {
  916. m_alog->write(log::alevel::devel, "transport::asio::init");
  917. // Initialize the connection socket component
  918. socket_type::init(lib::static_pointer_cast<socket_con_type,
  919. transport_con_type>(tcon));
  920. lib::error_code ec;
  921. ec = tcon->init_asio(m_io_service);
  922. if (ec) {return ec;}
  923. tcon->set_tcp_pre_init_handler(m_tcp_pre_init_handler);
  924. tcon->set_tcp_post_init_handler(m_tcp_post_init_handler);
  925. return lib::error_code();
  926. }
  927. private:
  928. /// Convenience method for logging the code and message for an error_code
  929. template <typename error_type>
  930. void log_err(log::level l, char const * msg, error_type const & ec) {
  931. std::stringstream s;
  932. s << msg << " error: " << ec << " (" << ec.message() << ")";
  933. m_elog->write(l,s.str());
  934. }
  935. enum state {
  936. UNINITIALIZED = 0,
  937. READY = 1,
  938. LISTENING = 2
  939. };
  940. // Handlers
  941. tcp_init_handler m_tcp_pre_init_handler;
  942. tcp_init_handler m_tcp_post_init_handler;
  943. // Network Resources
  944. io_service_ptr m_io_service;
  945. bool m_external_io_service;
  946. acceptor_ptr m_acceptor;
  947. resolver_ptr m_resolver;
  948. work_ptr m_work;
  949. // Network constants
  950. int m_listen_backlog;
  951. bool m_reuse_addr;
  952. elog_type* m_elog;
  953. alog_type* m_alog;
  954. // Transport state
  955. state m_state;
  956. };
  957. } // namespace asio
  958. } // namespace transport
  959. } // namespace websocketpp
  960. #endif // WEBSOCKETPP_TRANSPORT_ASIO_HPP