File indexing completed on 2025-09-15 08:29:55
0001
0002
0003
0004
0005
0006
0007
0008
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
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
0039
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
0052
0053
0054
0055
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 }
0071
0072 #if !defined(BOOST_ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL)
0073 #define BOOST_ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL
0074
0075
0076 template <typename Protocol,
0077 typename Clock = chrono::steady_clock,
0078 typename WaitTraits = wait_traits<Clock>>
0079 class basic_socket_streambuf;
0080
0081 #endif
0082
0083
0084 #if defined(GENERATING_DOCUMENTATION)
0085 template <typename Protocol,
0086 typename Clock = chrono::steady_clock,
0087 typename WaitTraits = wait_traits<Clock>>
0088 #else
0089 template <typename Protocol, typename Clock, typename WaitTraits>
0090 #endif
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
0102 typedef Protocol protocol_type;
0103
0104
0105 typedef typename Protocol::endpoint endpoint_type;
0106
0107
0108 typedef Clock clock_type;
0109
0110 #if defined(GENERATING_DOCUMENTATION)
0111
0112 typedef typename WaitTraits::time_point time_point;
0113
0114
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
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
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
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
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
0175 virtual ~basic_socket_streambuf()
0176 {
0177 if (pptr() != pbase())
0178 overflow(traits_type::eof());
0179 }
0180
0181
0182
0183
0184
0185
0186
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
0197
0198
0199
0200
0201
0202
0203
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
0216
0217
0218
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
0230 basic_socket<Protocol>& socket()
0231 {
0232 return *this;
0233 }
0234
0235
0236
0237
0238
0239
0240 const boost::system::error_code& error() const
0241 {
0242 return ec_;
0243 }
0244
0245
0246
0247
0248
0249
0250 time_point expiry() const
0251 {
0252 return expiry_time_;
0253 }
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264 void expires_at(const time_point& expiry_time)
0265 {
0266 expiry_time_ = expiry_time;
0267 }
0268
0269
0270
0271
0272
0273
0274
0275
0276
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
0290 if (gptr() != egptr())
0291 return traits_type::eof();
0292
0293 for (;;)
0294 {
0295
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
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
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
0319 if (bytes == 0)
0320 {
0321 ec_ = boost::asio::error::eof;
0322 return traits_type::eof();
0323 }
0324
0325
0326 if (ec_ != boost::asio::error::would_block
0327 && ec_ != boost::asio::error::try_again)
0328 return traits_type::eof();
0329
0330
0331 if (detail::socket_ops::poll_read(
0332 socket().native_handle(), 0, timeout(), ec_) < 0)
0333 return traits_type::eof();
0334 }
0335 #endif
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
0344 char_type ch = traits_type::to_char_type(c);
0345
0346
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);
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
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
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
0378 if (bytes > 0)
0379 {
0380 output_buffer += static_cast<std::size_t>(bytes);
0381 continue;
0382 }
0383
0384
0385 if (ec_ != boost::asio::error::would_block
0386 && ec_ != boost::asio::error::try_again)
0387 return traits_type::eof();
0388
0389
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
0400 if (traits_type::eq_int_type(c, traits_type::eof()))
0401 return traits_type::not_eof(c);
0402
0403
0404 *pptr() = ch;
0405 pbump(1);
0406 }
0407
0408 return c;
0409 #endif
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
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
0472 if (ec_)
0473 return;
0474
0475 ec_ = boost::asio::error::not_found;
0476 for (EndpointIterator i = begin; i != end; ++i)
0477 {
0478
0479 if (traits_helper::less_than(expiry_time_, traits_helper::now()))
0480 {
0481 ec_ = boost::asio::error::timed_out;
0482 return;
0483 }
0484
0485
0486 typename Protocol::endpoint ep(*i);
0487 socket().close(ec_);
0488 socket().open(ep.protocol(), ec_);
0489 if (ec_)
0490 continue;
0491
0492
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
0499 if (!ec_)
0500 return;
0501
0502
0503 if (ec_ != boost::asio::error::in_progress
0504 && ec_ != boost::asio::error::would_block)
0505 continue;
0506
0507
0508 if (detail::socket_ops::poll_connect(
0509 socket().native_handle(), timeout(), ec_) < 0)
0510 continue;
0511
0512
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
0521 ec_ = boost::system::error_code(connect_error,
0522 boost::asio::error::get_system_category());
0523 if (!ec_)
0524 return;
0525 }
0526 #endif
0527 }
0528
0529
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 }
0541 }
0542
0543 #include <boost/asio/detail/pop_options.hpp>
0544
0545 #endif
0546
0547 #endif