1 //
2 // basic_socket_streambuf.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2020 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 <vector>
24 #include <boost/asio/basic_socket.hpp>
25 #include <boost/asio/basic_stream_socket.hpp>
26 #include <boost/asio/detail/buffer_sequence_adapter.hpp>
27 #include <boost/asio/detail/memory.hpp>
28 #include <boost/asio/detail/throw_error.hpp>
29 #include <boost/asio/io_context.hpp>
30 
31 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) \
32   && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
33 # include <boost/asio/detail/deadline_timer_service.hpp>
34 #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
35       // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
36 # include <boost/asio/steady_timer.hpp>
37 #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
38        // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
39 
40 #if !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
41 
42 # include <boost/asio/detail/variadic_templates.hpp>
43 
44 // A macro that should expand to:
45 //   template <typename T1, ..., typename Tn>
46 //   basic_socket_streambuf* connect(T1 x1, ..., Tn xn)
47 //   {
48 //     init_buffers();
49 //     typedef typename Protocol::resolver resolver_type;
50 //     resolver_type resolver(socket().get_executor());
51 //     connect_to_endpoints(
52 //         resolver.resolve(x1, ..., xn, ec_));
53 //     return !ec_ ? this : 0;
54 //   }
55 // This macro should only persist within this file.
56 
57 # define BOOST_ASIO_PRIVATE_CONNECT_DEF(n) \
58   template <BOOST_ASIO_VARIADIC_TPARAMS(n)> \
59   basic_socket_streambuf* connect(BOOST_ASIO_VARIADIC_BYVAL_PARAMS(n)) \
60   { \
61     init_buffers(); \
62     typedef typename Protocol::resolver resolver_type; \
63     resolver_type resolver(socket().get_executor()); \
64     connect_to_endpoints( \
65         resolver.resolve(BOOST_ASIO_VARIADIC_BYVAL_ARGS(n), ec_)); \
66     return !ec_ ? this : 0; \
67   } \
68   /**/
69 
70 #endif // !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
71 
72 #include <boost/asio/detail/push_options.hpp>
73 
74 namespace boost {
75 namespace asio {
76 namespace detail {
77 
78 // A separate base class is used to ensure that the io_context member is
79 // initialised prior to the basic_socket_streambuf's basic_socket base class.
80 class socket_streambuf_io_context
81 {
82 protected:
socket_streambuf_io_context(io_context * ctx)83   socket_streambuf_io_context(io_context* ctx)
84     : default_io_context_(ctx)
85   {
86   }
87 
88   shared_ptr<io_context> default_io_context_;
89 };
90 
91 // A separate base class is used to ensure that the dynamically allocated
92 // buffers are constructed prior to the basic_socket_streambuf's basic_socket
93 // base class. This makes moving the socket is the last potentially throwing
94 // step in the streambuf's move constructor, giving the constructor a strong
95 // exception safety guarantee.
96 class socket_streambuf_buffers
97 {
98 protected:
socket_streambuf_buffers()99   socket_streambuf_buffers()
100     : get_buffer_(buffer_size),
101       put_buffer_(buffer_size)
102   {
103   }
104 
105   enum { buffer_size = 512 };
106   std::vector<char> get_buffer_;
107   std::vector<char> put_buffer_;
108 };
109 
110 } // namespace detail
111 
112 #if !defined(BOOST_ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL)
113 #define BOOST_ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL
114 
115 // Forward declaration with defaulted arguments.
116 template <typename Protocol,
117 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) \
118   && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
119     typename Clock = boost::posix_time::ptime,
120     typename WaitTraits = time_traits<Clock> >
121 #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
122       // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
123     typename Clock = chrono::steady_clock,
124     typename WaitTraits = wait_traits<Clock> >
125 #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
126        // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
127 class basic_socket_streambuf;
128 
129 #endif // !defined(BOOST_ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL)
130 
131 /// Iostream streambuf for a socket.
132 #if defined(GENERATING_DOCUMENTATION)
133 template <typename Protocol,
134     typename Clock = chrono::steady_clock,
135     typename WaitTraits = wait_traits<Clock> >
136 #else // defined(GENERATING_DOCUMENTATION)
137 template <typename Protocol, typename Clock, typename WaitTraits>
138 #endif // defined(GENERATING_DOCUMENTATION)
139 class basic_socket_streambuf
140   : public std::streambuf,
141     private detail::socket_streambuf_io_context,
142     private detail::socket_streambuf_buffers,
143 #if defined(BOOST_ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
144     private basic_socket<Protocol>
145 #else // defined(BOOST_ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
146     public basic_socket<Protocol>
147 #endif // defined(BOOST_ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
148 {
149 private:
150   // These typedefs are intended keep this class's implementation independent
151   // of whether it's using Boost.DateClock, Boost.Chrono or std::chrono.
152 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) \
153   && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
154   typedef WaitTraits traits_helper;
155 #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
156       // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
157   typedef detail::chrono_time_traits<Clock, WaitTraits> traits_helper;
158 #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
159        // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
160 
161 public:
162   /// The protocol type.
163   typedef Protocol protocol_type;
164 
165   /// The endpoint type.
166   typedef typename Protocol::endpoint endpoint_type;
167 
168   /// The clock type.
169   typedef Clock clock_type;
170 
171 #if defined(GENERATING_DOCUMENTATION)
172   /// (Deprecated: Use time_point.) The time type.
173   typedef typename WaitTraits::time_type time_type;
174 
175   /// The time type.
176   typedef typename WaitTraits::time_point time_point;
177 
178   /// (Deprecated: Use duration.) The duration type.
179   typedef typename WaitTraits::duration_type duration_type;
180 
181   /// The duration type.
182   typedef typename WaitTraits::duration duration;
183 #else
184 # if !defined(BOOST_ASIO_NO_DEPRECATED)
185   typedef typename traits_helper::time_type time_type;
186   typedef typename traits_helper::duration_type duration_type;
187 # endif // !defined(BOOST_ASIO_NO_DEPRECATED)
188   typedef typename traits_helper::time_type time_point;
189   typedef typename traits_helper::duration_type duration;
190 #endif
191 
192   /// Construct a basic_socket_streambuf without establishing a connection.
basic_socket_streambuf()193   basic_socket_streambuf()
194     : detail::socket_streambuf_io_context(new io_context),
195       basic_socket<Protocol>(*default_io_context_),
196       expiry_time_(max_expiry_time())
197   {
198     init_buffers();
199   }
200 
201 #if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
202   /// Construct a basic_socket_streambuf from the supplied socket.
basic_socket_streambuf(basic_stream_socket<protocol_type> s)203   explicit basic_socket_streambuf(basic_stream_socket<protocol_type> s)
204     : detail::socket_streambuf_io_context(0),
205       basic_socket<Protocol>(std::move(s)),
206       expiry_time_(max_expiry_time())
207   {
208     init_buffers();
209   }
210 
211   /// Move-construct a basic_socket_streambuf from another.
basic_socket_streambuf(basic_socket_streambuf && other)212   basic_socket_streambuf(basic_socket_streambuf&& other)
213     : detail::socket_streambuf_io_context(other),
214       basic_socket<Protocol>(std::move(other.socket())),
215       ec_(other.ec_),
216       expiry_time_(other.expiry_time_)
217   {
218     get_buffer_.swap(other.get_buffer_);
219     put_buffer_.swap(other.put_buffer_);
220     setg(other.eback(), other.gptr(), other.egptr());
221     setp(other.pptr(), other.epptr());
222     other.ec_ = boost::system::error_code();
223     other.expiry_time_ = max_expiry_time();
224     other.init_buffers();
225   }
226 
227   /// Move-assign a basic_socket_streambuf from another.
operator =(basic_socket_streambuf && other)228   basic_socket_streambuf& operator=(basic_socket_streambuf&& other)
229   {
230     this->close();
231     socket() = std::move(other.socket());
232     detail::socket_streambuf_io_context::operator=(other);
233     ec_ = other.ec_;
234     expiry_time_ = other.expiry_time_;
235     get_buffer_.swap(other.get_buffer_);
236     put_buffer_.swap(other.put_buffer_);
237     setg(other.eback(), other.gptr(), other.egptr());
238     setp(other.pptr(), other.epptr());
239     other.ec_ = boost::system::error_code();
240     other.expiry_time_ = max_expiry_time();
241     other.put_buffer_.resize(buffer_size);
242     other.init_buffers();
243     return *this;
244   }
245 #endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
246 
247   /// Destructor flushes buffered data.
~basic_socket_streambuf()248   virtual ~basic_socket_streambuf()
249   {
250     if (pptr() != pbase())
251       overflow(traits_type::eof());
252   }
253 
254   /// Establish a connection.
255   /**
256    * This function establishes a connection to the specified endpoint.
257    *
258    * @return \c this if a connection was successfully established, a null
259    * pointer otherwise.
260    */
connect(const endpoint_type & endpoint)261   basic_socket_streambuf* connect(const endpoint_type& endpoint)
262   {
263     init_buffers();
264     ec_ = boost::system::error_code();
265     this->connect_to_endpoints(&endpoint, &endpoint + 1);
266     return !ec_ ? this : 0;
267   }
268 
269 #if defined(GENERATING_DOCUMENTATION)
270   /// Establish a connection.
271   /**
272    * This function automatically establishes a connection based on the supplied
273    * resolver query parameters. The arguments are used to construct a resolver
274    * query object.
275    *
276    * @return \c this if a connection was successfully established, a null
277    * pointer otherwise.
278    */
279   template <typename T1, ..., typename TN>
280   basic_socket_streambuf* connect(T1 t1, ..., TN tn);
281 #elif defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
282   template <typename... T>
connect(T...x)283   basic_socket_streambuf* connect(T... x)
284   {
285     init_buffers();
286     typedef typename Protocol::resolver resolver_type;
287     resolver_type resolver(socket().get_executor());
288     connect_to_endpoints(resolver.resolve(x..., ec_));
289     return !ec_ ? this : 0;
290   }
291 #else
BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_CONNECT_DEF)292   BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_CONNECT_DEF)
293 #endif
294 
295   /// Close the connection.
296   /**
297    * @return \c this if a connection was successfully established, a null
298    * pointer otherwise.
299    */
300   basic_socket_streambuf* close()
301   {
302     sync();
303     socket().close(ec_);
304     if (!ec_)
305       init_buffers();
306     return !ec_ ? this : 0;
307   }
308 
309   /// Get a reference to the underlying socket.
socket()310   basic_socket<Protocol>& socket()
311   {
312     return *this;
313   }
314 
315   /// Get the last error associated with the stream buffer.
316   /**
317    * @return An \c error_code corresponding to the last error from the stream
318    * buffer.
319    */
error() const320   const boost::system::error_code& error() const
321   {
322     return ec_;
323   }
324 
325 #if !defined(BOOST_ASIO_NO_DEPRECATED)
326   /// (Deprecated: Use error().) Get the last error associated with the stream
327   /// buffer.
328   /**
329    * @return An \c error_code corresponding to the last error from the stream
330    * buffer.
331    */
puberror() const332   const boost::system::error_code& puberror() const
333   {
334     return error();
335   }
336 
337   /// (Deprecated: Use expiry().) Get the stream buffer's expiry time as an
338   /// absolute time.
339   /**
340    * @return An absolute time value representing the stream buffer's expiry
341    * time.
342    */
expires_at() const343   time_point expires_at() const
344   {
345     return expiry_time_;
346   }
347 #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
348 
349   /// Get the stream buffer's expiry time as an absolute time.
350   /**
351    * @return An absolute time value representing the stream buffer's expiry
352    * time.
353    */
expiry() const354   time_point expiry() const
355   {
356     return expiry_time_;
357   }
358 
359   /// Set the stream buffer's expiry time as an absolute time.
360   /**
361    * This function sets the expiry time associated with the stream. Stream
362    * operations performed after this time (where the operations cannot be
363    * completed using the internal buffers) will fail with the error
364    * boost::asio::error::operation_aborted.
365    *
366    * @param expiry_time The expiry time to be used for the stream.
367    */
expires_at(const time_point & expiry_time)368   void expires_at(const time_point& expiry_time)
369   {
370     expiry_time_ = expiry_time;
371   }
372 
373   /// Set the stream buffer's expiry time relative to now.
374   /**
375    * This function sets the expiry time associated with the stream. Stream
376    * operations performed after this time (where the operations cannot be
377    * completed using the internal buffers) will fail with the error
378    * boost::asio::error::operation_aborted.
379    *
380    * @param expiry_time The expiry time to be used for the timer.
381    */
expires_after(const duration & expiry_time)382   void expires_after(const duration& expiry_time)
383   {
384     expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time);
385   }
386 
387 #if !defined(BOOST_ASIO_NO_DEPRECATED)
388   /// (Deprecated: Use expiry().) Get the stream buffer's expiry time relative
389   /// to now.
390   /**
391    * @return A relative time value representing the stream buffer's expiry time.
392    */
expires_from_now() const393   duration expires_from_now() const
394   {
395     return traits_helper::subtract(expires_at(), traits_helper::now());
396   }
397 
398   /// (Deprecated: Use expires_after().) Set the stream buffer's expiry time
399   /// relative to now.
400   /**
401    * This function sets the expiry time associated with the stream. Stream
402    * operations performed after this time (where the operations cannot be
403    * completed using the internal buffers) will fail with the error
404    * boost::asio::error::operation_aborted.
405    *
406    * @param expiry_time The expiry time to be used for the timer.
407    */
expires_from_now(const duration & expiry_time)408   void expires_from_now(const duration& expiry_time)
409   {
410     expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time);
411   }
412 #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
413 
414 protected:
underflow()415   int_type underflow()
416   {
417 #if defined(BOOST_ASIO_WINDOWS_RUNTIME)
418     ec_ = boost::asio::error::operation_not_supported;
419     return traits_type::eof();
420 #else // defined(BOOST_ASIO_WINDOWS_RUNTIME)
421     if (gptr() != egptr())
422       return traits_type::eof();
423 
424     for (;;)
425     {
426       // Check if we are past the expiry time.
427       if (traits_helper::less_than(expiry_time_, traits_helper::now()))
428       {
429         ec_ = boost::asio::error::timed_out;
430         return traits_type::eof();
431       }
432 
433       // Try to complete the operation without blocking.
434       if (!socket().native_non_blocking())
435         socket().native_non_blocking(true, ec_);
436       detail::buffer_sequence_adapter<mutable_buffer, mutable_buffer>
437         bufs(boost::asio::buffer(get_buffer_) + putback_max);
438       detail::signed_size_type bytes = detail::socket_ops::recv(
439           socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_);
440 
441       // Check if operation succeeded.
442       if (bytes > 0)
443       {
444         setg(&get_buffer_[0], &get_buffer_[0] + putback_max,
445             &get_buffer_[0] + putback_max + bytes);
446         return traits_type::to_int_type(*gptr());
447       }
448 
449       // Check for EOF.
450       if (bytes == 0)
451       {
452         ec_ = boost::asio::error::eof;
453         return traits_type::eof();
454       }
455 
456       // Operation failed.
457       if (ec_ != boost::asio::error::would_block
458           && ec_ != boost::asio::error::try_again)
459         return traits_type::eof();
460 
461       // Wait for socket to become ready.
462       if (detail::socket_ops::poll_read(
463             socket().native_handle(), 0, timeout(), ec_) < 0)
464         return traits_type::eof();
465     }
466 #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME)
467   }
468 
overflow(int_type c)469   int_type overflow(int_type c)
470   {
471 #if defined(BOOST_ASIO_WINDOWS_RUNTIME)
472     ec_ = boost::asio::error::operation_not_supported;
473     return traits_type::eof();
474 #else // defined(BOOST_ASIO_WINDOWS_RUNTIME)
475     char_type ch = traits_type::to_char_type(c);
476 
477     // Determine what needs to be sent.
478     const_buffer output_buffer;
479     if (put_buffer_.empty())
480     {
481       if (traits_type::eq_int_type(c, traits_type::eof()))
482         return traits_type::not_eof(c); // Nothing to do.
483       output_buffer = boost::asio::buffer(&ch, sizeof(char_type));
484     }
485     else
486     {
487       output_buffer = boost::asio::buffer(pbase(),
488           (pptr() - pbase()) * sizeof(char_type));
489     }
490 
491     while (output_buffer.size() > 0)
492     {
493       // Check if we are past the expiry time.
494       if (traits_helper::less_than(expiry_time_, traits_helper::now()))
495       {
496         ec_ = boost::asio::error::timed_out;
497         return traits_type::eof();
498       }
499 
500       // Try to complete the operation without blocking.
501       if (!socket().native_non_blocking())
502         socket().native_non_blocking(true, ec_);
503       detail::buffer_sequence_adapter<
504         const_buffer, const_buffer> bufs(output_buffer);
505       detail::signed_size_type bytes = detail::socket_ops::send(
506           socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_);
507 
508       // Check if operation succeeded.
509       if (bytes > 0)
510       {
511         output_buffer += static_cast<std::size_t>(bytes);
512         continue;
513       }
514 
515       // Operation failed.
516       if (ec_ != boost::asio::error::would_block
517           && ec_ != boost::asio::error::try_again)
518         return traits_type::eof();
519 
520       // Wait for socket to become ready.
521       if (detail::socket_ops::poll_write(
522             socket().native_handle(), 0, timeout(), ec_) < 0)
523         return traits_type::eof();
524     }
525 
526     if (!put_buffer_.empty())
527     {
528       setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
529 
530       // If the new character is eof then our work here is done.
531       if (traits_type::eq_int_type(c, traits_type::eof()))
532         return traits_type::not_eof(c);
533 
534       // Add the new character to the output buffer.
535       *pptr() = ch;
536       pbump(1);
537     }
538 
539     return c;
540 #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME)
541   }
542 
sync()543   int sync()
544   {
545     return overflow(traits_type::eof());
546   }
547 
setbuf(char_type * s,std::streamsize n)548   std::streambuf* setbuf(char_type* s, std::streamsize n)
549   {
550     if (pptr() == pbase() && s == 0 && n == 0)
551     {
552       put_buffer_.clear();
553       setp(0, 0);
554       sync();
555       return this;
556     }
557 
558     return 0;
559   }
560 
561 private:
562   // Disallow copying and assignment.
563   basic_socket_streambuf(const basic_socket_streambuf&) BOOST_ASIO_DELETED;
564   basic_socket_streambuf& operator=(
565       const basic_socket_streambuf&) BOOST_ASIO_DELETED;
566 
init_buffers()567   void init_buffers()
568   {
569     setg(&get_buffer_[0],
570         &get_buffer_[0] + putback_max,
571         &get_buffer_[0] + putback_max);
572 
573     if (put_buffer_.empty())
574       setp(0, 0);
575     else
576       setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
577   }
578 
timeout() const579   int timeout() const
580   {
581     int64_t msec = traits_helper::to_posix_duration(
582         traits_helper::subtract(expiry_time_,
583           traits_helper::now())).total_milliseconds();
584     if (msec > (std::numeric_limits<int>::max)())
585       msec = (std::numeric_limits<int>::max)();
586     else if (msec < 0)
587       msec = 0;
588     return static_cast<int>(msec);
589   }
590 
591   template <typename EndpointSequence>
connect_to_endpoints(const EndpointSequence & endpoints)592   void connect_to_endpoints(const EndpointSequence& endpoints)
593   {
594     this->connect_to_endpoints(endpoints.begin(), endpoints.end());
595   }
596 
597   template <typename EndpointIterator>
connect_to_endpoints(EndpointIterator begin,EndpointIterator end)598   void connect_to_endpoints(EndpointIterator begin, EndpointIterator end)
599   {
600 #if defined(BOOST_ASIO_WINDOWS_RUNTIME)
601     ec_ = boost::asio::error::operation_not_supported;
602 #else // defined(BOOST_ASIO_WINDOWS_RUNTIME)
603     if (ec_)
604       return;
605 
606     ec_ = boost::asio::error::not_found;
607     for (EndpointIterator i = begin; i != end; ++i)
608     {
609       // Check if we are past the expiry time.
610       if (traits_helper::less_than(expiry_time_, traits_helper::now()))
611       {
612         ec_ = boost::asio::error::timed_out;
613         return;
614       }
615 
616       // Close and reopen the socket.
617       typename Protocol::endpoint ep(*i);
618       socket().close(ec_);
619       socket().open(ep.protocol(), ec_);
620       if (ec_)
621         continue;
622 
623       // Try to complete the operation without blocking.
624       if (!socket().native_non_blocking())
625         socket().native_non_blocking(true, ec_);
626       detail::socket_ops::connect(socket().native_handle(),
627           ep.data(), ep.size(), ec_);
628 
629       // Check if operation succeeded.
630       if (!ec_)
631         return;
632 
633       // Operation failed.
634       if (ec_ != boost::asio::error::in_progress
635           && ec_ != boost::asio::error::would_block)
636         continue;
637 
638       // Wait for socket to become ready.
639       if (detail::socket_ops::poll_connect(
640             socket().native_handle(), timeout(), ec_) < 0)
641         continue;
642 
643       // Get the error code from the connect operation.
644       int connect_error = 0;
645       size_t connect_error_len = sizeof(connect_error);
646       if (detail::socket_ops::getsockopt(socket().native_handle(), 0,
647             SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec_)
648           == detail::socket_error_retval)
649         return;
650 
651       // Check the result of the connect operation.
652       ec_ = boost::system::error_code(connect_error,
653           boost::asio::error::get_system_category());
654       if (!ec_)
655         return;
656     }
657 #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME)
658   }
659 
660   // Helper function to get the maximum expiry time.
max_expiry_time()661   static time_point max_expiry_time()
662   {
663 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) \
664   && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
665     return boost::posix_time::pos_infin;
666 #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
667       // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
668     return (time_point::max)();
669 #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
670        // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
671   }
672 
673   enum { putback_max = 8 };
674   boost::system::error_code ec_;
675   time_point expiry_time_;
676 };
677 
678 } // namespace asio
679 } // namespace boost
680 
681 #include <boost/asio/detail/pop_options.hpp>
682 
683 #if !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
684 # undef BOOST_ASIO_PRIVATE_CONNECT_DEF
685 #endif // !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
686 
687 #endif // !defined(BOOST_ASIO_NO_IOSTREAM)
688 
689 #endif // BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP
690