connection.hpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179
  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, alog_type & alog, 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 = lib::make_shared<lib::asio::steady_timer>(
  280. lib::ref(*m_io_service),
  281. lib::asio::milliseconds(duration)
  282. );
  283. if (config::enable_multithreading) {
  284. new_timer->async_wait(m_strand->wrap(lib::bind(
  285. &type::handle_timer, get_shared(),
  286. new_timer,
  287. callback,
  288. lib::placeholders::_1
  289. )));
  290. } else {
  291. new_timer->async_wait(lib::bind(
  292. &type::handle_timer, get_shared(),
  293. new_timer,
  294. callback,
  295. lib::placeholders::_1
  296. ));
  297. }
  298. return new_timer;
  299. }
  300. /// Timer callback
  301. /**
  302. * The timer pointer is included to ensure the timer isn't destroyed until
  303. * after it has expired.
  304. *
  305. * TODO: candidate for protected status
  306. *
  307. * @param post_timer Pointer to the timer in question
  308. * @param callback The function to call back
  309. * @param ec The status code
  310. */
  311. void handle_timer(timer_ptr, timer_handler callback,
  312. lib::asio::error_code const & ec)
  313. {
  314. if (ec) {
  315. if (ec == lib::asio::error::operation_aborted) {
  316. callback(make_error_code(transport::error::operation_aborted));
  317. } else {
  318. log_err(log::elevel::info,"asio handle_timer",ec);
  319. callback(make_error_code(error::pass_through));
  320. }
  321. } else {
  322. callback(lib::error_code());
  323. }
  324. }
  325. /// Get a pointer to this connection's strand
  326. strand_ptr get_strand() {
  327. return m_strand;
  328. }
  329. /// Initialize transport for reading
  330. /**
  331. * init_asio is called once immediately after construction to initialize
  332. * Asio components to the io_service
  333. *
  334. * The transport initialization sequence consists of the following steps:
  335. * - Pre-init: the underlying socket is initialized to the point where
  336. * bytes may be written. No bytes are actually written in this stage
  337. * - Proxy negotiation: if a proxy is set, a request is made to it to start
  338. * a tunnel to the final destination. This stage ends when the proxy is
  339. * ready to forward the
  340. * next byte to the remote endpoint.
  341. * - Post-init: Perform any i/o with the remote endpoint, such as setting up
  342. * tunnels for encryption. This stage ends when the connection is ready to
  343. * read or write the WebSocket handshakes. At this point the original
  344. * callback function is called.
  345. */
  346. protected:
  347. void init(init_handler callback) {
  348. if (m_alog.static_test(log::alevel::devel)) {
  349. m_alog.write(log::alevel::devel,"asio connection init");
  350. }
  351. // TODO: pre-init timeout. Right now no implemented socket policies
  352. // actually have an asyncronous pre-init
  353. m_init_handler = callback;
  354. socket_con_type::pre_init(
  355. lib::bind(
  356. &type::handle_pre_init,
  357. get_shared(),
  358. lib::placeholders::_1
  359. )
  360. );
  361. }
  362. /// initialize the proxy buffers and http parsers
  363. /**
  364. *
  365. * @param authority The address of the server we want the proxy to tunnel to
  366. * in the format of a URI authority (host:port)
  367. *
  368. * @return Status code indicating what errors occurred, if any
  369. */
  370. lib::error_code proxy_init(std::string const & authority) {
  371. if (!m_proxy_data) {
  372. return websocketpp::error::make_error_code(
  373. websocketpp::error::invalid_state);
  374. }
  375. m_proxy_data->req.set_version("HTTP/1.1");
  376. m_proxy_data->req.set_method("CONNECT");
  377. m_proxy_data->req.set_uri(authority);
  378. m_proxy_data->req.replace_header("Host",authority);
  379. return lib::error_code();
  380. }
  381. /// Finish constructing the transport
  382. /**
  383. * init_asio is called once immediately after construction to initialize
  384. * Asio components to the io_service.
  385. *
  386. * @param io_service A pointer to the io_service to register with this
  387. * connection
  388. *
  389. * @return Status code for the success or failure of the initialization
  390. */
  391. lib::error_code init_asio (io_service_ptr io_service) {
  392. m_io_service = io_service;
  393. if (config::enable_multithreading) {
  394. m_strand = lib::make_shared<lib::asio::strand>(
  395. lib::ref(*io_service));
  396. m_async_read_handler = m_strand->wrap(lib::bind(
  397. &type::handle_async_read, get_shared(),lib::placeholders::_1,
  398. lib::placeholders::_2));
  399. m_async_write_handler = m_strand->wrap(lib::bind(
  400. &type::handle_async_write, get_shared(),lib::placeholders::_1,
  401. lib::placeholders::_2));
  402. } else {
  403. m_async_read_handler = lib::bind(&type::handle_async_read,
  404. get_shared(), lib::placeholders::_1, lib::placeholders::_2);
  405. m_async_write_handler = lib::bind(&type::handle_async_write,
  406. get_shared(), lib::placeholders::_1, lib::placeholders::_2);
  407. }
  408. lib::error_code ec = socket_con_type::init_asio(io_service, m_strand,
  409. m_is_server);
  410. if (ec) {
  411. // reset the handlers to break the circular reference:
  412. // this->handler->this
  413. lib::clear_function(m_async_read_handler);
  414. lib::clear_function(m_async_write_handler);
  415. }
  416. return ec;
  417. }
  418. void handle_pre_init(lib::error_code const & ec) {
  419. if (m_alog.static_test(log::alevel::devel)) {
  420. m_alog.write(log::alevel::devel,"asio connection handle pre_init");
  421. }
  422. if (m_tcp_pre_init_handler) {
  423. m_tcp_pre_init_handler(m_connection_hdl);
  424. }
  425. if (ec) {
  426. m_init_handler(ec);
  427. }
  428. // If we have a proxy set issue a proxy connect, otherwise skip to
  429. // post_init
  430. if (!m_proxy.empty()) {
  431. proxy_write();
  432. } else {
  433. post_init();
  434. }
  435. }
  436. void post_init() {
  437. if (m_alog.static_test(log::alevel::devel)) {
  438. m_alog.write(log::alevel::devel,"asio connection post_init");
  439. }
  440. timer_ptr post_timer;
  441. if (config::timeout_socket_post_init > 0) {
  442. post_timer = set_timer(
  443. config::timeout_socket_post_init,
  444. lib::bind(
  445. &type::handle_post_init_timeout,
  446. get_shared(),
  447. post_timer,
  448. m_init_handler,
  449. lib::placeholders::_1
  450. )
  451. );
  452. }
  453. socket_con_type::post_init(
  454. lib::bind(
  455. &type::handle_post_init,
  456. get_shared(),
  457. post_timer,
  458. m_init_handler,
  459. lib::placeholders::_1
  460. )
  461. );
  462. }
  463. /// Post init timeout callback
  464. /**
  465. * The timer pointer is included to ensure the timer isn't destroyed until
  466. * after it has expired.
  467. *
  468. * @param post_timer Pointer to the timer in question
  469. * @param callback The function to call back
  470. * @param ec The status code
  471. */
  472. void handle_post_init_timeout(timer_ptr, init_handler callback,
  473. lib::error_code const & ec)
  474. {
  475. lib::error_code ret_ec;
  476. if (ec) {
  477. if (ec == transport::error::operation_aborted) {
  478. m_alog.write(log::alevel::devel,
  479. "asio post init timer cancelled");
  480. return;
  481. }
  482. log_err(log::elevel::devel,"asio handle_post_init_timeout",ec);
  483. ret_ec = ec;
  484. } else {
  485. if (socket_con_type::get_ec()) {
  486. ret_ec = socket_con_type::get_ec();
  487. } else {
  488. ret_ec = make_error_code(transport::error::timeout);
  489. }
  490. }
  491. m_alog.write(log::alevel::devel,"Asio transport post-init timed out");
  492. socket_con_type::cancel_socket();
  493. callback(ret_ec);
  494. }
  495. /// Post init timeout callback
  496. /**
  497. * The timer pointer is included to ensure the timer isn't destroyed until
  498. * after it has expired.
  499. *
  500. * @param post_timer Pointer to the timer in question
  501. * @param callback The function to call back
  502. * @param ec The status code
  503. */
  504. void handle_post_init(timer_ptr post_timer, init_handler callback,
  505. lib::error_code const & ec)
  506. {
  507. if (ec == transport::error::operation_aborted ||
  508. (post_timer && lib::asio::is_neg(post_timer->expires_from_now())))
  509. {
  510. m_alog.write(log::alevel::devel,"post_init cancelled");
  511. return;
  512. }
  513. if (post_timer) {
  514. post_timer->cancel();
  515. }
  516. if (m_alog.static_test(log::alevel::devel)) {
  517. m_alog.write(log::alevel::devel,"asio connection handle_post_init");
  518. }
  519. if (m_tcp_post_init_handler) {
  520. m_tcp_post_init_handler(m_connection_hdl);
  521. }
  522. callback(ec);
  523. }
  524. void proxy_write() {
  525. if (m_alog.static_test(log::alevel::devel)) {
  526. m_alog.write(log::alevel::devel,"asio connection proxy_write");
  527. }
  528. if (!m_proxy_data) {
  529. m_elog.write(log::elevel::library,
  530. "assertion failed: !m_proxy_data in asio::connection::proxy_write");
  531. m_init_handler(make_error_code(error::general));
  532. return;
  533. }
  534. m_proxy_data->write_buf = m_proxy_data->req.raw();
  535. m_bufs.push_back(lib::asio::buffer(m_proxy_data->write_buf.data(),
  536. m_proxy_data->write_buf.size()));
  537. m_alog.write(log::alevel::devel,m_proxy_data->write_buf);
  538. // Set a timer so we don't wait forever for the proxy to respond
  539. m_proxy_data->timer = this->set_timer(
  540. m_proxy_data->timeout_proxy,
  541. lib::bind(
  542. &type::handle_proxy_timeout,
  543. get_shared(),
  544. m_init_handler,
  545. lib::placeholders::_1
  546. )
  547. );
  548. // Send proxy request
  549. if (config::enable_multithreading) {
  550. lib::asio::async_write(
  551. socket_con_type::get_next_layer(),
  552. m_bufs,
  553. m_strand->wrap(lib::bind(
  554. &type::handle_proxy_write, get_shared(),
  555. m_init_handler,
  556. lib::placeholders::_1
  557. ))
  558. );
  559. } else {
  560. lib::asio::async_write(
  561. socket_con_type::get_next_layer(),
  562. m_bufs,
  563. lib::bind(
  564. &type::handle_proxy_write, get_shared(),
  565. m_init_handler,
  566. lib::placeholders::_1
  567. )
  568. );
  569. }
  570. }
  571. void handle_proxy_timeout(init_handler callback, lib::error_code const & ec)
  572. {
  573. if (ec == transport::error::operation_aborted) {
  574. m_alog.write(log::alevel::devel,
  575. "asio handle_proxy_write timer cancelled");
  576. return;
  577. } else if (ec) {
  578. log_err(log::elevel::devel,"asio handle_proxy_write",ec);
  579. callback(ec);
  580. } else {
  581. m_alog.write(log::alevel::devel,
  582. "asio handle_proxy_write timer expired");
  583. socket_con_type::cancel_socket();
  584. callback(make_error_code(transport::error::timeout));
  585. }
  586. }
  587. void handle_proxy_write(init_handler callback,
  588. lib::asio::error_code const & ec)
  589. {
  590. if (m_alog.static_test(log::alevel::devel)) {
  591. m_alog.write(log::alevel::devel,
  592. "asio connection handle_proxy_write");
  593. }
  594. m_bufs.clear();
  595. // Timer expired or the operation was aborted for some reason.
  596. // Whatever aborted it will be issuing the callback so we are safe to
  597. // return
  598. if (ec == lib::asio::error::operation_aborted ||
  599. lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
  600. {
  601. m_elog.write(log::elevel::devel,"write operation aborted");
  602. return;
  603. }
  604. if (ec) {
  605. log_err(log::elevel::info,"asio handle_proxy_write",ec);
  606. m_proxy_data->timer->cancel();
  607. callback(make_error_code(error::pass_through));
  608. return;
  609. }
  610. proxy_read(callback);
  611. }
  612. void proxy_read(init_handler callback) {
  613. if (m_alog.static_test(log::alevel::devel)) {
  614. m_alog.write(log::alevel::devel,"asio connection proxy_read");
  615. }
  616. if (!m_proxy_data) {
  617. m_elog.write(log::elevel::library,
  618. "assertion failed: !m_proxy_data in asio::connection::proxy_read");
  619. m_proxy_data->timer->cancel();
  620. callback(make_error_code(error::general));
  621. return;
  622. }
  623. if (config::enable_multithreading) {
  624. lib::asio::async_read_until(
  625. socket_con_type::get_next_layer(),
  626. m_proxy_data->read_buf,
  627. "\r\n\r\n",
  628. m_strand->wrap(lib::bind(
  629. &type::handle_proxy_read, get_shared(),
  630. callback,
  631. lib::placeholders::_1, lib::placeholders::_2
  632. ))
  633. );
  634. } else {
  635. lib::asio::async_read_until(
  636. socket_con_type::get_next_layer(),
  637. m_proxy_data->read_buf,
  638. "\r\n\r\n",
  639. lib::bind(
  640. &type::handle_proxy_read, get_shared(),
  641. callback,
  642. lib::placeholders::_1, lib::placeholders::_2
  643. )
  644. );
  645. }
  646. }
  647. /// Proxy read callback
  648. /**
  649. * @param init_handler The function to call back
  650. * @param ec The status code
  651. * @param bytes_transferred The number of bytes read
  652. */
  653. void handle_proxy_read(init_handler callback,
  654. lib::asio::error_code const & ec, size_t)
  655. {
  656. if (m_alog.static_test(log::alevel::devel)) {
  657. m_alog.write(log::alevel::devel,
  658. "asio connection handle_proxy_read");
  659. }
  660. // Timer expired or the operation was aborted for some reason.
  661. // Whatever aborted it will be issuing the callback so we are safe to
  662. // return
  663. if (ec == lib::asio::error::operation_aborted ||
  664. lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
  665. {
  666. m_elog.write(log::elevel::devel,"read operation aborted");
  667. return;
  668. }
  669. // At this point there is no need to wait for the timer anymore
  670. m_proxy_data->timer->cancel();
  671. if (ec) {
  672. m_elog.write(log::elevel::info,
  673. "asio handle_proxy_read error: "+ec.message());
  674. callback(make_error_code(error::pass_through));
  675. } else {
  676. if (!m_proxy_data) {
  677. m_elog.write(log::elevel::library,
  678. "assertion failed: !m_proxy_data in asio::connection::handle_proxy_read");
  679. callback(make_error_code(error::general));
  680. return;
  681. }
  682. std::istream input(&m_proxy_data->read_buf);
  683. m_proxy_data->res.consume(input);
  684. if (!m_proxy_data->res.headers_ready()) {
  685. // we read until the headers were done in theory but apparently
  686. // they aren't. Internal endpoint error.
  687. callback(make_error_code(error::general));
  688. return;
  689. }
  690. m_alog.write(log::alevel::devel,m_proxy_data->res.raw());
  691. if (m_proxy_data->res.get_status_code() != http::status_code::ok) {
  692. // got an error response back
  693. // TODO: expose this error in a programmatically accessible way?
  694. // if so, see below for an option on how to do this.
  695. std::stringstream s;
  696. s << "Proxy connection error: "
  697. << m_proxy_data->res.get_status_code()
  698. << " ("
  699. << m_proxy_data->res.get_status_msg()
  700. << ")";
  701. m_elog.write(log::elevel::info,s.str());
  702. callback(make_error_code(error::proxy_failed));
  703. return;
  704. }
  705. // we have successfully established a connection to the proxy, now
  706. // we can continue and the proxy will transparently forward the
  707. // WebSocket connection.
  708. // TODO: decide if we want an on_proxy callback that would allow
  709. // access to the proxy response.
  710. // free the proxy buffers and req/res objects as they aren't needed
  711. // anymore
  712. m_proxy_data.reset();
  713. // Continue with post proxy initialization
  714. post_init();
  715. }
  716. }
  717. /// read at least num_bytes bytes into buf and then call handler.
  718. /**
  719. *
  720. *
  721. */
  722. void async_read_at_least(size_t num_bytes, char *buf, size_t len,
  723. read_handler handler)
  724. {
  725. if (m_alog.static_test(log::alevel::devel)) {
  726. std::stringstream s;
  727. s << "asio async_read_at_least: " << num_bytes;
  728. m_alog.write(log::alevel::devel,s.str());
  729. }
  730. if (!m_async_read_handler) {
  731. m_alog.write(log::alevel::devel,
  732. "async_read_at_least called after async_shutdown");
  733. handler(make_error_code(transport::error::action_after_shutdown),0);
  734. return;
  735. }
  736. // TODO: safety vs speed ?
  737. // maybe move into an if devel block
  738. /*if (num_bytes > len) {
  739. m_elog.write(log::elevel::devel,
  740. "asio async_read_at_least error::invalid_num_bytes");
  741. handler(make_error_code(transport::error::invalid_num_bytes),
  742. size_t(0));
  743. return;
  744. }*/
  745. m_read_handler = handler;
  746. if (!m_read_handler) {
  747. m_alog.write(log::alevel::devel,
  748. "asio con async_read_at_least called with bad handler");
  749. }
  750. lib::asio::async_read(
  751. socket_con_type::get_socket(),
  752. lib::asio::buffer(buf,len),
  753. lib::asio::transfer_at_least(num_bytes),
  754. make_custom_alloc_handler(
  755. m_read_handler_allocator,
  756. m_async_read_handler
  757. )
  758. );
  759. }
  760. void handle_async_read(lib::asio::error_code const & ec,
  761. size_t bytes_transferred)
  762. {
  763. m_alog.write(log::alevel::devel, "asio con handle_async_read");
  764. // translate asio error codes into more lib::error_codes
  765. lib::error_code tec;
  766. if (ec == lib::asio::error::eof) {
  767. tec = make_error_code(transport::error::eof);
  768. } else if (ec) {
  769. // We don't know much more about the error at this point. As our
  770. // socket/security policy if it knows more:
  771. tec = socket_con_type::translate_ec(ec);
  772. if (tec == transport::error::tls_error ||
  773. tec == transport::error::pass_through)
  774. {
  775. // These are aggregate/catch all errors. Log some human readable
  776. // information to the info channel to give library users some
  777. // more details about why the upstream method may have failed.
  778. log_err(log::elevel::info,"asio async_read_at_least",ec);
  779. }
  780. }
  781. if (m_read_handler) {
  782. m_read_handler(tec,bytes_transferred);
  783. // TODO: why does this line break things?
  784. //m_read_handler = _WEBSOCKETPP_NULL_FUNCTION_;
  785. } else {
  786. // This can happen in cases where the connection is terminated while
  787. // the transport is waiting on a read.
  788. m_alog.write(log::alevel::devel,
  789. "handle_async_read called with null read handler");
  790. }
  791. }
  792. void async_write(const char* buf, size_t len, write_handler handler) {
  793. if (!m_async_write_handler) {
  794. m_alog.write(log::alevel::devel,
  795. "async_write (single) called after async_shutdown");
  796. handler(make_error_code(transport::error::action_after_shutdown));
  797. return;
  798. }
  799. m_bufs.push_back(lib::asio::buffer(buf,len));
  800. m_write_handler = handler;
  801. lib::asio::async_write(
  802. socket_con_type::get_socket(),
  803. m_bufs,
  804. make_custom_alloc_handler(
  805. m_write_handler_allocator,
  806. m_async_write_handler
  807. )
  808. );
  809. }
  810. void async_write(std::vector<buffer> const & bufs, write_handler handler) {
  811. if (!m_async_write_handler) {
  812. m_alog.write(log::alevel::devel,
  813. "async_write (vector) called after async_shutdown");
  814. handler(make_error_code(transport::error::action_after_shutdown));
  815. return;
  816. }
  817. std::vector<buffer>::const_iterator it;
  818. for (it = bufs.begin(); it != bufs.end(); ++it) {
  819. m_bufs.push_back(lib::asio::buffer((*it).buf,(*it).len));
  820. }
  821. m_write_handler = handler;
  822. lib::asio::async_write(
  823. socket_con_type::get_socket(),
  824. m_bufs,
  825. make_custom_alloc_handler(
  826. m_write_handler_allocator,
  827. m_async_write_handler
  828. )
  829. );
  830. }
  831. /// Async write callback
  832. /**
  833. * @param ec The status code
  834. * @param bytes_transferred The number of bytes read
  835. */
  836. void handle_async_write(lib::asio::error_code const & ec, size_t) {
  837. m_bufs.clear();
  838. lib::error_code tec;
  839. if (ec) {
  840. log_err(log::elevel::info,"asio async_write",ec);
  841. tec = make_error_code(transport::error::pass_through);
  842. }
  843. if (m_write_handler) {
  844. m_write_handler(tec);
  845. // TODO: why does this line break things?
  846. //m_write_handler = _WEBSOCKETPP_NULL_FUNCTION_;
  847. } else {
  848. // This can happen in cases where the connection is terminated while
  849. // the transport is waiting on a read.
  850. m_alog.write(log::alevel::devel,
  851. "handle_async_write called with null write handler");
  852. }
  853. }
  854. /// Set Connection Handle
  855. /**
  856. * See common/connection_hdl.hpp for information
  857. *
  858. * @param hdl A connection_hdl that the transport will use to refer
  859. * to itself
  860. */
  861. void set_handle(connection_hdl hdl) {
  862. m_connection_hdl = hdl;
  863. socket_con_type::set_handle(hdl);
  864. }
  865. /// Trigger the on_interrupt handler
  866. /**
  867. * This needs to be thread safe
  868. */
  869. lib::error_code interrupt(interrupt_handler handler) {
  870. if (config::enable_multithreading) {
  871. m_io_service->post(m_strand->wrap(handler));
  872. } else {
  873. m_io_service->post(handler);
  874. }
  875. return lib::error_code();
  876. }
  877. lib::error_code dispatch(dispatch_handler handler) {
  878. if (config::enable_multithreading) {
  879. m_io_service->post(m_strand->wrap(handler));
  880. } else {
  881. m_io_service->post(handler);
  882. }
  883. return lib::error_code();
  884. }
  885. /*void handle_interrupt(interrupt_handler handler) {
  886. handler();
  887. }*/
  888. /// close and clean up the underlying socket
  889. void async_shutdown(shutdown_handler callback) {
  890. if (m_alog.static_test(log::alevel::devel)) {
  891. m_alog.write(log::alevel::devel,"asio connection async_shutdown");
  892. }
  893. // Reset cached handlers now that we won't be reading or writing anymore
  894. // These cached handlers store shared pointers to this connection and
  895. // will leak the connection if not destroyed.
  896. lib::clear_function(m_async_read_handler);
  897. lib::clear_function(m_async_write_handler);
  898. lib::clear_function(m_init_handler);
  899. lib::clear_function(m_read_handler);
  900. lib::clear_function(m_write_handler);
  901. timer_ptr shutdown_timer;
  902. shutdown_timer = set_timer(
  903. config::timeout_socket_shutdown,
  904. lib::bind(
  905. &type::handle_async_shutdown_timeout,
  906. get_shared(),
  907. shutdown_timer,
  908. callback,
  909. lib::placeholders::_1
  910. )
  911. );
  912. socket_con_type::async_shutdown(
  913. lib::bind(
  914. &type::handle_async_shutdown,
  915. get_shared(),
  916. shutdown_timer,
  917. callback,
  918. lib::placeholders::_1
  919. )
  920. );
  921. }
  922. /// Async shutdown timeout handler
  923. /**
  924. * @param shutdown_timer A pointer to the timer to keep it in scope
  925. * @param callback The function to call back
  926. * @param ec The status code
  927. */
  928. void handle_async_shutdown_timeout(timer_ptr, init_handler callback,
  929. lib::error_code const & ec)
  930. {
  931. lib::error_code ret_ec;
  932. if (ec) {
  933. if (ec == transport::error::operation_aborted) {
  934. m_alog.write(log::alevel::devel,
  935. "asio socket shutdown timer cancelled");
  936. return;
  937. }
  938. log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec);
  939. ret_ec = ec;
  940. } else {
  941. ret_ec = make_error_code(transport::error::timeout);
  942. }
  943. m_alog.write(log::alevel::devel,
  944. "Asio transport socket shutdown timed out");
  945. socket_con_type::cancel_socket();
  946. callback(ret_ec);
  947. }
  948. void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler
  949. callback, lib::asio::error_code const & ec)
  950. {
  951. if (ec == lib::asio::error::operation_aborted ||
  952. lib::asio::is_neg(shutdown_timer->expires_from_now()))
  953. {
  954. m_alog.write(log::alevel::devel,"async_shutdown cancelled");
  955. return;
  956. }
  957. shutdown_timer->cancel();
  958. lib::error_code tec;
  959. if (ec) {
  960. if (ec == lib::asio::error::not_connected) {
  961. // The socket was already closed when we tried to close it. This
  962. // happens periodically (usually if a read or write fails
  963. // earlier and if it is a real error will be caught at another
  964. // level of the stack.
  965. } else {
  966. // We don't know anything more about this error, give our
  967. // socket/security policy a crack at it.
  968. tec = socket_con_type::translate_ec(ec);
  969. if (tec == transport::error::tls_short_read) {
  970. // TLS short read at this point is somewhat expected if both
  971. // sides try and end the connection at the same time or if
  972. // SSLv2 is being used. In general there is nothing that can
  973. // be done here other than a low level development log.
  974. } else {
  975. // all other errors are effectively pass through errors of
  976. // some sort so print some detail on the info channel for
  977. // library users to look up if needed.
  978. log_err(log::elevel::info,"asio async_shutdown",ec);
  979. }
  980. }
  981. } else {
  982. if (m_alog.static_test(log::alevel::devel)) {
  983. m_alog.write(log::alevel::devel,
  984. "asio con handle_async_shutdown");
  985. }
  986. }
  987. callback(tec);
  988. }
  989. private:
  990. /// Convenience method for logging the code and message for an error_code
  991. template <typename error_type>
  992. void log_err(log::level l, const char * msg, const error_type & ec) {
  993. std::stringstream s;
  994. s << msg << " error: " << ec << " (" << ec.message() << ")";
  995. m_elog.write(l,s.str());
  996. }
  997. // static settings
  998. const bool m_is_server;
  999. alog_type& m_alog;
  1000. elog_type& m_elog;
  1001. struct proxy_data {
  1002. proxy_data() : timeout_proxy(config::timeout_proxy) {}
  1003. request_type req;
  1004. response_type res;
  1005. std::string write_buf;
  1006. lib::asio::streambuf read_buf;
  1007. long timeout_proxy;
  1008. timer_ptr timer;
  1009. };
  1010. std::string m_proxy;
  1011. lib::shared_ptr<proxy_data> m_proxy_data;
  1012. // transport resources
  1013. io_service_ptr m_io_service;
  1014. strand_ptr m_strand;
  1015. connection_hdl m_connection_hdl;
  1016. std::vector<lib::asio::const_buffer> m_bufs;
  1017. // Handlers
  1018. tcp_init_handler m_tcp_pre_init_handler;
  1019. tcp_init_handler m_tcp_post_init_handler;
  1020. handler_allocator m_read_handler_allocator;
  1021. handler_allocator m_write_handler_allocator;
  1022. read_handler m_read_handler;
  1023. write_handler m_write_handler;
  1024. init_handler m_init_handler;
  1025. async_read_handler m_async_read_handler;
  1026. async_write_handler m_async_write_handler;
  1027. };
  1028. } // namespace asio
  1029. } // namespace transport
  1030. } // namespace websocketpp
  1031. #endif // WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP