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