basic_socket_streambuf.hpp 16 KB

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