Imported Upstream version 1.57.0
[platform/upstream/boost.git] / boost / asio / basic_socket_streambuf.hpp
1 //
2 // basic_socket_streambuf.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2014 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 BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP
12 #define BOOST_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 <boost/asio/detail/config.hpp>
19
20 #if !defined(BOOST_ASIO_NO_IOSTREAM)
21
22 #include <streambuf>
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>
29
30 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
31 # include <boost/asio/deadline_timer.hpp>
32 #else
33 # include <boost/asio/steady_timer.hpp>
34 #endif
35
36 #if !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
37
38 # include <boost/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 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)) \
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(BOOST_ASIO_VARIADIC_ARGS(n)); \
66     resolve_and_connect(query); \
67     return !ec_ ? this : 0; \
68   } \
69   /**/
70
71 #endif // !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
72
73 #include <boost/asio/detail/push_options.hpp>
74
75 namespace boost {
76 namespace asio {
77 namespace detail {
78
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
82 {
83 protected:
84   io_service io_service_;
85 };
86
87 } // namespace detail
88
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> >
97 #else
98     typename Time = steady_timer::clock_type,
99     typename TimeTraits = steady_timer::traits_type,
100     typename TimerService = steady_timer::service_type>
101 #endif
102 class basic_socket_streambuf
103   : public std::streambuf,
104     private detail::socket_streambuf_base,
105     public basic_socket<Protocol, StreamSocketService>
106 {
107 private:
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;
112 #else
113   typedef detail::chrono_time_traits<Time, TimeTraits> traits_helper;
114 #endif
115
116 public:
117   /// The endpoint type.
118   typedef typename Protocol::endpoint endpoint_type;
119
120 #if defined(GENERATING_DOCUMENTATION)
121   /// The time type.
122   typedef typename TimeTraits::time_type time_type;
123
124   /// The duration type.
125   typedef typename TimeTraits::duration_type duration_type;
126 #else
127   typedef typename traits_helper::time_type time_type;
128   typedef typename traits_helper::duration_type duration_type;
129 #endif
130
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_),
135       unbuffered_(false),
136       timer_service_(0),
137       timer_state_(no_timer)
138   {
139     init_buffers();
140   }
141
142   /// Destructor flushes buffered data.
143   virtual ~basic_socket_streambuf()
144   {
145     if (pptr() != pbase())
146       overflow(traits_type::eof());
147
148     destroy_timer();
149   }
150
151   /// Establish a connection.
152   /**
153    * This function establishes a connection to the specified endpoint.
154    *
155    * @return \c this if a connection was successfully established, a null
156    * pointer otherwise.
157    */
158   basic_socket_streambuf<Protocol, StreamSocketService,
159     Time, TimeTraits, TimerService>* connect(
160       const endpoint_type& endpoint)
161   {
162     init_buffers();
163
164     this->basic_socket<Protocol, StreamSocketService>::close(ec_);
165
166     if (timer_state_ == timer_has_expired)
167     {
168       ec_ = boost::asio::error::operation_aborted;
169       return 0;
170     }
171
172     io_handler handler = { this };
173     this->basic_socket<Protocol, StreamSocketService>::async_connect(
174         endpoint, handler);
175
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);
180
181     return !ec_ ? this : 0;
182   }
183
184 #if defined(GENERATING_DOCUMENTATION)
185   /// Establish a connection.
186   /**
187    * This function automatically establishes a connection based on the supplied
188    * resolver query parameters. The arguments are used to construct a resolver
189    * query object.
190    *
191    * @return \c this if a connection was successfully established, a null
192    * pointer otherwise.
193    */
194   template <typename T1, ..., typename TN>
195   basic_socket_streambuf<Protocol, StreamSocketService>* connect(
196       T1 t1, ..., TN tn);
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)
201   {
202     init_buffers();
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;
209   }
210 #else
211   BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_CONNECT_DEF)
212 #endif
213
214   /// Close the connection.
215   /**
216    * @return \c this if a connection was successfully established, a null
217    * pointer otherwise.
218    */
219   basic_socket_streambuf<Protocol, StreamSocketService,
220     Time, TimeTraits, TimerService>* close()
221   {
222     sync();
223     this->basic_socket<Protocol, StreamSocketService>::close(ec_);
224     if (!ec_)
225       init_buffers();
226     return !ec_ ? this : 0;
227   }
228
229   /// Get the last error associated with the stream buffer.
230   /**
231    * @return An \c error_code corresponding to the last error from the stream
232    * buffer.
233    */
234   const boost::system::error_code& puberror() const
235   {
236     return error();
237   }
238
239   /// Get the stream buffer's expiry time as an absolute time.
240   /**
241    * @return An absolute time value representing the stream buffer's expiry
242    * time.
243    */
244   time_type expires_at() const
245   {
246     return timer_service_
247       ? timer_service_->expires_at(timer_implementation_)
248       : time_type();
249   }
250
251   /// Set the stream buffer's expiry time as an absolute time.
252   /**
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.
257    *
258    * @param expiry_time The expiry time to be used for the stream.
259    */
260   void expires_at(const time_type& expiry_time)
261   {
262     construct_timer();
263
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");
267
268     start_timer();
269   }
270
271   /// Get the stream buffer's expiry time relative to now.
272   /**
273    * @return A relative time value representing the stream buffer's expiry time.
274    */
275   duration_type expires_from_now() const
276   {
277     return traits_helper::subtract(expires_at(), traits_helper::now());
278   }
279
280   /// Set the stream buffer's expiry time relative to now.
281   /**
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.
286    *
287    * @param expiry_time The expiry time to be used for the timer.
288    */
289   void expires_from_now(const duration_type& expiry_time)
290   {
291     construct_timer();
292
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");
296
297     start_timer();
298   }
299
300 protected:
301   int_type underflow()
302   {
303     if (gptr() == egptr())
304     {
305       if (timer_state_ == timer_has_expired)
306       {
307         ec_ = boost::asio::error::operation_aborted;
308         return traits_type::eof();
309       }
310
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),
314           0, handler);
315
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);
320       if (ec_)
321         return traits_type::eof();
322
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());
326     }
327     else
328     {
329       return traits_type::eof();
330     }
331   }
332
333   int_type overflow(int_type c)
334   {
335     if (unbuffered_)
336     {
337       if (traits_type::eq_int_type(c, traits_type::eof()))
338       {
339         // Nothing to do.
340         return traits_type::not_eof(c);
341       }
342       else
343       {
344         if (timer_state_ == timer_has_expired)
345         {
346           ec_ = boost::asio::error::operation_aborted;
347           return traits_type::eof();
348         }
349
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);
355
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);
360         if (ec_)
361           return traits_type::eof();
362
363         return c;
364       }
365     }
366     else
367     {
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)
372       {
373         if (timer_state_ == timer_has_expired)
374         {
375           ec_ = boost::asio::error::operation_aborted;
376           return traits_type::eof();
377         }
378
379         io_handler handler = { this };
380         this->get_service().async_send(this->get_implementation(),
381             boost::asio::buffer(buffer), 0, handler);
382
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);
387         if (ec_)
388           return traits_type::eof();
389
390         buffer = buffer + bytes_transferred_;
391       }
392       setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
393
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);
397
398       // Add the new character to the output buffer.
399       *pptr() = traits_type::to_char_type(c);
400       pbump(1);
401       return c;
402     }
403   }
404
405   int sync()
406   {
407     return overflow(traits_type::eof());
408   }
409
410   std::streambuf* setbuf(char_type* s, std::streamsize n)
411   {
412     if (pptr() == pbase() && s == 0 && n == 0)
413     {
414       unbuffered_ = true;
415       setp(0, 0);
416       return this;
417     }
418
419     return 0;
420   }
421
422   /// Get the last error associated with the stream buffer.
423   /**
424    * @return An \c error_code corresponding to the last error from the stream
425    * buffer.
426    */
427   virtual const boost::system::error_code& error() const
428   {
429     return ec_;
430   }
431
432 private:
433   void init_buffers()
434   {
435     setg(&get_buffer_[0],
436         &get_buffer_[0] + putback_max,
437         &get_buffer_[0] + putback_max);
438     if (unbuffered_)
439       setp(0, 0);
440     else
441       setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
442   }
443
444   template <typename ResolverQuery>
445   void resolve_and_connect(const ResolverQuery& query)
446   {
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_);
451     if (!ec_)
452     {
453       iterator_type end;
454       ec_ = boost::asio::error::host_not_found;
455       while (ec_ && i != end)
456       {
457         this->basic_socket<Protocol, StreamSocketService>::close(ec_);
458
459         if (timer_state_ == timer_has_expired)
460         {
461           ec_ = boost::asio::error::operation_aborted;
462           return;
463         }
464
465         io_handler handler = { this };
466         this->basic_socket<Protocol, StreamSocketService>::async_connect(
467             *i, handler);
468
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);
473
474         ++i;
475       }
476     }
477   }
478
479   struct io_handler;
480   friend struct io_handler;
481   struct io_handler
482   {
483     basic_socket_streambuf* this_;
484
485     void operator()(const boost::system::error_code& ec,
486         std::size_t bytes_transferred = 0)
487     {
488       this_->ec_ = ec;
489       this_->bytes_transferred_ = bytes_transferred;
490     }
491   };
492
493   struct timer_handler;
494   friend struct timer_handler;
495   struct timer_handler
496   {
497     basic_socket_streambuf* this_;
498
499     void operator()(const boost::system::error_code&)
500     {
501       time_type now = traits_helper::now();
502
503       time_type expiry_time = this_->timer_service_->expires_at(
504             this_->timer_implementation_);
505
506       if (traits_helper::less_than(now, expiry_time))
507       {
508         this_->timer_state_ = timer_is_pending;
509         this_->timer_service_->async_wait(this_->timer_implementation_, *this);
510       }
511       else
512       {
513         this_->timer_state_ = timer_has_expired;
514         boost::system::error_code ec;
515         this_->basic_socket<Protocol, StreamSocketService>::close(ec);
516       }
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_service_);
526       timer_service.construct(timer_implementation_);
527       timer_service_ = &timer_service;
528     }
529   }
530
531   void destroy_timer()
532   {
533     if (timer_service_)
534       timer_service_->destroy(timer_implementation_);
535   }
536
537   void start_timer()
538   {
539     if (timer_state_ != timer_is_pending)
540     {
541       timer_handler handler = { this };
542       handler(boost::system::error_code());
543     }
544   }
545
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_;
550   bool unbuffered_;
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_;
556 };
557
558 } // namespace asio
559 } // namespace boost
560
561 #include <boost/asio/detail/pop_options.hpp>
562
563 #if !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
564 # undef BOOST_ASIO_PRIVATE_CONNECT_DEF
565 #endif // !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
566
567 #endif // !defined(BOOST_ASIO_NO_IOSTREAM)
568
569 #endif // BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP