endpoint.hpp 37 KB

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