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