endpoint.hpp 40 KB

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