Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

647 lines
18KB

  1. //
  2. // basic_socket_streambuf.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef ASIO_BASIC_SOCKET_STREAMBUF_HPP
  11. #define ASIO_BASIC_SOCKET_STREAMBUF_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include "asio/detail/config.hpp"
  16. #if !defined(ASIO_NO_IOSTREAM)
  17. #include <streambuf>
  18. #include "asio/basic_socket.hpp"
  19. #include "asio/deadline_timer_service.hpp"
  20. #include "asio/detail/array.hpp"
  21. #include "asio/detail/throw_error.hpp"
  22. #include "asio/io_context.hpp"
  23. #include "asio/stream_socket_service.hpp"
  24. #if defined(ASIO_HAS_BOOST_DATE_TIME)
  25. # include "asio/deadline_timer.hpp"
  26. #else
  27. # include "asio/steady_timer.hpp"
  28. #endif
  29. #if !defined(ASIO_HAS_VARIADIC_TEMPLATES)
  30. # include "asio/detail/variadic_templates.hpp"
  31. // A macro that should expand to:
  32. // template <typename T1, ..., typename Tn>
  33. // basic_socket_streambuf<Protocol, StreamSocketService,
  34. // Time, TimeTraits, TimerService>* connect(
  35. // T1 x1, ..., Tn xn)
  36. // {
  37. // init_buffers();
  38. // this->basic_socket<Protocol, StreamSocketService>::close(ec_);
  39. // typedef typename Protocol::resolver resolver_type;
  40. // resolver_type resolver(detail::socket_streambuf_base::io_context_);
  41. // connect_to_endpoints(
  42. // resolver.resolve(x1, ..., xn, ec_));
  43. // return !ec_ ? this : 0;
  44. // }
  45. // This macro should only persist within this file.
  46. # define ASIO_PRIVATE_CONNECT_DEF(n) \
  47. template <ASIO_VARIADIC_TPARAMS(n)> \
  48. basic_socket_streambuf<Protocol, StreamSocketService, \
  49. Time, TimeTraits, TimerService>* connect( \
  50. ASIO_VARIADIC_BYVAL_PARAMS(n)) \
  51. { \
  52. init_buffers(); \
  53. this->basic_socket<Protocol, StreamSocketService>::close(ec_); \
  54. typedef typename Protocol::resolver resolver_type; \
  55. resolver_type resolver(detail::socket_streambuf_base::io_context_); \
  56. connect_to_endpoints( \
  57. resolver.resolve(ASIO_VARIADIC_BYVAL_ARGS(n), ec_)); \
  58. return !ec_ ? this : 0; \
  59. } \
  60. /**/
  61. #endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)
  62. #include "asio/detail/push_options.hpp"
  63. namespace asio {
  64. namespace detail {
  65. // A separate base class is used to ensure that the io_context is initialised
  66. // prior to the basic_socket_streambuf's basic_socket base class.
  67. class socket_streambuf_base
  68. {
  69. protected:
  70. io_context io_context_;
  71. };
  72. } // namespace detail
  73. /// Iostream streambuf for a socket.
  74. template <typename Protocol,
  75. typename StreamSocketService = stream_socket_service<Protocol>,
  76. #if defined(ASIO_HAS_BOOST_DATE_TIME) \
  77. || defined(GENERATING_DOCUMENTATION)
  78. typename Time = boost::posix_time::ptime,
  79. typename TimeTraits = asio::time_traits<Time>,
  80. typename TimerService = deadline_timer_service<Time, TimeTraits> >
  81. #else
  82. typename Time = steady_timer::clock_type,
  83. typename TimeTraits = steady_timer::traits_type,
  84. typename TimerService = steady_timer::service_type>
  85. #endif
  86. class basic_socket_streambuf
  87. : public std::streambuf,
  88. private detail::socket_streambuf_base,
  89. public basic_socket<Protocol, StreamSocketService>
  90. {
  91. private:
  92. // These typedefs are intended keep this class's implementation independent
  93. // of whether it's using Boost.DateTime, Boost.Chrono or std::chrono.
  94. #if defined(ASIO_HAS_BOOST_DATE_TIME)
  95. typedef TimeTraits traits_helper;
  96. #else
  97. typedef detail::chrono_time_traits<Time, TimeTraits> traits_helper;
  98. #endif
  99. public:
  100. /// The endpoint type.
  101. typedef typename Protocol::endpoint endpoint_type;
  102. #if defined(GENERATING_DOCUMENTATION)
  103. /// (Deprecated: Use time_point.) The time type.
  104. typedef typename TimeTraits::time_type time_type;
  105. /// The time type.
  106. typedef typename TimeTraits::time_point time_point;
  107. /// (Deprecated: Use duration.) The duration type.
  108. typedef typename TimeTraits::duration_type duration_type;
  109. /// The duration type.
  110. typedef typename TimeTraits::duration duration;
  111. #else
  112. # if !defined(ASIO_NO_DEPRECATED)
  113. typedef typename traits_helper::time_type time_type;
  114. typedef typename traits_helper::duration_type duration_type;
  115. # endif // !defined(ASIO_NO_DEPRECATED)
  116. typedef typename traits_helper::time_type time_point;
  117. typedef typename traits_helper::duration_type duration;
  118. #endif
  119. /// Construct a basic_socket_streambuf without establishing a connection.
  120. basic_socket_streambuf()
  121. : basic_socket<Protocol, StreamSocketService>(
  122. this->detail::socket_streambuf_base::io_context_),
  123. unbuffered_(false),
  124. timer_service_(0),
  125. timer_state_(no_timer)
  126. {
  127. init_buffers();
  128. }
  129. /// Destructor flushes buffered data.
  130. virtual ~basic_socket_streambuf()
  131. {
  132. if (pptr() != pbase())
  133. overflow(traits_type::eof());
  134. destroy_timer();
  135. }
  136. /// Establish a connection.
  137. /**
  138. * This function establishes a connection to the specified endpoint.
  139. *
  140. * @return \c this if a connection was successfully established, a null
  141. * pointer otherwise.
  142. */
  143. basic_socket_streambuf<Protocol, StreamSocketService,
  144. Time, TimeTraits, TimerService>* connect(
  145. const endpoint_type& endpoint)
  146. {
  147. init_buffers();
  148. this->basic_socket<Protocol, StreamSocketService>::close(ec_);
  149. if (timer_state_ == timer_has_expired)
  150. {
  151. ec_ = asio::error::operation_aborted;
  152. return 0;
  153. }
  154. io_handler handler = { this };
  155. this->basic_socket<Protocol, StreamSocketService>::async_connect(
  156. endpoint, handler);
  157. ec_ = asio::error::would_block;
  158. this->get_service().get_io_context().restart();
  159. do this->get_service().get_io_context().run_one();
  160. while (ec_ == asio::error::would_block);
  161. return !ec_ ? this : 0;
  162. }
  163. #if defined(GENERATING_DOCUMENTATION)
  164. /// Establish a connection.
  165. /**
  166. * This function automatically establishes a connection based on the supplied
  167. * resolver query parameters. The arguments are used to construct a resolver
  168. * query object.
  169. *
  170. * @return \c this if a connection was successfully established, a null
  171. * pointer otherwise.
  172. */
  173. template <typename T1, ..., typename TN>
  174. basic_socket_streambuf<Protocol, StreamSocketService>* connect(
  175. T1 t1, ..., TN tn);
  176. #elif defined(ASIO_HAS_VARIADIC_TEMPLATES)
  177. template <typename... T>
  178. basic_socket_streambuf<Protocol, StreamSocketService,
  179. Time, TimeTraits, TimerService>* connect(T... x)
  180. {
  181. init_buffers();
  182. this->basic_socket<Protocol, StreamSocketService>::close(ec_);
  183. typedef typename Protocol::resolver resolver_type;
  184. resolver_type resolver(detail::socket_streambuf_base::io_context_);
  185. connect_to_endpoints(resolver.resolve(x..., ec_));
  186. return !ec_ ? this : 0;
  187. }
  188. #else
  189. ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CONNECT_DEF)
  190. #endif
  191. /// Close the connection.
  192. /**
  193. * @return \c this if a connection was successfully established, a null
  194. * pointer otherwise.
  195. */
  196. basic_socket_streambuf<Protocol, StreamSocketService,
  197. Time, TimeTraits, TimerService>* close()
  198. {
  199. sync();
  200. this->basic_socket<Protocol, StreamSocketService>::close(ec_);
  201. if (!ec_)
  202. init_buffers();
  203. return !ec_ ? this : 0;
  204. }
  205. /// Get the last error associated with the stream buffer.
  206. /**
  207. * @return An \c error_code corresponding to the last error from the stream
  208. * buffer.
  209. */
  210. const asio::error_code& puberror() const
  211. {
  212. return error();
  213. }
  214. #if !defined(ASIO_NO_DEPRECATED)
  215. /// (Deprecated: Use expiry().) Get the stream buffer's expiry time as an
  216. /// absolute time.
  217. /**
  218. * @return An absolute time value representing the stream buffer's expiry
  219. * time.
  220. */
  221. time_point expires_at() const
  222. {
  223. return timer_service_
  224. ? timer_service_->expires_at(timer_implementation_)
  225. : time_point();
  226. }
  227. #endif // !defined(ASIO_NO_DEPRECATED)
  228. /// Get the stream buffer's expiry time as an absolute time.
  229. /**
  230. * @return An absolute time value representing the stream buffer's expiry
  231. * time.
  232. */
  233. time_point expiry() const
  234. {
  235. return timer_service_
  236. #if defined(ASIO_HAS_BOOST_DATE_TIME)
  237. ? timer_service_->expires_at(timer_implementation_)
  238. #else // defined(ASIO_HAS_BOOST_DATE_TIME)
  239. ? timer_service_->expiry(timer_implementation_)
  240. #endif // defined(ASIO_HAS_BOOST_DATE_TIME)
  241. : time_point();
  242. }
  243. /// Set the stream buffer's expiry time as an absolute time.
  244. /**
  245. * This function sets the expiry time associated with the stream. Stream
  246. * operations performed after this time (where the operations cannot be
  247. * completed using the internal buffers) will fail with the error
  248. * asio::error::operation_aborted.
  249. *
  250. * @param expiry_time The expiry time to be used for the stream.
  251. */
  252. void expires_at(const time_point& expiry_time)
  253. {
  254. construct_timer();
  255. asio::error_code ec;
  256. timer_service_->expires_at(timer_implementation_, expiry_time, ec);
  257. asio::detail::throw_error(ec, "expires_at");
  258. start_timer();
  259. }
  260. /// Set the stream buffer's expiry time relative to now.
  261. /**
  262. * This function sets the expiry time associated with the stream. Stream
  263. * operations performed after this time (where the operations cannot be
  264. * completed using the internal buffers) will fail with the error
  265. * asio::error::operation_aborted.
  266. *
  267. * @param expiry_time The expiry time to be used for the timer.
  268. */
  269. void expires_at(const duration& expiry_time)
  270. {
  271. construct_timer();
  272. asio::error_code ec;
  273. timer_service_->expires_from_now(timer_implementation_, expiry_time, ec);
  274. asio::detail::throw_error(ec, "expires_from_now");
  275. start_timer();
  276. }
  277. /// Set the stream buffer's expiry time relative to now.
  278. /**
  279. * This function sets the expiry time associated with the stream. Stream
  280. * operations performed after this time (where the operations cannot be
  281. * completed using the internal buffers) will fail with the error
  282. * asio::error::operation_aborted.
  283. *
  284. * @param expiry_time The expiry time to be used for the timer.
  285. */
  286. void expires_after(const duration& expiry_time)
  287. {
  288. construct_timer();
  289. asio::error_code ec;
  290. #if defined(ASIO_HAS_BOOST_DATE_TIME)
  291. timer_service_->expires_from_now(timer_implementation_, expiry_time, ec);
  292. #else // defined(ASIO_HAS_BOOST_DATE_TIME)
  293. timer_service_->expires_after(timer_implementation_, expiry_time, ec);
  294. #endif // defined(ASIO_HAS_BOOST_DATE_TIME)
  295. asio::detail::throw_error(ec, "after");
  296. start_timer();
  297. }
  298. #if !defined(ASIO_NO_DEPRECATED)
  299. /// (Deprecated: Use expiry().) Get the stream buffer's expiry time relative
  300. /// to now.
  301. /**
  302. * @return A relative time value representing the stream buffer's expiry time.
  303. */
  304. duration expires_from_now() const
  305. {
  306. return traits_helper::subtract(expires_at(), traits_helper::now());
  307. }
  308. /// (Deprecated: Use expires_after().) Set the stream buffer's expiry time
  309. /// relative to now.
  310. /**
  311. * This function sets the expiry time associated with the stream. Stream
  312. * operations performed after this time (where the operations cannot be
  313. * completed using the internal buffers) will fail with the error
  314. * asio::error::operation_aborted.
  315. *
  316. * @param expiry_time The expiry time to be used for the timer.
  317. */
  318. void expires_from_now(const duration& expiry_time)
  319. {
  320. construct_timer();
  321. asio::error_code ec;
  322. timer_service_->expires_from_now(timer_implementation_, expiry_time, ec);
  323. asio::detail::throw_error(ec, "expires_from_now");
  324. start_timer();
  325. }
  326. #endif // !defined(ASIO_NO_DEPRECATED)
  327. protected:
  328. int_type underflow()
  329. {
  330. if (gptr() == egptr())
  331. {
  332. if (timer_state_ == timer_has_expired)
  333. {
  334. ec_ = asio::error::operation_aborted;
  335. return traits_type::eof();
  336. }
  337. io_handler handler = { this };
  338. this->get_service().async_receive(this->get_implementation(),
  339. asio::buffer(asio::buffer(get_buffer_) + putback_max),
  340. 0, handler);
  341. ec_ = asio::error::would_block;
  342. this->get_service().get_io_context().restart();
  343. do this->get_service().get_io_context().run_one();
  344. while (ec_ == asio::error::would_block);
  345. if (ec_)
  346. return traits_type::eof();
  347. setg(&get_buffer_[0], &get_buffer_[0] + putback_max,
  348. &get_buffer_[0] + putback_max + bytes_transferred_);
  349. return traits_type::to_int_type(*gptr());
  350. }
  351. else
  352. {
  353. return traits_type::eof();
  354. }
  355. }
  356. int_type overflow(int_type c)
  357. {
  358. if (unbuffered_)
  359. {
  360. if (traits_type::eq_int_type(c, traits_type::eof()))
  361. {
  362. // Nothing to do.
  363. return traits_type::not_eof(c);
  364. }
  365. else
  366. {
  367. if (timer_state_ == timer_has_expired)
  368. {
  369. ec_ = asio::error::operation_aborted;
  370. return traits_type::eof();
  371. }
  372. // Send the single character immediately.
  373. char_type ch = traits_type::to_char_type(c);
  374. io_handler handler = { this };
  375. this->get_service().async_send(this->get_implementation(),
  376. asio::buffer(&ch, sizeof(char_type)), 0, handler);
  377. ec_ = asio::error::would_block;
  378. this->get_service().get_io_context().restart();
  379. do this->get_service().get_io_context().run_one();
  380. while (ec_ == asio::error::would_block);
  381. if (ec_)
  382. return traits_type::eof();
  383. return c;
  384. }
  385. }
  386. else
  387. {
  388. // Send all data in the output buffer.
  389. asio::const_buffer buffer =
  390. asio::buffer(pbase(), pptr() - pbase());
  391. while (buffer.size() > 0)
  392. {
  393. if (timer_state_ == timer_has_expired)
  394. {
  395. ec_ = asio::error::operation_aborted;
  396. return traits_type::eof();
  397. }
  398. io_handler handler = { this };
  399. this->get_service().async_send(this->get_implementation(),
  400. asio::buffer(buffer), 0, handler);
  401. ec_ = asio::error::would_block;
  402. this->get_service().get_io_context().restart();
  403. do this->get_service().get_io_context().run_one();
  404. while (ec_ == asio::error::would_block);
  405. if (ec_)
  406. return traits_type::eof();
  407. buffer = buffer + bytes_transferred_;
  408. }
  409. setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
  410. // If the new character is eof then our work here is done.
  411. if (traits_type::eq_int_type(c, traits_type::eof()))
  412. return traits_type::not_eof(c);
  413. // Add the new character to the output buffer.
  414. *pptr() = traits_type::to_char_type(c);
  415. pbump(1);
  416. return c;
  417. }
  418. }
  419. int sync()
  420. {
  421. return overflow(traits_type::eof());
  422. }
  423. std::streambuf* setbuf(char_type* s, std::streamsize n)
  424. {
  425. if (pptr() == pbase() && s == 0 && n == 0)
  426. {
  427. unbuffered_ = true;
  428. setp(0, 0);
  429. return this;
  430. }
  431. return 0;
  432. }
  433. /// Get the last error associated with the stream buffer.
  434. /**
  435. * @return An \c error_code corresponding to the last error from the stream
  436. * buffer.
  437. */
  438. virtual const asio::error_code& error() const
  439. {
  440. return ec_;
  441. }
  442. private:
  443. void init_buffers()
  444. {
  445. setg(&get_buffer_[0],
  446. &get_buffer_[0] + putback_max,
  447. &get_buffer_[0] + putback_max);
  448. if (unbuffered_)
  449. setp(0, 0);
  450. else
  451. setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
  452. }
  453. template <typename EndpointSequence>
  454. void connect_to_endpoints(const EndpointSequence& endpoints)
  455. {
  456. if (!ec_)
  457. {
  458. typename EndpointSequence::iterator i = endpoints.begin();
  459. typename EndpointSequence::iterator end = endpoints.end();
  460. ec_ = asio::error::host_not_found;
  461. while (ec_ && i != end)
  462. {
  463. this->basic_socket<Protocol, StreamSocketService>::close(ec_);
  464. if (timer_state_ == timer_has_expired)
  465. {
  466. ec_ = asio::error::operation_aborted;
  467. return;
  468. }
  469. io_handler handler = { this };
  470. this->basic_socket<Protocol, StreamSocketService>::async_connect(
  471. *i, handler);
  472. ec_ = asio::error::would_block;
  473. this->get_service().get_io_context().restart();
  474. do this->get_service().get_io_context().run_one();
  475. while (ec_ == asio::error::would_block);
  476. ++i;
  477. }
  478. }
  479. }
  480. struct io_handler;
  481. friend struct io_handler;
  482. struct io_handler
  483. {
  484. basic_socket_streambuf* this_;
  485. void operator()(const asio::error_code& ec,
  486. std::size_t bytes_transferred = 0)
  487. {
  488. this_->ec_ = ec;
  489. this_->bytes_transferred_ = bytes_transferred;
  490. }
  491. };
  492. struct timer_handler;
  493. friend struct timer_handler;
  494. struct timer_handler
  495. {
  496. basic_socket_streambuf* this_;
  497. void operator()(const asio::error_code&)
  498. {
  499. time_point now = traits_helper::now();
  500. #if defined(ASIO_HAS_BOOST_DATE_TIME)
  501. time_point expiry_time = this_->timer_service_->expires_at(
  502. this_->timer_implementation_);
  503. #else // defined(ASIO_HAS_BOOST_DATE_TIME)
  504. time_point expiry_time = this_->timer_service_->expiry(
  505. this_->timer_implementation_);
  506. #endif // defined(ASIO_HAS_BOOST_DATE_TIME)
  507. if (traits_helper::less_than(now, expiry_time))
  508. {
  509. this_->timer_state_ = timer_is_pending;
  510. this_->timer_service_->async_wait(this_->timer_implementation_, *this);
  511. }
  512. else
  513. {
  514. this_->timer_state_ = timer_has_expired;
  515. asio::error_code ec;
  516. this_->basic_socket<Protocol, StreamSocketService>::close(ec);
  517. }
  518. }
  519. };
  520. void construct_timer()
  521. {
  522. if (timer_service_ == 0)
  523. {
  524. TimerService& timer_service = use_service<TimerService>(
  525. detail::socket_streambuf_base::io_context_);
  526. timer_service.construct(timer_implementation_);
  527. timer_service_ = &timer_service;
  528. }
  529. }
  530. void destroy_timer()
  531. {
  532. if (timer_service_)
  533. timer_service_->destroy(timer_implementation_);
  534. }
  535. void start_timer()
  536. {
  537. if (timer_state_ != timer_is_pending)
  538. {
  539. timer_handler handler = { this };
  540. handler(asio::error_code());
  541. }
  542. }
  543. enum { putback_max = 8 };
  544. enum { buffer_size = 512 };
  545. asio::detail::array<char, buffer_size> get_buffer_;
  546. asio::detail::array<char, buffer_size> put_buffer_;
  547. bool unbuffered_;
  548. asio::error_code ec_;
  549. std::size_t bytes_transferred_;
  550. TimerService* timer_service_;
  551. typename TimerService::implementation_type timer_implementation_;
  552. enum state { no_timer, timer_is_pending, timer_has_expired } timer_state_;
  553. };
  554. } // namespace asio
  555. #include "asio/detail/pop_options.hpp"
  556. #if !defined(ASIO_HAS_VARIADIC_TEMPLATES)
  557. # undef ASIO_PRIVATE_CONNECT_DEF
  558. #endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)
  559. #endif // !defined(ASIO_NO_IOSTREAM)
  560. #endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP