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
42namespace boost {
43namespace asio {
44namespace 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.
48class socket_streambuf_io_context
49{
50protected:
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.
64class socket_streambuf_buffers
65{
66protected:
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.
84template <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)
95class 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)
101template <typename Protocol,
102 typename Clock = chrono::steady_clock,
103 typename WaitTraits = wait_traits<Clock>>
104#else // defined(GENERATING_DOCUMENTATION)
105template <typename Protocol, typename Clock, typename WaitTraits>
106#endif // defined(GENERATING_DOCUMENTATION)
107class 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{
117private:
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
129public:
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
373protected:
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
520private:
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

source code of boost/libs/asio/include/boost/asio/basic_socket_streambuf.hpp