Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 08:29:55

0001 //
0002 // basic_socket_streambuf.hpp
0003 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
0004 //
0005 // Copyright (c) 2003-2025 Christopher M. Kohlhoff (chris at kohlhoff dot com)
0006 //
0007 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0009 //
0010 
0011 #ifndef BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP
0012 #define BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP
0013 
0014 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
0015 # pragma once
0016 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
0017 
0018 #include <boost/asio/detail/config.hpp>
0019 
0020 #if !defined(BOOST_ASIO_NO_IOSTREAM)
0021 
0022 #include <streambuf>
0023 #include <vector>
0024 #include <boost/asio/basic_socket.hpp>
0025 #include <boost/asio/basic_stream_socket.hpp>
0026 #include <boost/asio/detail/buffer_sequence_adapter.hpp>
0027 #include <boost/asio/detail/memory.hpp>
0028 #include <boost/asio/detail/throw_error.hpp>
0029 #include <boost/asio/io_context.hpp>
0030 #include <boost/asio/steady_timer.hpp>
0031 
0032 #include <boost/asio/detail/push_options.hpp>
0033 
0034 namespace boost {
0035 namespace asio {
0036 namespace detail {
0037 
0038 // A separate base class is used to ensure that the io_context member is
0039 // initialised prior to the basic_socket_streambuf's basic_socket base class.
0040 class socket_streambuf_io_context
0041 {
0042 protected:
0043   socket_streambuf_io_context(io_context* ctx)
0044     : default_io_context_(ctx)
0045   {
0046   }
0047 
0048   shared_ptr<io_context> default_io_context_;
0049 };
0050 
0051 // A separate base class is used to ensure that the dynamically allocated
0052 // buffers are constructed prior to the basic_socket_streambuf's basic_socket
0053 // base class. This makes moving the socket is the last potentially throwing
0054 // step in the streambuf's move constructor, giving the constructor a strong
0055 // exception safety guarantee.
0056 class socket_streambuf_buffers
0057 {
0058 protected:
0059   socket_streambuf_buffers()
0060     : get_buffer_(buffer_size),
0061       put_buffer_(buffer_size)
0062   {
0063   }
0064 
0065   enum { buffer_size = 512 };
0066   std::vector<char> get_buffer_;
0067   std::vector<char> put_buffer_;
0068 };
0069 
0070 } // namespace detail
0071 
0072 #if !defined(BOOST_ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL)
0073 #define BOOST_ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL
0074 
0075 // Forward declaration with defaulted arguments.
0076 template <typename Protocol,
0077     typename Clock = chrono::steady_clock,
0078     typename WaitTraits = wait_traits<Clock>>
0079 class basic_socket_streambuf;
0080 
0081 #endif // !defined(BOOST_ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL)
0082 
0083 /// Iostream streambuf for a socket.
0084 #if defined(GENERATING_DOCUMENTATION)
0085 template <typename Protocol,
0086     typename Clock = chrono::steady_clock,
0087     typename WaitTraits = wait_traits<Clock>>
0088 #else // defined(GENERATING_DOCUMENTATION)
0089 template <typename Protocol, typename Clock, typename WaitTraits>
0090 #endif // defined(GENERATING_DOCUMENTATION)
0091 class basic_socket_streambuf
0092   : public std::streambuf,
0093     private detail::socket_streambuf_io_context,
0094     private detail::socket_streambuf_buffers,
0095     private basic_socket<Protocol>
0096 {
0097 private:
0098   typedef detail::chrono_time_traits<Clock, WaitTraits> traits_helper;
0099 
0100 public:
0101   /// The protocol type.
0102   typedef Protocol protocol_type;
0103 
0104   /// The endpoint type.
0105   typedef typename Protocol::endpoint endpoint_type;
0106 
0107   /// The clock type.
0108   typedef Clock clock_type;
0109 
0110 #if defined(GENERATING_DOCUMENTATION)
0111   /// The time type.
0112   typedef typename WaitTraits::time_point time_point;
0113 
0114   /// The duration type.
0115   typedef typename WaitTraits::duration duration;
0116 #else
0117   typedef typename traits_helper::time_type time_point;
0118   typedef typename traits_helper::duration_type duration;
0119 #endif
0120 
0121   /// Construct a basic_socket_streambuf without establishing a connection.
0122   basic_socket_streambuf()
0123     : detail::socket_streambuf_io_context(new io_context),
0124       basic_socket<Protocol>(*default_io_context_),
0125       expiry_time_(max_expiry_time())
0126   {
0127     init_buffers();
0128   }
0129 
0130   /// Construct a basic_socket_streambuf from the supplied socket.
0131   explicit basic_socket_streambuf(basic_stream_socket<protocol_type> s)
0132     : detail::socket_streambuf_io_context(0),
0133       basic_socket<Protocol>(std::move(s)),
0134       expiry_time_(max_expiry_time())
0135   {
0136     init_buffers();
0137   }
0138 
0139   /// Move-construct a basic_socket_streambuf from another.
0140   basic_socket_streambuf(basic_socket_streambuf&& other)
0141     : detail::socket_streambuf_io_context(other),
0142       basic_socket<Protocol>(std::move(other.socket())),
0143       ec_(other.ec_),
0144       expiry_time_(other.expiry_time_)
0145   {
0146     get_buffer_.swap(other.get_buffer_);
0147     put_buffer_.swap(other.put_buffer_);
0148     setg(other.eback(), other.gptr(), other.egptr());
0149     setp(other.pptr(), other.epptr());
0150     other.ec_ = boost::system::error_code();
0151     other.expiry_time_ = max_expiry_time();
0152     other.init_buffers();
0153   }
0154 
0155   /// Move-assign a basic_socket_streambuf from another.
0156   basic_socket_streambuf& operator=(basic_socket_streambuf&& other)
0157   {
0158     this->close();
0159     socket() = std::move(other.socket());
0160     detail::socket_streambuf_io_context::operator=(other);
0161     ec_ = other.ec_;
0162     expiry_time_ = other.expiry_time_;
0163     get_buffer_.swap(other.get_buffer_);
0164     put_buffer_.swap(other.put_buffer_);
0165     setg(other.eback(), other.gptr(), other.egptr());
0166     setp(other.pptr(), other.epptr());
0167     other.ec_ = boost::system::error_code();
0168     other.expiry_time_ = max_expiry_time();
0169     other.put_buffer_.resize(buffer_size);
0170     other.init_buffers();
0171     return *this;
0172   }
0173 
0174   /// Destructor flushes buffered data.
0175   virtual ~basic_socket_streambuf()
0176   {
0177     if (pptr() != pbase())
0178       overflow(traits_type::eof());
0179   }
0180 
0181   /// Establish a connection.
0182   /**
0183    * This function establishes a connection to the specified endpoint.
0184    *
0185    * @return \c this if a connection was successfully established, a null
0186    * pointer otherwise.
0187    */
0188   basic_socket_streambuf* connect(const endpoint_type& endpoint)
0189   {
0190     init_buffers();
0191     ec_ = boost::system::error_code();
0192     this->connect_to_endpoints(&endpoint, &endpoint + 1);
0193     return !ec_ ? this : 0;
0194   }
0195 
0196   /// Establish a connection.
0197   /**
0198    * This function automatically establishes a connection based on the supplied
0199    * resolver query parameters. The arguments are used to construct a resolver
0200    * query object.
0201    *
0202    * @return \c this if a connection was successfully established, a null
0203    * pointer otherwise.
0204    */
0205   template <typename... T>
0206   basic_socket_streambuf* connect(T... x)
0207   {
0208     init_buffers();
0209     typedef typename Protocol::resolver resolver_type;
0210     resolver_type resolver(socket().get_executor());
0211     connect_to_endpoints(resolver.resolve(x..., ec_));
0212     return !ec_ ? this : 0;
0213   }
0214 
0215   /// Close the connection.
0216   /**
0217    * @return \c this if a connection was successfully established, a null
0218    * pointer otherwise.
0219    */
0220   basic_socket_streambuf* close()
0221   {
0222     sync();
0223     socket().close(ec_);
0224     if (!ec_)
0225       init_buffers();
0226     return !ec_ ? this : 0;
0227   }
0228 
0229   /// Get a reference to the underlying socket.
0230   basic_socket<Protocol>& socket()
0231   {
0232     return *this;
0233   }
0234 
0235   /// Get the last error associated with the stream buffer.
0236   /**
0237    * @return An \c error_code corresponding to the last error from the stream
0238    * buffer.
0239    */
0240   const boost::system::error_code& error() const
0241   {
0242     return ec_;
0243   }
0244 
0245   /// Get the stream buffer's expiry time as an absolute time.
0246   /**
0247    * @return An absolute time value representing the stream buffer's expiry
0248    * time.
0249    */
0250   time_point expiry() const
0251   {
0252     return expiry_time_;
0253   }
0254 
0255   /// Set the stream buffer's expiry time as an absolute time.
0256   /**
0257    * This function sets the expiry time associated with the stream. Stream
0258    * operations performed after this time (where the operations cannot be
0259    * completed using the internal buffers) will fail with the error
0260    * boost::asio::error::operation_aborted.
0261    *
0262    * @param expiry_time The expiry time to be used for the stream.
0263    */
0264   void expires_at(const time_point& expiry_time)
0265   {
0266     expiry_time_ = expiry_time;
0267   }
0268 
0269   /// Set the stream buffer's expiry time relative to now.
0270   /**
0271    * This function sets the expiry time associated with the stream. Stream
0272    * operations performed after this time (where the operations cannot be
0273    * completed using the internal buffers) will fail with the error
0274    * boost::asio::error::operation_aborted.
0275    *
0276    * @param expiry_time The expiry time to be used for the timer.
0277    */
0278   void expires_after(const duration& expiry_time)
0279   {
0280     expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time);
0281   }
0282 
0283 protected:
0284   int_type underflow()
0285   {
0286 #if defined(BOOST_ASIO_WINDOWS_RUNTIME)
0287     ec_ = boost::asio::error::operation_not_supported;
0288     return traits_type::eof();
0289 #else // defined(BOOST_ASIO_WINDOWS_RUNTIME)
0290     if (gptr() != egptr())
0291       return traits_type::eof();
0292 
0293     for (;;)
0294     {
0295       // Check if we are past the expiry time.
0296       if (traits_helper::less_than(expiry_time_, traits_helper::now()))
0297       {
0298         ec_ = boost::asio::error::timed_out;
0299         return traits_type::eof();
0300       }
0301 
0302       // Try to complete the operation without blocking.
0303       if (!socket().native_non_blocking())
0304         socket().native_non_blocking(true, ec_);
0305       detail::buffer_sequence_adapter<mutable_buffer, mutable_buffer>
0306         bufs(boost::asio::buffer(get_buffer_) + putback_max);
0307       detail::signed_size_type bytes = detail::socket_ops::recv(
0308           socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_);
0309 
0310       // Check if operation succeeded.
0311       if (bytes > 0)
0312       {
0313         setg(&get_buffer_[0], &get_buffer_[0] + putback_max,
0314             &get_buffer_[0] + putback_max + bytes);
0315         return traits_type::to_int_type(*gptr());
0316       }
0317 
0318       // Check for EOF.
0319       if (bytes == 0)
0320       {
0321         ec_ = boost::asio::error::eof;
0322         return traits_type::eof();
0323       }
0324 
0325       // Operation failed.
0326       if (ec_ != boost::asio::error::would_block
0327           && ec_ != boost::asio::error::try_again)
0328         return traits_type::eof();
0329 
0330       // Wait for socket to become ready.
0331       if (detail::socket_ops::poll_read(
0332             socket().native_handle(), 0, timeout(), ec_) < 0)
0333         return traits_type::eof();
0334     }
0335 #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME)
0336   }
0337 
0338   int_type overflow(int_type c)
0339   {
0340 #if defined(BOOST_ASIO_WINDOWS_RUNTIME)
0341     ec_ = boost::asio::error::operation_not_supported;
0342     return traits_type::eof();
0343 #else // defined(BOOST_ASIO_WINDOWS_RUNTIME)
0344     char_type ch = traits_type::to_char_type(c);
0345 
0346     // Determine what needs to be sent.
0347     const_buffer output_buffer;
0348     if (put_buffer_.empty())
0349     {
0350       if (traits_type::eq_int_type(c, traits_type::eof()))
0351         return traits_type::not_eof(c); // Nothing to do.
0352       output_buffer = boost::asio::buffer(&ch, sizeof(char_type));
0353     }
0354     else
0355     {
0356       output_buffer = boost::asio::buffer(pbase(),
0357           (pptr() - pbase()) * sizeof(char_type));
0358     }
0359 
0360     while (output_buffer.size() > 0)
0361     {
0362       // Check if we are past the expiry time.
0363       if (traits_helper::less_than(expiry_time_, traits_helper::now()))
0364       {
0365         ec_ = boost::asio::error::timed_out;
0366         return traits_type::eof();
0367       }
0368 
0369       // Try to complete the operation without blocking.
0370       if (!socket().native_non_blocking())
0371         socket().native_non_blocking(true, ec_);
0372       detail::buffer_sequence_adapter<
0373         const_buffer, const_buffer> bufs(output_buffer);
0374       detail::signed_size_type bytes = detail::socket_ops::send(
0375           socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_);
0376 
0377       // Check if operation succeeded.
0378       if (bytes > 0)
0379       {
0380         output_buffer += static_cast<std::size_t>(bytes);
0381         continue;
0382       }
0383 
0384       // Operation failed.
0385       if (ec_ != boost::asio::error::would_block
0386           && ec_ != boost::asio::error::try_again)
0387         return traits_type::eof();
0388 
0389       // Wait for socket to become ready.
0390       if (detail::socket_ops::poll_write(
0391             socket().native_handle(), 0, timeout(), ec_) < 0)
0392         return traits_type::eof();
0393     }
0394 
0395     if (!put_buffer_.empty())
0396     {
0397       setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
0398 
0399       // If the new character is eof then our work here is done.
0400       if (traits_type::eq_int_type(c, traits_type::eof()))
0401         return traits_type::not_eof(c);
0402 
0403       // Add the new character to the output buffer.
0404       *pptr() = ch;
0405       pbump(1);
0406     }
0407 
0408     return c;
0409 #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME)
0410   }
0411 
0412   int sync()
0413   {
0414     return overflow(traits_type::eof());
0415   }
0416 
0417   std::streambuf* setbuf(char_type* s, std::streamsize n)
0418   {
0419     if (pptr() == pbase() && s == 0 && n == 0)
0420     {
0421       put_buffer_.clear();
0422       setp(0, 0);
0423       sync();
0424       return this;
0425     }
0426 
0427     return 0;
0428   }
0429 
0430 private:
0431   // Disallow copying and assignment.
0432   basic_socket_streambuf(const basic_socket_streambuf&) = delete;
0433   basic_socket_streambuf& operator=(
0434       const basic_socket_streambuf&) = delete;
0435 
0436   void init_buffers()
0437   {
0438     setg(&get_buffer_[0],
0439         &get_buffer_[0] + putback_max,
0440         &get_buffer_[0] + putback_max);
0441 
0442     if (put_buffer_.empty())
0443       setp(0, 0);
0444     else
0445       setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
0446   }
0447 
0448   int timeout() const
0449   {
0450     int64_t msec = traits_helper::to_posix_duration(
0451         traits_helper::subtract(expiry_time_,
0452           traits_helper::now())).total_milliseconds();
0453     if (msec > (std::numeric_limits<int>::max)())
0454       msec = (std::numeric_limits<int>::max)();
0455     else if (msec < 0)
0456       msec = 0;
0457     return static_cast<int>(msec);
0458   }
0459 
0460   template <typename EndpointSequence>
0461   void connect_to_endpoints(const EndpointSequence& endpoints)
0462   {
0463     this->connect_to_endpoints(endpoints.begin(), endpoints.end());
0464   }
0465 
0466   template <typename EndpointIterator>
0467   void connect_to_endpoints(EndpointIterator begin, EndpointIterator end)
0468   {
0469 #if defined(BOOST_ASIO_WINDOWS_RUNTIME)
0470     ec_ = boost::asio::error::operation_not_supported;
0471 #else // defined(BOOST_ASIO_WINDOWS_RUNTIME)
0472     if (ec_)
0473       return;
0474 
0475     ec_ = boost::asio::error::not_found;
0476     for (EndpointIterator i = begin; i != end; ++i)
0477     {
0478       // Check if we are past the expiry time.
0479       if (traits_helper::less_than(expiry_time_, traits_helper::now()))
0480       {
0481         ec_ = boost::asio::error::timed_out;
0482         return;
0483       }
0484 
0485       // Close and reopen the socket.
0486       typename Protocol::endpoint ep(*i);
0487       socket().close(ec_);
0488       socket().open(ep.protocol(), ec_);
0489       if (ec_)
0490         continue;
0491 
0492       // Try to complete the operation without blocking.
0493       if (!socket().native_non_blocking())
0494         socket().native_non_blocking(true, ec_);
0495       detail::socket_ops::connect(socket().native_handle(),
0496           ep.data(), ep.size(), ec_);
0497 
0498       // Check if operation succeeded.
0499       if (!ec_)
0500         return;
0501 
0502       // Operation failed.
0503       if (ec_ != boost::asio::error::in_progress
0504           && ec_ != boost::asio::error::would_block)
0505         continue;
0506 
0507       // Wait for socket to become ready.
0508       if (detail::socket_ops::poll_connect(
0509             socket().native_handle(), timeout(), ec_) < 0)
0510         continue;
0511 
0512       // Get the error code from the connect operation.
0513       int connect_error = 0;
0514       size_t connect_error_len = sizeof(connect_error);
0515       if (detail::socket_ops::getsockopt(socket().native_handle(), 0,
0516             SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec_)
0517           == detail::socket_error_retval)
0518         return;
0519 
0520       // Check the result of the connect operation.
0521       ec_ = boost::system::error_code(connect_error,
0522           boost::asio::error::get_system_category());
0523       if (!ec_)
0524         return;
0525     }
0526 #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME)
0527   }
0528 
0529   // Helper function to get the maximum expiry time.
0530   static time_point max_expiry_time()
0531   {
0532     return (time_point::max)();
0533   }
0534 
0535   enum { putback_max = 8 };
0536   boost::system::error_code ec_;
0537   time_point expiry_time_;
0538 };
0539 
0540 } // namespace asio
0541 } // namespace boost
0542 
0543 #include <boost/asio/detail/pop_options.hpp>
0544 
0545 #endif // !defined(BOOST_ASIO_NO_IOSTREAM)
0546 
0547 #endif // BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP