1 // 2 // basic_socket_streambuf.hpp 3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2015 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 <boost/asio/basic_socket.hpp> 24 #include <boost/asio/deadline_timer_service.hpp> 25 #include <boost/asio/detail/array.hpp> 26 #include <boost/asio/detail/throw_error.hpp> 27 #include <boost/asio/io_service.hpp> 28 #include <boost/asio/stream_socket_service.hpp> 29 30 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) 31 # include <boost/asio/deadline_timer.hpp> 32 #else 33 # include <boost/asio/steady_timer.hpp> 34 #endif 35 36 #if !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 37 38 # include <boost/asio/detail/variadic_templates.hpp> 39 40 // A macro that should expand to: 41 // template <typename T1, ..., typename Tn> 42 // basic_socket_streambuf<Protocol, StreamSocketService, 43 // Time, TimeTraits, TimerService>* connect( 44 // T1 x1, ..., Tn xn) 45 // { 46 // init_buffers(); 47 // this->basic_socket<Protocol, StreamSocketService>::close(ec_); 48 // typedef typename Protocol::resolver resolver_type; 49 // typedef typename resolver_type::query resolver_query; 50 // resolver_query query(x1, ..., xn); 51 // resolve_and_connect(query); 52 // return !ec_ ? this : 0; 53 // } 54 // This macro should only persist within this file. 55 56 # define BOOST_ASIO_PRIVATE_CONNECT_DEF(n) \ 57 template <BOOST_ASIO_VARIADIC_TPARAMS(n)> \ 58 basic_socket_streambuf<Protocol, StreamSocketService, \ 59 Time, TimeTraits, TimerService>* connect(BOOST_ASIO_VARIADIC_PARAMS(n)) \ 60 { \ 61 init_buffers(); \ 62 this->basic_socket<Protocol, StreamSocketService>::close(ec_); \ 63 typedef typename Protocol::resolver resolver_type; \ 64 typedef typename resolver_type::query resolver_query; \ 65 resolver_query query(BOOST_ASIO_VARIADIC_ARGS(n)); \ 66 resolve_and_connect(query); \ 67 return !ec_ ? this : 0; \ 68 } \ 69 /**/ 70 71 #endif // !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 72 73 #include <boost/asio/detail/push_options.hpp> 74 75 namespace boost { 76 namespace asio { 77 namespace detail { 78 79 // A separate base class is used to ensure that the io_service is initialised 80 // prior to the basic_socket_streambuf's basic_socket base class. 81 class socket_streambuf_base 82 { 83 protected: 84 io_service io_service_; 85 }; 86 87 } // namespace detail 88 89 /// Iostream streambuf for a socket. 90 template <typename Protocol, 91 typename StreamSocketService = stream_socket_service<Protocol>, 92 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) \ 93 || defined(GENERATING_DOCUMENTATION) 94 typename Time = boost::posix_time::ptime, 95 typename TimeTraits = boost::asio::time_traits<Time>, 96 typename TimerService = deadline_timer_service<Time, TimeTraits> > 97 #else 98 typename Time = steady_timer::clock_type, 99 typename TimeTraits = steady_timer::traits_type, 100 typename TimerService = steady_timer::service_type> 101 #endif 102 class basic_socket_streambuf 103 : public std::streambuf, 104 private detail::socket_streambuf_base, 105 public basic_socket<Protocol, StreamSocketService> 106 { 107 private: 108 // These typedefs are intended keep this class's implementation independent 109 // of whether it's using Boost.DateTime, Boost.Chrono or std::chrono. 110 #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) 111 typedef TimeTraits traits_helper; 112 #else 113 typedef detail::chrono_time_traits<Time, TimeTraits> traits_helper; 114 #endif 115 116 public: 117 /// The endpoint type. 118 typedef typename Protocol::endpoint endpoint_type; 119 120 #if defined(GENERATING_DOCUMENTATION) 121 /// The time type. 122 typedef typename TimeTraits::time_type time_type; 123 124 /// The duration type. 125 typedef typename TimeTraits::duration_type duration_type; 126 #else 127 typedef typename traits_helper::time_type time_type; 128 typedef typename traits_helper::duration_type duration_type; 129 #endif 130 131 /// Construct a basic_socket_streambuf without establishing a connection. basic_socket_streambuf()132 basic_socket_streambuf() 133 : basic_socket<Protocol, StreamSocketService>( 134 this->detail::socket_streambuf_base::io_service_), 135 unbuffered_(false), 136 timer_service_(0), 137 timer_state_(no_timer) 138 { 139 init_buffers(); 140 } 141 142 /// Destructor flushes buffered data. ~basic_socket_streambuf()143 virtual ~basic_socket_streambuf() 144 { 145 if (pptr() != pbase()) 146 overflow(traits_type::eof()); 147 148 destroy_timer(); 149 } 150 151 /// Establish a connection. 152 /** 153 * This function establishes a connection to the specified endpoint. 154 * 155 * @return \c this if a connection was successfully established, a null 156 * pointer otherwise. 157 */ 158 basic_socket_streambuf<Protocol, StreamSocketService, connect(const endpoint_type & endpoint)159 Time, TimeTraits, TimerService>* connect( 160 const endpoint_type& endpoint) 161 { 162 init_buffers(); 163 164 this->basic_socket<Protocol, StreamSocketService>::close(ec_); 165 166 if (timer_state_ == timer_has_expired) 167 { 168 ec_ = boost::asio::error::operation_aborted; 169 return 0; 170 } 171 172 io_handler handler = { this }; 173 this->basic_socket<Protocol, StreamSocketService>::async_connect( 174 endpoint, handler); 175 176 ec_ = boost::asio::error::would_block; 177 this->get_service().get_io_service().reset(); 178 do this->get_service().get_io_service().run_one(); 179 while (ec_ == boost::asio::error::would_block); 180 181 return !ec_ ? this : 0; 182 } 183 184 #if defined(GENERATING_DOCUMENTATION) 185 /// Establish a connection. 186 /** 187 * This function automatically establishes a connection based on the supplied 188 * resolver query parameters. The arguments are used to construct a resolver 189 * query object. 190 * 191 * @return \c this if a connection was successfully established, a null 192 * pointer otherwise. 193 */ 194 template <typename T1, ..., typename TN> 195 basic_socket_streambuf<Protocol, StreamSocketService>* connect( 196 T1 t1, ..., TN tn); 197 #elif defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 198 template <typename... T> 199 basic_socket_streambuf<Protocol, StreamSocketService, connect(T...x)200 Time, TimeTraits, TimerService>* connect(T... x) 201 { 202 init_buffers(); 203 this->basic_socket<Protocol, StreamSocketService>::close(ec_); 204 typedef typename Protocol::resolver resolver_type; 205 typedef typename resolver_type::query resolver_query; 206 resolver_query query(x...); 207 resolve_and_connect(query); 208 return !ec_ ? this : 0; 209 } 210 #else BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_CONNECT_DEF)211 BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_CONNECT_DEF) 212 #endif 213 214 /// Close the connection. 215 /** 216 * @return \c this if a connection was successfully established, a null 217 * pointer otherwise. 218 */ 219 basic_socket_streambuf<Protocol, StreamSocketService, 220 Time, TimeTraits, TimerService>* close() 221 { 222 sync(); 223 this->basic_socket<Protocol, StreamSocketService>::close(ec_); 224 if (!ec_) 225 init_buffers(); 226 return !ec_ ? this : 0; 227 } 228 229 /// Get the last error associated with the stream buffer. 230 /** 231 * @return An \c error_code corresponding to the last error from the stream 232 * buffer. 233 */ puberror() const234 const boost::system::error_code& puberror() const 235 { 236 return error(); 237 } 238 239 /// Get the stream buffer's expiry time as an absolute time. 240 /** 241 * @return An absolute time value representing the stream buffer's expiry 242 * time. 243 */ expires_at() const244 time_type expires_at() const 245 { 246 return timer_service_ 247 ? timer_service_->expires_at(timer_implementation_) 248 : time_type(); 249 } 250 251 /// Set the stream buffer's expiry time as an absolute time. 252 /** 253 * This function sets the expiry time associated with the stream. Stream 254 * operations performed after this time (where the operations cannot be 255 * completed using the internal buffers) will fail with the error 256 * boost::asio::error::operation_aborted. 257 * 258 * @param expiry_time The expiry time to be used for the stream. 259 */ expires_at(const time_type & expiry_time)260 void expires_at(const time_type& expiry_time) 261 { 262 construct_timer(); 263 264 boost::system::error_code ec; 265 timer_service_->expires_at(timer_implementation_, expiry_time, ec); 266 boost::asio::detail::throw_error(ec, "expires_at"); 267 268 start_timer(); 269 } 270 271 /// Get the stream buffer's expiry time relative to now. 272 /** 273 * @return A relative time value representing the stream buffer's expiry time. 274 */ expires_from_now() const275 duration_type expires_from_now() const 276 { 277 return traits_helper::subtract(expires_at(), traits_helper::now()); 278 } 279 280 /// Set the stream buffer's expiry time relative to now. 281 /** 282 * This function sets the expiry time associated with the stream. Stream 283 * operations performed after this time (where the operations cannot be 284 * completed using the internal buffers) will fail with the error 285 * boost::asio::error::operation_aborted. 286 * 287 * @param expiry_time The expiry time to be used for the timer. 288 */ expires_from_now(const duration_type & expiry_time)289 void expires_from_now(const duration_type& expiry_time) 290 { 291 construct_timer(); 292 293 boost::system::error_code ec; 294 timer_service_->expires_from_now(timer_implementation_, expiry_time, ec); 295 boost::asio::detail::throw_error(ec, "expires_from_now"); 296 297 start_timer(); 298 } 299 300 protected: underflow()301 int_type underflow() 302 { 303 if (gptr() == egptr()) 304 { 305 if (timer_state_ == timer_has_expired) 306 { 307 ec_ = boost::asio::error::operation_aborted; 308 return traits_type::eof(); 309 } 310 311 io_handler handler = { this }; 312 this->get_service().async_receive(this->get_implementation(), 313 boost::asio::buffer(boost::asio::buffer(get_buffer_) + putback_max), 314 0, handler); 315 316 ec_ = boost::asio::error::would_block; 317 this->get_service().get_io_service().reset(); 318 do this->get_service().get_io_service().run_one(); 319 while (ec_ == boost::asio::error::would_block); 320 if (ec_) 321 return traits_type::eof(); 322 323 setg(&get_buffer_[0], &get_buffer_[0] + putback_max, 324 &get_buffer_[0] + putback_max + bytes_transferred_); 325 return traits_type::to_int_type(*gptr()); 326 } 327 else 328 { 329 return traits_type::eof(); 330 } 331 } 332 overflow(int_type c)333 int_type overflow(int_type c) 334 { 335 if (unbuffered_) 336 { 337 if (traits_type::eq_int_type(c, traits_type::eof())) 338 { 339 // Nothing to do. 340 return traits_type::not_eof(c); 341 } 342 else 343 { 344 if (timer_state_ == timer_has_expired) 345 { 346 ec_ = boost::asio::error::operation_aborted; 347 return traits_type::eof(); 348 } 349 350 // Send the single character immediately. 351 char_type ch = traits_type::to_char_type(c); 352 io_handler handler = { this }; 353 this->get_service().async_send(this->get_implementation(), 354 boost::asio::buffer(&ch, sizeof(char_type)), 0, handler); 355 356 ec_ = boost::asio::error::would_block; 357 this->get_service().get_io_service().reset(); 358 do this->get_service().get_io_service().run_one(); 359 while (ec_ == boost::asio::error::would_block); 360 if (ec_) 361 return traits_type::eof(); 362 363 return c; 364 } 365 } 366 else 367 { 368 // Send all data in the output buffer. 369 boost::asio::const_buffer buffer = 370 boost::asio::buffer(pbase(), pptr() - pbase()); 371 while (boost::asio::buffer_size(buffer) > 0) 372 { 373 if (timer_state_ == timer_has_expired) 374 { 375 ec_ = boost::asio::error::operation_aborted; 376 return traits_type::eof(); 377 } 378 379 io_handler handler = { this }; 380 this->get_service().async_send(this->get_implementation(), 381 boost::asio::buffer(buffer), 0, handler); 382 383 ec_ = boost::asio::error::would_block; 384 this->get_service().get_io_service().reset(); 385 do this->get_service().get_io_service().run_one(); 386 while (ec_ == boost::asio::error::would_block); 387 if (ec_) 388 return traits_type::eof(); 389 390 buffer = buffer + bytes_transferred_; 391 } 392 setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); 393 394 // If the new character is eof then our work here is done. 395 if (traits_type::eq_int_type(c, traits_type::eof())) 396 return traits_type::not_eof(c); 397 398 // Add the new character to the output buffer. 399 *pptr() = traits_type::to_char_type(c); 400 pbump(1); 401 return c; 402 } 403 } 404 sync()405 int sync() 406 { 407 return overflow(traits_type::eof()); 408 } 409 setbuf(char_type * s,std::streamsize n)410 std::streambuf* setbuf(char_type* s, std::streamsize n) 411 { 412 if (pptr() == pbase() && s == 0 && n == 0) 413 { 414 unbuffered_ = true; 415 setp(0, 0); 416 return this; 417 } 418 419 return 0; 420 } 421 422 /// Get the last error associated with the stream buffer. 423 /** 424 * @return An \c error_code corresponding to the last error from the stream 425 * buffer. 426 */ error() const427 virtual const boost::system::error_code& error() const 428 { 429 return ec_; 430 } 431 432 private: init_buffers()433 void init_buffers() 434 { 435 setg(&get_buffer_[0], 436 &get_buffer_[0] + putback_max, 437 &get_buffer_[0] + putback_max); 438 if (unbuffered_) 439 setp(0, 0); 440 else 441 setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); 442 } 443 444 template <typename ResolverQuery> resolve_and_connect(const ResolverQuery & query)445 void resolve_and_connect(const ResolverQuery& query) 446 { 447 typedef typename Protocol::resolver resolver_type; 448 typedef typename resolver_type::iterator iterator_type; 449 resolver_type resolver(detail::socket_streambuf_base::io_service_); 450 iterator_type i = resolver.resolve(query, ec_); 451 if (!ec_) 452 { 453 iterator_type end; 454 ec_ = boost::asio::error::host_not_found; 455 while (ec_ && i != end) 456 { 457 this->basic_socket<Protocol, StreamSocketService>::close(ec_); 458 459 if (timer_state_ == timer_has_expired) 460 { 461 ec_ = boost::asio::error::operation_aborted; 462 return; 463 } 464 465 io_handler handler = { this }; 466 this->basic_socket<Protocol, StreamSocketService>::async_connect( 467 *i, handler); 468 469 ec_ = boost::asio::error::would_block; 470 this->get_service().get_io_service().reset(); 471 do this->get_service().get_io_service().run_one(); 472 while (ec_ == boost::asio::error::would_block); 473 474 ++i; 475 } 476 } 477 } 478 479 struct io_handler; 480 friend struct io_handler; 481 struct io_handler 482 { 483 basic_socket_streambuf* this_; 484 operator ()boost::asio::basic_socket_streambuf::io_handler485 void operator()(const boost::system::error_code& ec, 486 std::size_t bytes_transferred = 0) 487 { 488 this_->ec_ = ec; 489 this_->bytes_transferred_ = bytes_transferred; 490 } 491 }; 492 493 struct timer_handler; 494 friend struct timer_handler; 495 struct timer_handler 496 { 497 basic_socket_streambuf* this_; 498 operator ()boost::asio::basic_socket_streambuf::timer_handler499 void operator()(const boost::system::error_code&) 500 { 501 time_type now = traits_helper::now(); 502 503 time_type expiry_time = this_->timer_service_->expires_at( 504 this_->timer_implementation_); 505 506 if (traits_helper::less_than(now, expiry_time)) 507 { 508 this_->timer_state_ = timer_is_pending; 509 this_->timer_service_->async_wait(this_->timer_implementation_, *this); 510 } 511 else 512 { 513 this_->timer_state_ = timer_has_expired; 514 boost::system::error_code ec; 515 this_->basic_socket<Protocol, StreamSocketService>::close(ec); 516 } 517 } 518 }; 519 construct_timer()520 void construct_timer() 521 { 522 if (timer_service_ == 0) 523 { 524 TimerService& timer_service = use_service<TimerService>( 525 detail::socket_streambuf_base::io_service_); 526 timer_service.construct(timer_implementation_); 527 timer_service_ = &timer_service; 528 } 529 } 530 destroy_timer()531 void destroy_timer() 532 { 533 if (timer_service_) 534 timer_service_->destroy(timer_implementation_); 535 } 536 start_timer()537 void start_timer() 538 { 539 if (timer_state_ != timer_is_pending) 540 { 541 timer_handler handler = { this }; 542 handler(boost::system::error_code()); 543 } 544 } 545 546 enum { putback_max = 8 }; 547 enum { buffer_size = 512 }; 548 boost::asio::detail::array<char, buffer_size> get_buffer_; 549 boost::asio::detail::array<char, buffer_size> put_buffer_; 550 bool unbuffered_; 551 boost::system::error_code ec_; 552 std::size_t bytes_transferred_; 553 TimerService* timer_service_; 554 typename TimerService::implementation_type timer_implementation_; 555 enum state { no_timer, timer_is_pending, timer_has_expired } timer_state_; 556 }; 557 558 } // namespace asio 559 } // namespace boost 560 561 #include <boost/asio/detail/pop_options.hpp> 562 563 #if !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 564 # undef BOOST_ASIO_PRIVATE_CONNECT_DEF 565 #endif // !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 566 567 #endif // !defined(BOOST_ASIO_NO_IOSTREAM) 568 569 #endif // BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP 570