2 // basic_socket_streambuf.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
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)
11 #ifndef BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP
12 #define BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18 #include <boost/asio/detail/config.hpp>
20 #if !defined(BOOST_ASIO_NO_IOSTREAM)
23 #include <boost/asio/basic_socket.hpp>
24 #include <boost/asio/deadline_timer_service.hpp>
25 #include <boost/asio/detail/array.hpp>
26 #include <boost/asio/detail/throw_error.hpp>
27 #include <boost/asio/io_service.hpp>
28 #include <boost/asio/stream_socket_service.hpp>
30 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
31 # include <boost/asio/deadline_timer.hpp>
33 # include <boost/asio/steady_timer.hpp>
36 #if !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
38 # include <boost/asio/detail/variadic_templates.hpp>
40 // A macro that should expand to:
41 // template <typename T1, ..., typename Tn>
42 // basic_socket_streambuf<Protocol, StreamSocketService,
43 // Time, TimeTraits, TimerService>* connect(
47 // this->basic_socket<Protocol, StreamSocketService>::close(ec_);
48 // typedef typename Protocol::resolver resolver_type;
49 // typedef typename resolver_type::query resolver_query;
50 // resolver_query query(x1, ..., xn);
51 // resolve_and_connect(query);
52 // return !ec_ ? this : 0;
54 // This macro should only persist within this file.
56 # define BOOST_ASIO_PRIVATE_CONNECT_DEF(n) \
57 template <BOOST_ASIO_VARIADIC_TPARAMS(n)> \
58 basic_socket_streambuf<Protocol, StreamSocketService, \
59 Time, TimeTraits, TimerService>* connect(BOOST_ASIO_VARIADIC_PARAMS(n)) \
62 this->basic_socket<Protocol, StreamSocketService>::close(ec_); \
63 typedef typename Protocol::resolver resolver_type; \
64 typedef typename resolver_type::query resolver_query; \
65 resolver_query query(BOOST_ASIO_VARIADIC_ARGS(n)); \
66 resolve_and_connect(query); \
67 return !ec_ ? this : 0; \
71 #endif // !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
73 #include <boost/asio/detail/push_options.hpp>
79 // A separate base class is used to ensure that the io_service is initialised
80 // prior to the basic_socket_streambuf's basic_socket base class.
81 class socket_streambuf_base
84 io_service io_service_;
89 /// Iostream streambuf for a socket.
90 template <typename Protocol,
91 typename StreamSocketService = stream_socket_service<Protocol>,
92 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) \
93 || defined(GENERATING_DOCUMENTATION)
94 typename Time = boost::posix_time::ptime,
95 typename TimeTraits = boost::asio::time_traits<Time>,
96 typename TimerService = deadline_timer_service<Time, TimeTraits> >
98 typename Time = steady_timer::clock_type,
99 typename TimeTraits = steady_timer::traits_type,
100 typename TimerService = steady_timer::service_type>
102 class basic_socket_streambuf
103 : public std::streambuf,
104 private detail::socket_streambuf_base,
105 public basic_socket<Protocol, StreamSocketService>
108 // These typedefs are intended keep this class's implementation independent
109 // of whether it's using Boost.DateTime, Boost.Chrono or std::chrono.
110 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
111 typedef TimeTraits traits_helper;
113 typedef detail::chrono_time_traits<Time, TimeTraits> traits_helper;
117 /// The endpoint type.
118 typedef typename Protocol::endpoint endpoint_type;
120 #if defined(GENERATING_DOCUMENTATION)
122 typedef typename TimeTraits::time_type time_type;
124 /// The duration type.
125 typedef typename TimeTraits::duration_type duration_type;
127 typedef typename traits_helper::time_type time_type;
128 typedef typename traits_helper::duration_type duration_type;
131 /// Construct a basic_socket_streambuf without establishing a connection.
132 basic_socket_streambuf()
133 : basic_socket<Protocol, StreamSocketService>(
134 this->detail::socket_streambuf_base::io_service_),
137 timer_state_(no_timer)
142 /// Destructor flushes buffered data.
143 virtual ~basic_socket_streambuf()
145 if (pptr() != pbase())
146 overflow(traits_type::eof());
151 /// Establish a connection.
153 * This function establishes a connection to the specified endpoint.
155 * @return \c this if a connection was successfully established, a null
158 basic_socket_streambuf<Protocol, StreamSocketService,
159 Time, TimeTraits, TimerService>* connect(
160 const endpoint_type& endpoint)
164 this->basic_socket<Protocol, StreamSocketService>::close(ec_);
166 if (timer_state_ == timer_has_expired)
168 ec_ = boost::asio::error::operation_aborted;
172 io_handler handler = { this };
173 this->basic_socket<Protocol, StreamSocketService>::async_connect(
176 ec_ = boost::asio::error::would_block;
177 this->get_service().get_io_service().reset();
178 do this->get_service().get_io_service().run_one();
179 while (ec_ == boost::asio::error::would_block);
181 return !ec_ ? this : 0;
184 #if defined(GENERATING_DOCUMENTATION)
185 /// Establish a connection.
187 * This function automatically establishes a connection based on the supplied
188 * resolver query parameters. The arguments are used to construct a resolver
191 * @return \c this if a connection was successfully established, a null
194 template <typename T1, ..., typename TN>
195 basic_socket_streambuf<Protocol, StreamSocketService>* connect(
197 #elif defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
198 template <typename... T>
199 basic_socket_streambuf<Protocol, StreamSocketService,
200 Time, TimeTraits, TimerService>* connect(T... x)
203 this->basic_socket<Protocol, StreamSocketService>::close(ec_);
204 typedef typename Protocol::resolver resolver_type;
205 typedef typename resolver_type::query resolver_query;
206 resolver_query query(x...);
207 resolve_and_connect(query);
208 return !ec_ ? this : 0;
211 BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_CONNECT_DEF)
214 /// Close the connection.
216 * @return \c this if a connection was successfully established, a null
219 basic_socket_streambuf<Protocol, StreamSocketService,
220 Time, TimeTraits, TimerService>* close()
223 this->basic_socket<Protocol, StreamSocketService>::close(ec_);
226 return !ec_ ? this : 0;
229 /// Get the last error associated with the stream buffer.
231 * @return An \c error_code corresponding to the last error from the stream
234 const boost::system::error_code& puberror() const
239 /// Get the stream buffer's expiry time as an absolute time.
241 * @return An absolute time value representing the stream buffer's expiry
244 time_type expires_at() const
246 return timer_service_
247 ? timer_service_->expires_at(timer_implementation_)
251 /// Set the stream buffer's expiry time as an absolute time.
253 * This function sets the expiry time associated with the stream. Stream
254 * operations performed after this time (where the operations cannot be
255 * completed using the internal buffers) will fail with the error
256 * boost::asio::error::operation_aborted.
258 * @param expiry_time The expiry time to be used for the stream.
260 void expires_at(const time_type& expiry_time)
264 boost::system::error_code ec;
265 timer_service_->expires_at(timer_implementation_, expiry_time, ec);
266 boost::asio::detail::throw_error(ec, "expires_at");
271 /// Get the stream buffer's expiry time relative to now.
273 * @return A relative time value representing the stream buffer's expiry time.
275 duration_type expires_from_now() const
277 return traits_helper::subtract(expires_at(), traits_helper::now());
280 /// Set the stream buffer's expiry time relative to now.
282 * This function sets the expiry time associated with the stream. Stream
283 * operations performed after this time (where the operations cannot be
284 * completed using the internal buffers) will fail with the error
285 * boost::asio::error::operation_aborted.
287 * @param expiry_time The expiry time to be used for the timer.
289 void expires_from_now(const duration_type& expiry_time)
293 boost::system::error_code ec;
294 timer_service_->expires_from_now(timer_implementation_, expiry_time, ec);
295 boost::asio::detail::throw_error(ec, "expires_from_now");
303 if (gptr() == egptr())
305 if (timer_state_ == timer_has_expired)
307 ec_ = boost::asio::error::operation_aborted;
308 return traits_type::eof();
311 io_handler handler = { this };
312 this->get_service().async_receive(this->get_implementation(),
313 boost::asio::buffer(boost::asio::buffer(get_buffer_) + putback_max),
316 ec_ = boost::asio::error::would_block;
317 this->get_service().get_io_service().reset();
318 do this->get_service().get_io_service().run_one();
319 while (ec_ == boost::asio::error::would_block);
321 return traits_type::eof();
323 setg(&get_buffer_[0], &get_buffer_[0] + putback_max,
324 &get_buffer_[0] + putback_max + bytes_transferred_);
325 return traits_type::to_int_type(*gptr());
329 return traits_type::eof();
333 int_type overflow(int_type c)
337 if (traits_type::eq_int_type(c, traits_type::eof()))
340 return traits_type::not_eof(c);
344 if (timer_state_ == timer_has_expired)
346 ec_ = boost::asio::error::operation_aborted;
347 return traits_type::eof();
350 // Send the single character immediately.
351 char_type ch = traits_type::to_char_type(c);
352 io_handler handler = { this };
353 this->get_service().async_send(this->get_implementation(),
354 boost::asio::buffer(&ch, sizeof(char_type)), 0, handler);
356 ec_ = boost::asio::error::would_block;
357 this->get_service().get_io_service().reset();
358 do this->get_service().get_io_service().run_one();
359 while (ec_ == boost::asio::error::would_block);
361 return traits_type::eof();
368 // Send all data in the output buffer.
369 boost::asio::const_buffer buffer =
370 boost::asio::buffer(pbase(), pptr() - pbase());
371 while (boost::asio::buffer_size(buffer) > 0)
373 if (timer_state_ == timer_has_expired)
375 ec_ = boost::asio::error::operation_aborted;
376 return traits_type::eof();
379 io_handler handler = { this };
380 this->get_service().async_send(this->get_implementation(),
381 boost::asio::buffer(buffer), 0, handler);
383 ec_ = boost::asio::error::would_block;
384 this->get_service().get_io_service().reset();
385 do this->get_service().get_io_service().run_one();
386 while (ec_ == boost::asio::error::would_block);
388 return traits_type::eof();
390 buffer = buffer + bytes_transferred_;
392 setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
394 // If the new character is eof then our work here is done.
395 if (traits_type::eq_int_type(c, traits_type::eof()))
396 return traits_type::not_eof(c);
398 // Add the new character to the output buffer.
399 *pptr() = traits_type::to_char_type(c);
407 return overflow(traits_type::eof());
410 std::streambuf* setbuf(char_type* s, std::streamsize n)
412 if (pptr() == pbase() && s == 0 && n == 0)
422 /// Get the last error associated with the stream buffer.
424 * @return An \c error_code corresponding to the last error from the stream
427 virtual const boost::system::error_code& error() const
435 setg(&get_buffer_[0],
436 &get_buffer_[0] + putback_max,
437 &get_buffer_[0] + putback_max);
441 setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
444 template <typename ResolverQuery>
445 void resolve_and_connect(const ResolverQuery& query)
447 typedef typename Protocol::resolver resolver_type;
448 typedef typename resolver_type::iterator iterator_type;
449 resolver_type resolver(detail::socket_streambuf_base::io_service_);
450 iterator_type i = resolver.resolve(query, ec_);
454 ec_ = boost::asio::error::host_not_found;
455 while (ec_ && i != end)
457 this->basic_socket<Protocol, StreamSocketService>::close(ec_);
459 if (timer_state_ == timer_has_expired)
461 ec_ = boost::asio::error::operation_aborted;
465 io_handler handler = { this };
466 this->basic_socket<Protocol, StreamSocketService>::async_connect(
469 ec_ = boost::asio::error::would_block;
470 this->get_service().get_io_service().reset();
471 do this->get_service().get_io_service().run_one();
472 while (ec_ == boost::asio::error::would_block);
480 friend struct io_handler;
483 basic_socket_streambuf* this_;
485 void operator()(const boost::system::error_code& ec,
486 std::size_t bytes_transferred = 0)
489 this_->bytes_transferred_ = bytes_transferred;
493 struct timer_handler;
494 friend struct timer_handler;
497 basic_socket_streambuf* this_;
499 void operator()(const boost::system::error_code&)
501 time_type now = traits_helper::now();
503 time_type expiry_time = this_->timer_service_->expires_at(
504 this_->timer_implementation_);
506 if (traits_helper::less_than(now, expiry_time))
508 this_->timer_state_ = timer_is_pending;
509 this_->timer_service_->async_wait(this_->timer_implementation_, *this);
513 this_->timer_state_ = timer_has_expired;
514 boost::system::error_code ec;
515 this_->basic_socket<Protocol, StreamSocketService>::close(ec);
520 void construct_timer()
522 if (timer_service_ == 0)
524 TimerService& timer_service = use_service<TimerService>(
525 detail::socket_streambuf_base::io_service_);
526 timer_service.construct(timer_implementation_);
527 timer_service_ = &timer_service;
534 timer_service_->destroy(timer_implementation_);
539 if (timer_state_ != timer_is_pending)
541 timer_handler handler = { this };
542 handler(boost::system::error_code());
546 enum { putback_max = 8 };
547 enum { buffer_size = 512 };
548 boost::asio::detail::array<char, buffer_size> get_buffer_;
549 boost::asio::detail::array<char, buffer_size> put_buffer_;
551 boost::system::error_code ec_;
552 std::size_t bytes_transferred_;
553 TimerService* timer_service_;
554 typename TimerService::implementation_type timer_implementation_;
555 enum state { no_timer, timer_is_pending, timer_has_expired } timer_state_;
561 #include <boost/asio/detail/pop_options.hpp>
563 #if !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
564 # undef BOOST_ASIO_PRIVATE_CONNECT_DEF
565 #endif // !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
567 #endif // !defined(BOOST_ASIO_NO_IOSTREAM)
569 #endif // BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP