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