connection.hpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197
  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_CON_HPP
  28. #define WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
  29. #include <websocketpp/transport/asio/base.hpp>
  30. #include <websocketpp/transport/base/connection.hpp>
  31. #include <websocketpp/logger/levels.hpp>
  32. #include <websocketpp/http/constants.hpp>
  33. #include <websocketpp/base64/base64.hpp>
  34. #include <websocketpp/error.hpp>
  35. #include <websocketpp/uri.hpp>
  36. #include <websocketpp/common/asio.hpp>
  37. #include <websocketpp/common/chrono.hpp>
  38. #include <websocketpp/common/cpp11.hpp>
  39. #include <websocketpp/common/memory.hpp>
  40. #include <websocketpp/common/functional.hpp>
  41. #include <websocketpp/common/connection_hdl.hpp>
  42. #include <istream>
  43. #include <sstream>
  44. #include <string>
  45. #include <vector>
  46. namespace websocketpp {
  47. namespace transport {
  48. namespace asio {
  49. typedef lib::function<void(connection_hdl)> tcp_init_handler;
  50. /// Asio based connection transport component
  51. /**
  52. * transport::asio::connection implements a connection transport component using
  53. * Asio that works with the transport::asio::endpoint endpoint transport
  54. * component.
  55. */
  56. template <typename config>
  57. class connection : public config::socket_type::socket_con_type {
  58. public:
  59. /// Type of this connection transport component
  60. typedef connection<config> type;
  61. /// Type of a shared pointer to this connection transport component
  62. typedef lib::shared_ptr<type> ptr;
  63. /// Type of the socket connection component
  64. typedef typename config::socket_type::socket_con_type socket_con_type;
  65. /// Type of a shared pointer to the socket connection component
  66. typedef typename socket_con_type::ptr socket_con_ptr;
  67. /// Type of this transport's access logging policy
  68. typedef typename config::alog_type alog_type;
  69. /// Type of this transport's error logging policy
  70. typedef typename config::elog_type elog_type;
  71. typedef typename config::request_type request_type;
  72. typedef typename request_type::ptr request_ptr;
  73. typedef typename config::response_type response_type;
  74. typedef typename response_type::ptr response_ptr;
  75. /// Type of a pointer to the Asio io_service being used
  76. typedef lib::asio::io_service * io_service_ptr;
  77. /// Type of a pointer to the Asio io_service::strand being used
  78. typedef lib::shared_ptr<lib::asio::io_service::strand> strand_ptr;
  79. /// Type of a pointer to the Asio timer class
  80. typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
  81. // connection is friends with its associated endpoint to allow the endpoint
  82. // to call private/protected utility methods that we don't want to expose
  83. // to the public api.
  84. friend class endpoint<config>;
  85. // generate and manage our own io_service
  86. explicit connection(bool is_server, const lib::shared_ptr<alog_type> & alog, const lib::shared_ptr<elog_type> & elog)
  87. : m_is_server(is_server)
  88. , m_alog(alog)
  89. , m_elog(elog)
  90. {
  91. m_alog->write(log::alevel::devel,"asio con transport constructor");
  92. }
  93. /// Get a shared pointer to this component
  94. ptr get_shared() {
  95. return lib::static_pointer_cast<type>(socket_con_type::get_shared());
  96. }
  97. bool is_secure() const {
  98. return socket_con_type::is_secure();
  99. }
  100. /// Set uri hook
  101. /**
  102. * Called by the endpoint as a connection is being established to provide
  103. * the uri being connected to to the transport layer.
  104. *
  105. * This transport policy doesn't use the uri except to forward it to the
  106. * socket layer.
  107. *
  108. * @since 0.6.0
  109. *
  110. * @param u The uri to set
  111. */
  112. void set_uri(uri_ptr u) {
  113. socket_con_type::set_uri(u);
  114. }
  115. /// Sets the tcp pre init handler
  116. /**
  117. * The tcp pre init handler is called after the raw tcp connection has been
  118. * established but before any additional wrappers (proxy connects, TLS
  119. * handshakes, etc) have been performed.
  120. *
  121. * @since 0.3.0
  122. *
  123. * @param h The handler to call on tcp pre init.
  124. */
  125. void set_tcp_pre_init_handler(tcp_init_handler h) {
  126. m_tcp_pre_init_handler = h;
  127. }
  128. /// Sets the tcp pre init handler (deprecated)
  129. /**
  130. * The tcp pre init handler is called after the raw tcp connection has been
  131. * established but before any additional wrappers (proxy connects, TLS
  132. * handshakes, etc) have been performed.
  133. *
  134. * @deprecated Use set_tcp_pre_init_handler instead
  135. *
  136. * @param h The handler to call on tcp pre init.
  137. */
  138. void set_tcp_init_handler(tcp_init_handler h) {
  139. set_tcp_pre_init_handler(h);
  140. }
  141. /// Sets the tcp post init handler
  142. /**
  143. * The tcp post init handler is called after the tcp connection has been
  144. * established and all additional wrappers (proxy connects, TLS handshakes,
  145. * etc have been performed. This is fired before any bytes are read or any
  146. * WebSocket specific handshake logic has been performed.
  147. *
  148. * @since 0.3.0
  149. *
  150. * @param h The handler to call on tcp post init.
  151. */
  152. void set_tcp_post_init_handler(tcp_init_handler h) {
  153. m_tcp_post_init_handler = h;
  154. }
  155. /// Set the proxy to connect through (exception free)
  156. /**
  157. * The URI passed should be a complete URI including scheme. For example:
  158. * http://proxy.example.com:8080/
  159. *
  160. * The proxy must be set up as an explicit (CONNECT) proxy allowed to
  161. * connect to the port you specify. Traffic to the proxy is not encrypted.
  162. *
  163. * @param uri The full URI of the proxy to connect to.
  164. *
  165. * @param ec A status value
  166. */
  167. void set_proxy(std::string const & uri, lib::error_code & ec) {
  168. // TODO: return errors for illegal URIs here?
  169. // TODO: should https urls be illegal for the moment?
  170. m_proxy = uri;
  171. m_proxy_data = lib::make_shared<proxy_data>();
  172. ec = lib::error_code();
  173. }
  174. /// Set the proxy to connect through (exception)
  175. void set_proxy(std::string const & uri) {
  176. lib::error_code ec;
  177. set_proxy(uri,ec);
  178. if (ec) { throw exception(ec); }
  179. }
  180. /// Set the basic auth credentials to use (exception free)
  181. /**
  182. * The URI passed should be a complete URI including scheme. For example:
  183. * http://proxy.example.com:8080/
  184. *
  185. * The proxy must be set up as an explicit proxy
  186. *
  187. * @param username The username to send
  188. *
  189. * @param password The password to send
  190. *
  191. * @param ec A status value
  192. */
  193. void set_proxy_basic_auth(std::string const & username, std::string const &
  194. password, lib::error_code & ec)
  195. {
  196. if (!m_proxy_data) {
  197. ec = make_error_code(websocketpp::error::invalid_state);
  198. return;
  199. }
  200. // TODO: username can't contain ':'
  201. std::string val = "Basic "+base64_encode(username + ":" + password);
  202. m_proxy_data->req.replace_header("Proxy-Authorization",val);
  203. ec = lib::error_code();
  204. }
  205. /// Set the basic auth credentials to use (exception)
  206. void set_proxy_basic_auth(std::string const & username, std::string const &
  207. password)
  208. {
  209. lib::error_code ec;
  210. set_proxy_basic_auth(username,password,ec);
  211. if (ec) { throw exception(ec); }
  212. }
  213. /// Set the proxy timeout duration (exception free)
  214. /**
  215. * Duration is in milliseconds. Default value is based on the transport
  216. * config
  217. *
  218. * @param duration The number of milliseconds to wait before aborting the
  219. * proxy connection.
  220. *
  221. * @param ec A status value
  222. */
  223. void set_proxy_timeout(long duration, lib::error_code & ec) {
  224. if (!m_proxy_data) {
  225. ec = make_error_code(websocketpp::error::invalid_state);
  226. return;
  227. }
  228. m_proxy_data->timeout_proxy = duration;
  229. ec = lib::error_code();
  230. }
  231. /// Set the proxy timeout duration (exception)
  232. void set_proxy_timeout(long duration) {
  233. lib::error_code ec;
  234. set_proxy_timeout(duration,ec);
  235. if (ec) { throw exception(ec); }
  236. }
  237. std::string const & get_proxy() const {
  238. return m_proxy;
  239. }
  240. /// Get the remote endpoint address
  241. /**
  242. * The iostream transport has no information about the ultimate remote
  243. * endpoint. It will return the string "iostream transport". To indicate
  244. * this.
  245. *
  246. * TODO: allow user settable remote endpoint addresses if this seems useful
  247. *
  248. * @return A string identifying the address of the remote endpoint
  249. */
  250. std::string get_remote_endpoint() const {
  251. lib::error_code ec;
  252. std::string ret = socket_con_type::get_remote_endpoint(ec);
  253. if (ec) {
  254. m_elog->write(log::elevel::info,ret);
  255. return "Unknown";
  256. } else {
  257. return ret;
  258. }
  259. }
  260. /// Get the connection handle
  261. connection_hdl get_handle() const {
  262. return m_connection_hdl;
  263. }
  264. /// Call back a function after a period of time.
  265. /**
  266. * Sets a timer that calls back a function after the specified period of
  267. * milliseconds. Returns a handle that can be used to cancel the timer.
  268. * A cancelled timer will return the error code error::operation_aborted
  269. * A timer that expired will return no error.
  270. *
  271. * @param duration Length of time to wait in milliseconds
  272. *
  273. * @param callback The function to call back when the timer has expired
  274. *
  275. * @return A handle that can be used to cancel the timer if it is no longer
  276. * needed.
  277. */
  278. timer_ptr set_timer(long duration, timer_handler callback) {
  279. timer_ptr new_timer(
  280. new lib::asio::steady_timer(
  281. *m_io_service,
  282. lib::asio::milliseconds(duration))
  283. );
  284. if (config::enable_multithreading) {
  285. new_timer->async_wait(m_strand->wrap(lib::bind(
  286. &type::handle_timer, get_shared(),
  287. new_timer,
  288. callback,
  289. lib::placeholders::_1
  290. )));
  291. } else {
  292. new_timer->async_wait(lib::bind(
  293. &type::handle_timer, get_shared(),
  294. new_timer,
  295. callback,
  296. lib::placeholders::_1
  297. ));
  298. }
  299. return new_timer;
  300. }
  301. /// Timer callback
  302. /**
  303. * The timer pointer is included to ensure the timer isn't destroyed until
  304. * after it has expired.
  305. *
  306. * TODO: candidate for protected status
  307. *
  308. * @param post_timer Pointer to the timer in question
  309. * @param callback The function to call back
  310. * @param ec The status code
  311. */
  312. void handle_timer(timer_ptr, timer_handler callback,
  313. lib::asio::error_code const & ec)
  314. {
  315. if (ec) {
  316. if (ec == lib::asio::error::operation_aborted) {
  317. callback(make_error_code(transport::error::operation_aborted));
  318. } else {
  319. log_err(log::elevel::info,"asio handle_timer",ec);
  320. callback(make_error_code(error::pass_through));
  321. }
  322. } else {
  323. callback(lib::error_code());
  324. }
  325. }
  326. /// Get a pointer to this connection's strand
  327. strand_ptr get_strand() {
  328. return m_strand;
  329. }
  330. /// Get the internal transport error code for a closed/failed connection
  331. /**
  332. * Retrieves a machine readable detailed error code indicating the reason
  333. * that the connection was closed or failed. Valid only after the close or
  334. * fail handler is called.
  335. *
  336. * Primarily used if you are using mismatched asio / system_error
  337. * implementations such as `boost::asio` with `std::system_error`. In these
  338. * cases the transport error type is different than the library error type
  339. * and some WebSocket++ functions that return transport errors via the
  340. * library error code type will be coerced into a catch all `pass_through`
  341. * or `tls_error` error. This method will return the original machine
  342. * readable transport error in the native type.
  343. *
  344. * @since 0.7.0
  345. *
  346. * @return Error code indicating the reason the connection was closed or
  347. * failed
  348. */
  349. lib::asio::error_code get_transport_ec() const {
  350. return m_tec;
  351. }
  352. /// Initialize transport for reading
  353. /**
  354. * init_asio is called once immediately after construction to initialize
  355. * Asio components to the io_service
  356. *
  357. * The transport initialization sequence consists of the following steps:
  358. * - Pre-init: the underlying socket is initialized to the point where
  359. * bytes may be written. No bytes are actually written in this stage
  360. * - Proxy negotiation: if a proxy is set, a request is made to it to start
  361. * a tunnel to the final destination. This stage ends when the proxy is
  362. * ready to forward the
  363. * next byte to the remote endpoint.
  364. * - Post-init: Perform any i/o with the remote endpoint, such as setting up
  365. * tunnels for encryption. This stage ends when the connection is ready to
  366. * read or write the WebSocket handshakes. At this point the original
  367. * callback function is called.
  368. */
  369. protected:
  370. void init(init_handler callback) {
  371. if (m_alog->static_test(log::alevel::devel)) {
  372. m_alog->write(log::alevel::devel,"asio connection init");
  373. }
  374. // TODO: pre-init timeout. Right now no implemented socket policies
  375. // actually have an asyncronous pre-init
  376. socket_con_type::pre_init(
  377. lib::bind(
  378. &type::handle_pre_init,
  379. get_shared(),
  380. callback,
  381. lib::placeholders::_1
  382. )
  383. );
  384. }
  385. /// initialize the proxy buffers and http parsers
  386. /**
  387. *
  388. * @param authority The address of the server we want the proxy to tunnel to
  389. * in the format of a URI authority (host:port)
  390. *
  391. * @return Status code indicating what errors occurred, if any
  392. */
  393. lib::error_code proxy_init(std::string const & authority) {
  394. if (!m_proxy_data) {
  395. return websocketpp::error::make_error_code(
  396. websocketpp::error::invalid_state);
  397. }
  398. m_proxy_data->req.set_version("HTTP/1.1");
  399. m_proxy_data->req.set_method("CONNECT");
  400. m_proxy_data->req.set_uri(authority);
  401. m_proxy_data->req.replace_header("Host",authority);
  402. return lib::error_code();
  403. }
  404. /// Finish constructing the transport
  405. /**
  406. * init_asio is called once immediately after construction to initialize
  407. * Asio components to the io_service.
  408. *
  409. * @param io_service A pointer to the io_service to register with this
  410. * connection
  411. *
  412. * @return Status code for the success or failure of the initialization
  413. */
  414. lib::error_code init_asio (io_service_ptr io_service) {
  415. m_io_service = io_service;
  416. if (config::enable_multithreading) {
  417. m_strand.reset(new lib::asio::io_service::strand(*io_service));
  418. }
  419. lib::error_code ec = socket_con_type::init_asio(io_service, m_strand,
  420. m_is_server);
  421. return ec;
  422. }
  423. void handle_pre_init(init_handler callback, lib::error_code const & ec) {
  424. if (m_alog->static_test(log::alevel::devel)) {
  425. m_alog->write(log::alevel::devel,"asio connection handle pre_init");
  426. }
  427. if (m_tcp_pre_init_handler) {
  428. m_tcp_pre_init_handler(m_connection_hdl);
  429. }
  430. if (ec) {
  431. callback(ec);
  432. }
  433. // If we have a proxy set issue a proxy connect, otherwise skip to
  434. // post_init
  435. if (!m_proxy.empty()) {
  436. proxy_write(callback);
  437. } else {
  438. post_init(callback);
  439. }
  440. }
  441. void post_init(init_handler callback) {
  442. if (m_alog->static_test(log::alevel::devel)) {
  443. m_alog->write(log::alevel::devel,"asio connection post_init");
  444. }
  445. timer_ptr post_timer;
  446. if (config::timeout_socket_post_init > 0) {
  447. post_timer = set_timer(
  448. config::timeout_socket_post_init,
  449. lib::bind(
  450. &type::handle_post_init_timeout,
  451. get_shared(),
  452. post_timer,
  453. callback,
  454. lib::placeholders::_1
  455. )
  456. );
  457. }
  458. socket_con_type::post_init(
  459. lib::bind(
  460. &type::handle_post_init,
  461. get_shared(),
  462. post_timer,
  463. callback,
  464. lib::placeholders::_1
  465. )
  466. );
  467. }
  468. /// Post init timeout callback
  469. /**
  470. * The timer pointer is included to ensure the timer isn't destroyed until
  471. * after it has expired.
  472. *
  473. * @param post_timer Pointer to the timer in question
  474. * @param callback The function to call back
  475. * @param ec The status code
  476. */
  477. void handle_post_init_timeout(timer_ptr, init_handler callback,
  478. lib::error_code const & ec)
  479. {
  480. lib::error_code ret_ec;
  481. if (ec) {
  482. if (ec == transport::error::operation_aborted) {
  483. m_alog->write(log::alevel::devel,
  484. "asio post init timer cancelled");
  485. return;
  486. }
  487. log_err(log::elevel::devel,"asio handle_post_init_timeout",ec);
  488. ret_ec = ec;
  489. } else {
  490. if (socket_con_type::get_ec()) {
  491. ret_ec = socket_con_type::get_ec();
  492. } else {
  493. ret_ec = make_error_code(transport::error::timeout);
  494. }
  495. }
  496. m_alog->write(log::alevel::devel, "Asio transport post-init timed out");
  497. cancel_socket_checked();
  498. callback(ret_ec);
  499. }
  500. /// Post init timeout callback
  501. /**
  502. * The timer pointer is included to ensure the timer isn't destroyed until
  503. * after it has expired.
  504. *
  505. * @param post_timer Pointer to the timer in question
  506. * @param callback The function to call back
  507. * @param ec The status code
  508. */
  509. void handle_post_init(timer_ptr post_timer, init_handler callback,
  510. lib::error_code const & ec)
  511. {
  512. if (ec == transport::error::operation_aborted ||
  513. (post_timer && lib::asio::is_neg(post_timer->expires_from_now())))
  514. {
  515. m_alog->write(log::alevel::devel,"post_init cancelled");
  516. return;
  517. }
  518. if (post_timer) {
  519. post_timer->cancel();
  520. }
  521. if (m_alog->static_test(log::alevel::devel)) {
  522. m_alog->write(log::alevel::devel,"asio connection handle_post_init");
  523. }
  524. if (m_tcp_post_init_handler) {
  525. m_tcp_post_init_handler(m_connection_hdl);
  526. }
  527. callback(ec);
  528. }
  529. void proxy_write(init_handler callback) {
  530. if (m_alog->static_test(log::alevel::devel)) {
  531. m_alog->write(log::alevel::devel,"asio connection proxy_write");
  532. }
  533. if (!m_proxy_data) {
  534. m_elog->write(log::elevel::library,
  535. "assertion failed: !m_proxy_data in asio::connection::proxy_write");
  536. callback(make_error_code(error::general));
  537. return;
  538. }
  539. m_proxy_data->write_buf = m_proxy_data->req.raw();
  540. m_bufs.push_back(lib::asio::buffer(m_proxy_data->write_buf.data(),
  541. m_proxy_data->write_buf.size()));
  542. m_alog->write(log::alevel::devel,m_proxy_data->write_buf);
  543. // Set a timer so we don't wait forever for the proxy to respond
  544. m_proxy_data->timer = this->set_timer(
  545. m_proxy_data->timeout_proxy,
  546. lib::bind(
  547. &type::handle_proxy_timeout,
  548. get_shared(),
  549. callback,
  550. lib::placeholders::_1
  551. )
  552. );
  553. // Send proxy request
  554. if (config::enable_multithreading) {
  555. lib::asio::async_write(
  556. socket_con_type::get_next_layer(),
  557. m_bufs,
  558. m_strand->wrap(lib::bind(
  559. &type::handle_proxy_write, get_shared(),
  560. callback,
  561. lib::placeholders::_1
  562. ))
  563. );
  564. } else {
  565. lib::asio::async_write(
  566. socket_con_type::get_next_layer(),
  567. m_bufs,
  568. lib::bind(
  569. &type::handle_proxy_write, get_shared(),
  570. callback,
  571. lib::placeholders::_1
  572. )
  573. );
  574. }
  575. }
  576. void handle_proxy_timeout(init_handler callback, lib::error_code const & ec)
  577. {
  578. if (ec == transport::error::operation_aborted) {
  579. m_alog->write(log::alevel::devel,
  580. "asio handle_proxy_write timer cancelled");
  581. return;
  582. } else if (ec) {
  583. log_err(log::elevel::devel,"asio handle_proxy_write",ec);
  584. callback(ec);
  585. } else {
  586. m_alog->write(log::alevel::devel,
  587. "asio handle_proxy_write timer expired");
  588. cancel_socket_checked();
  589. callback(make_error_code(transport::error::timeout));
  590. }
  591. }
  592. void handle_proxy_write(init_handler callback,
  593. lib::asio::error_code const & ec)
  594. {
  595. if (m_alog->static_test(log::alevel::devel)) {
  596. m_alog->write(log::alevel::devel,
  597. "asio connection handle_proxy_write");
  598. }
  599. m_bufs.clear();
  600. // Timer expired or the operation was aborted for some reason.
  601. // Whatever aborted it will be issuing the callback so we are safe to
  602. // return
  603. if (ec == lib::asio::error::operation_aborted ||
  604. lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
  605. {
  606. m_elog->write(log::elevel::devel,"write operation aborted");
  607. return;
  608. }
  609. if (ec) {
  610. log_err(log::elevel::info,"asio handle_proxy_write",ec);
  611. m_proxy_data->timer->cancel();
  612. callback(make_error_code(error::pass_through));
  613. return;
  614. }
  615. proxy_read(callback);
  616. }
  617. void proxy_read(init_handler callback) {
  618. if (m_alog->static_test(log::alevel::devel)) {
  619. m_alog->write(log::alevel::devel,"asio connection proxy_read");
  620. }
  621. if (!m_proxy_data) {
  622. m_elog->write(log::elevel::library,
  623. "assertion failed: !m_proxy_data in asio::connection::proxy_read");
  624. m_proxy_data->timer->cancel();
  625. callback(make_error_code(error::general));
  626. return;
  627. }
  628. if (config::enable_multithreading) {
  629. lib::asio::async_read_until(
  630. socket_con_type::get_next_layer(),
  631. m_proxy_data->read_buf,
  632. "\r\n\r\n",
  633. m_strand->wrap(lib::bind(
  634. &type::handle_proxy_read, get_shared(),
  635. callback,
  636. lib::placeholders::_1, lib::placeholders::_2
  637. ))
  638. );
  639. } else {
  640. lib::asio::async_read_until(
  641. socket_con_type::get_next_layer(),
  642. m_proxy_data->read_buf,
  643. "\r\n\r\n",
  644. lib::bind(
  645. &type::handle_proxy_read, get_shared(),
  646. callback,
  647. lib::placeholders::_1, lib::placeholders::_2
  648. )
  649. );
  650. }
  651. }
  652. /// Proxy read callback
  653. /**
  654. * @param init_handler The function to call back
  655. * @param ec The status code
  656. * @param bytes_transferred The number of bytes read
  657. */
  658. void handle_proxy_read(init_handler callback,
  659. lib::asio::error_code const & ec, size_t)
  660. {
  661. if (m_alog->static_test(log::alevel::devel)) {
  662. m_alog->write(log::alevel::devel,
  663. "asio connection handle_proxy_read");
  664. }
  665. // Timer expired or the operation was aborted for some reason.
  666. // Whatever aborted it will be issuing the callback so we are safe to
  667. // return
  668. if (ec == lib::asio::error::operation_aborted ||
  669. lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
  670. {
  671. m_elog->write(log::elevel::devel,"read operation aborted");
  672. return;
  673. }
  674. // At this point there is no need to wait for the timer anymore
  675. m_proxy_data->timer->cancel();
  676. if (ec) {
  677. m_elog->write(log::elevel::info,
  678. "asio handle_proxy_read error: "+ec.message());
  679. callback(make_error_code(error::pass_through));
  680. } else {
  681. if (!m_proxy_data) {
  682. m_elog->write(log::elevel::library,
  683. "assertion failed: !m_proxy_data in asio::connection::handle_proxy_read");
  684. callback(make_error_code(error::general));
  685. return;
  686. }
  687. std::istream input(&m_proxy_data->read_buf);
  688. m_proxy_data->res.consume(input);
  689. if (!m_proxy_data->res.headers_ready()) {
  690. // we read until the headers were done in theory but apparently
  691. // they aren't. Internal endpoint error.
  692. callback(make_error_code(error::general));
  693. return;
  694. }
  695. m_alog->write(log::alevel::devel,m_proxy_data->res.raw());
  696. if (m_proxy_data->res.get_status_code() != http::status_code::ok) {
  697. // got an error response back
  698. // TODO: expose this error in a programmatically accessible way?
  699. // if so, see below for an option on how to do this.
  700. std::stringstream s;
  701. s << "Proxy connection error: "
  702. << m_proxy_data->res.get_status_code()
  703. << " ("
  704. << m_proxy_data->res.get_status_msg()
  705. << ")";
  706. m_elog->write(log::elevel::info,s.str());
  707. callback(make_error_code(error::proxy_failed));
  708. return;
  709. }
  710. // we have successfully established a connection to the proxy, now
  711. // we can continue and the proxy will transparently forward the
  712. // WebSocket connection.
  713. // TODO: decide if we want an on_proxy callback that would allow
  714. // access to the proxy response.
  715. // free the proxy buffers and req/res objects as they aren't needed
  716. // anymore
  717. m_proxy_data.reset();
  718. // Continue with post proxy initialization
  719. post_init(callback);
  720. }
  721. }
  722. /// read at least num_bytes bytes into buf and then call handler.
  723. void async_read_at_least(size_t num_bytes, char *buf, size_t len,
  724. read_handler handler)
  725. {
  726. if (m_alog->static_test(log::alevel::devel)) {
  727. std::stringstream s;
  728. s << "asio async_read_at_least: " << num_bytes;
  729. m_alog->write(log::alevel::devel,s.str());
  730. }
  731. // TODO: safety vs speed ?
  732. // maybe move into an if devel block
  733. /*if (num_bytes > len) {
  734. m_elog->write(log::elevel::devel,
  735. "asio async_read_at_least error::invalid_num_bytes");
  736. handler(make_error_code(transport::error::invalid_num_bytes),
  737. size_t(0));
  738. return;
  739. }*/
  740. if (config::enable_multithreading) {
  741. lib::asio::async_read(
  742. socket_con_type::get_socket(),
  743. lib::asio::buffer(buf,len),
  744. lib::asio::transfer_at_least(num_bytes),
  745. m_strand->wrap(make_custom_alloc_handler(
  746. m_read_handler_allocator,
  747. lib::bind(
  748. &type::handle_async_read, get_shared(),
  749. handler,
  750. lib::placeholders::_1, lib::placeholders::_2
  751. )
  752. ))
  753. );
  754. } else {
  755. lib::asio::async_read(
  756. socket_con_type::get_socket(),
  757. lib::asio::buffer(buf,len),
  758. lib::asio::transfer_at_least(num_bytes),
  759. make_custom_alloc_handler(
  760. m_read_handler_allocator,
  761. lib::bind(
  762. &type::handle_async_read, get_shared(),
  763. handler,
  764. lib::placeholders::_1, lib::placeholders::_2
  765. )
  766. )
  767. );
  768. }
  769. }
  770. void handle_async_read(read_handler handler, lib::asio::error_code const & ec,
  771. size_t bytes_transferred)
  772. {
  773. m_alog->write(log::alevel::devel, "asio con handle_async_read");
  774. // translate asio error codes into more lib::error_codes
  775. lib::error_code tec;
  776. if (ec == lib::asio::error::eof) {
  777. tec = make_error_code(transport::error::eof);
  778. } else if (ec) {
  779. // We don't know much more about the error at this point. As our
  780. // socket/security policy if it knows more:
  781. tec = socket_con_type::translate_ec(ec);
  782. m_tec = ec;
  783. if (tec == transport::error::tls_error ||
  784. tec == transport::error::pass_through)
  785. {
  786. // These are aggregate/catch all errors. Log some human readable
  787. // information to the info channel to give library users some
  788. // more details about why the upstream method may have failed.
  789. log_err(log::elevel::info,"asio async_read_at_least",ec);
  790. }
  791. }
  792. if (handler) {
  793. handler(tec,bytes_transferred);
  794. } else {
  795. // This can happen in cases where the connection is terminated while
  796. // the transport is waiting on a read.
  797. m_alog->write(log::alevel::devel,
  798. "handle_async_read called with null read handler");
  799. }
  800. }
  801. /// Initiate a potentially asyncronous write of the given buffer
  802. void async_write(const char* buf, size_t len, write_handler handler) {
  803. m_bufs.push_back(lib::asio::buffer(buf,len));
  804. if (config::enable_multithreading) {
  805. lib::asio::async_write(
  806. socket_con_type::get_socket(),
  807. m_bufs,
  808. m_strand->wrap(make_custom_alloc_handler(
  809. m_write_handler_allocator,
  810. lib::bind(
  811. &type::handle_async_write, get_shared(),
  812. handler,
  813. lib::placeholders::_1, lib::placeholders::_2
  814. )
  815. ))
  816. );
  817. } else {
  818. lib::asio::async_write(
  819. socket_con_type::get_socket(),
  820. m_bufs,
  821. make_custom_alloc_handler(
  822. m_write_handler_allocator,
  823. lib::bind(
  824. &type::handle_async_write, get_shared(),
  825. handler,
  826. lib::placeholders::_1, lib::placeholders::_2
  827. )
  828. )
  829. );
  830. }
  831. }
  832. /// Initiate a potentially asyncronous write of the given buffers
  833. void async_write(std::vector<buffer> const & bufs, write_handler handler) {
  834. std::vector<buffer>::const_iterator it;
  835. for (it = bufs.begin(); it != bufs.end(); ++it) {
  836. m_bufs.push_back(lib::asio::buffer((*it).buf,(*it).len));
  837. }
  838. if (config::enable_multithreading) {
  839. lib::asio::async_write(
  840. socket_con_type::get_socket(),
  841. m_bufs,
  842. m_strand->wrap(make_custom_alloc_handler(
  843. m_write_handler_allocator,
  844. lib::bind(
  845. &type::handle_async_write, get_shared(),
  846. handler,
  847. lib::placeholders::_1, lib::placeholders::_2
  848. )
  849. ))
  850. );
  851. } else {
  852. lib::asio::async_write(
  853. socket_con_type::get_socket(),
  854. m_bufs,
  855. make_custom_alloc_handler(
  856. m_write_handler_allocator,
  857. lib::bind(
  858. &type::handle_async_write, get_shared(),
  859. handler,
  860. lib::placeholders::_1, lib::placeholders::_2
  861. )
  862. )
  863. );
  864. }
  865. }
  866. /// Async write callback
  867. /**
  868. * @param ec The status code
  869. * @param bytes_transferred The number of bytes read
  870. */
  871. void handle_async_write(write_handler handler, lib::asio::error_code const & ec, size_t) {
  872. m_bufs.clear();
  873. lib::error_code tec;
  874. if (ec) {
  875. log_err(log::elevel::info,"asio async_write",ec);
  876. tec = make_error_code(transport::error::pass_through);
  877. }
  878. if (handler) {
  879. handler(tec);
  880. } else {
  881. // This can happen in cases where the connection is terminated while
  882. // the transport is waiting on a read.
  883. m_alog->write(log::alevel::devel,
  884. "handle_async_write called with null write handler");
  885. }
  886. }
  887. /// Set Connection Handle
  888. /**
  889. * See common/connection_hdl.hpp for information
  890. *
  891. * @param hdl A connection_hdl that the transport will use to refer
  892. * to itself
  893. */
  894. void set_handle(connection_hdl hdl) {
  895. m_connection_hdl = hdl;
  896. socket_con_type::set_handle(hdl);
  897. }
  898. /// Trigger the on_interrupt handler
  899. /**
  900. * This needs to be thread safe
  901. */
  902. lib::error_code interrupt(interrupt_handler handler) {
  903. if (config::enable_multithreading) {
  904. m_io_service->post(m_strand->wrap(handler));
  905. } else {
  906. m_io_service->post(handler);
  907. }
  908. return lib::error_code();
  909. }
  910. lib::error_code dispatch(dispatch_handler handler) {
  911. if (config::enable_multithreading) {
  912. m_io_service->post(m_strand->wrap(handler));
  913. } else {
  914. m_io_service->post(handler);
  915. }
  916. return lib::error_code();
  917. }
  918. /*void handle_interrupt(interrupt_handler handler) {
  919. handler();
  920. }*/
  921. /// close and clean up the underlying socket
  922. void async_shutdown(shutdown_handler callback) {
  923. if (m_alog->static_test(log::alevel::devel)) {
  924. m_alog->write(log::alevel::devel,"asio connection async_shutdown");
  925. }
  926. timer_ptr shutdown_timer;
  927. shutdown_timer = set_timer(
  928. config::timeout_socket_shutdown,
  929. lib::bind(
  930. &type::handle_async_shutdown_timeout,
  931. get_shared(),
  932. shutdown_timer,
  933. callback,
  934. lib::placeholders::_1
  935. )
  936. );
  937. socket_con_type::async_shutdown(
  938. lib::bind(
  939. &type::handle_async_shutdown,
  940. get_shared(),
  941. shutdown_timer,
  942. callback,
  943. lib::placeholders::_1
  944. )
  945. );
  946. }
  947. /// Async shutdown timeout handler
  948. /**
  949. * @param shutdown_timer A pointer to the timer to keep it in scope
  950. * @param callback The function to call back
  951. * @param ec The status code
  952. */
  953. void handle_async_shutdown_timeout(timer_ptr, init_handler callback,
  954. lib::error_code const & ec)
  955. {
  956. lib::error_code ret_ec;
  957. if (ec) {
  958. if (ec == transport::error::operation_aborted) {
  959. m_alog->write(log::alevel::devel,
  960. "asio socket shutdown timer cancelled");
  961. return;
  962. }
  963. log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec);
  964. ret_ec = ec;
  965. } else {
  966. ret_ec = make_error_code(transport::error::timeout);
  967. }
  968. m_alog->write(log::alevel::devel,
  969. "Asio transport socket shutdown timed out");
  970. cancel_socket_checked();
  971. callback(ret_ec);
  972. }
  973. void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler
  974. callback, lib::asio::error_code const & ec)
  975. {
  976. if (ec == lib::asio::error::operation_aborted ||
  977. lib::asio::is_neg(shutdown_timer->expires_from_now()))
  978. {
  979. m_alog->write(log::alevel::devel,"async_shutdown cancelled");
  980. return;
  981. }
  982. shutdown_timer->cancel();
  983. lib::error_code tec;
  984. if (ec) {
  985. if (ec == lib::asio::error::not_connected) {
  986. // The socket was already closed when we tried to close it. This
  987. // happens periodically (usually if a read or write fails
  988. // earlier and if it is a real error will be caught at another
  989. // level of the stack.
  990. } else {
  991. // We don't know anything more about this error, give our
  992. // socket/security policy a crack at it.
  993. tec = socket_con_type::translate_ec(ec);
  994. m_tec = ec;
  995. // all other errors are effectively pass through errors of
  996. // some sort so print some detail on the info channel for
  997. // library users to look up if needed.
  998. log_err(log::elevel::info,"asio async_shutdown",ec);
  999. }
  1000. } else {
  1001. if (m_alog->static_test(log::alevel::devel)) {
  1002. m_alog->write(log::alevel::devel,
  1003. "asio con handle_async_shutdown");
  1004. }
  1005. }
  1006. callback(tec);
  1007. }
  1008. /// Cancel the underlying socket and log any errors
  1009. void cancel_socket_checked() {
  1010. lib::asio::error_code cec = socket_con_type::cancel_socket();
  1011. if (cec) {
  1012. if (cec == lib::asio::error::operation_not_supported) {
  1013. // cancel not supported on this OS, ignore and log at dev level
  1014. m_alog->write(log::alevel::devel, "socket cancel not supported");
  1015. } else {
  1016. log_err(log::elevel::warn, "socket cancel failed", cec);
  1017. }
  1018. }
  1019. }
  1020. private:
  1021. /// Convenience method for logging the code and message for an error_code
  1022. template <typename error_type>
  1023. void log_err(log::level l, const char * msg, const error_type & ec) {
  1024. std::stringstream s;
  1025. s << msg << " error: " << ec << " (" << ec.message() << ")";
  1026. m_elog->write(l,s.str());
  1027. }
  1028. // static settings
  1029. const bool m_is_server;
  1030. lib::shared_ptr<alog_type> m_alog;
  1031. lib::shared_ptr<elog_type> m_elog;
  1032. struct proxy_data {
  1033. proxy_data() : timeout_proxy(config::timeout_proxy) {}
  1034. request_type req;
  1035. response_type res;
  1036. std::string write_buf;
  1037. lib::asio::streambuf read_buf;
  1038. long timeout_proxy;
  1039. timer_ptr timer;
  1040. };
  1041. std::string m_proxy;
  1042. lib::shared_ptr<proxy_data> m_proxy_data;
  1043. // transport resources
  1044. io_service_ptr m_io_service;
  1045. strand_ptr m_strand;
  1046. connection_hdl m_connection_hdl;
  1047. std::vector<lib::asio::const_buffer> m_bufs;
  1048. /// Detailed internal error code
  1049. lib::asio::error_code m_tec;
  1050. // Handlers
  1051. tcp_init_handler m_tcp_pre_init_handler;
  1052. tcp_init_handler m_tcp_post_init_handler;
  1053. handler_allocator m_read_handler_allocator;
  1054. handler_allocator m_write_handler_allocator;
  1055. };
  1056. } // namespace asio
  1057. } // namespace transport
  1058. } // namespace websocketpp
  1059. #endif // WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP