]>
Commit | Line | Data |
---|---|---|
b32b8144 FG |
1 | // |
2 | // basic_socket_streambuf.hpp | |
3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
4 | // | |
f67539c2 | 5 | // Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
b32b8144 FG |
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 | ||
11fdf7f2 TL |
31 | #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) \ |
32 | && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |
92f5a8d4 | 33 | # include <boost/asio/detail/deadline_timer_service.hpp> |
11fdf7f2 TL |
34 | #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
35 | // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |
b32b8144 | 36 | # include <boost/asio/steady_timer.hpp> |
11fdf7f2 TL |
37 | #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
38 | // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |
b32b8144 FG |
39 | |
40 | #if !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) | |
41 | ||
42 | # include <boost/asio/detail/variadic_templates.hpp> | |
43 | ||
44 | // A macro that should expand to: | |
45 | // template <typename T1, ..., typename Tn> | |
46 | // basic_socket_streambuf* connect(T1 x1, ..., Tn xn) | |
47 | // { | |
48 | // init_buffers(); | |
49 | // typedef typename Protocol::resolver resolver_type; | |
92f5a8d4 | 50 | // resolver_type resolver(socket().get_executor()); |
b32b8144 FG |
51 | // connect_to_endpoints( |
52 | // resolver.resolve(x1, ..., xn, ec_)); | |
53 | // return !ec_ ? this : 0; | |
54 | // } | |
55 | // This macro should only persist within this file. | |
56 | ||
57 | # define BOOST_ASIO_PRIVATE_CONNECT_DEF(n) \ | |
58 | template <BOOST_ASIO_VARIADIC_TPARAMS(n)> \ | |
59 | basic_socket_streambuf* connect(BOOST_ASIO_VARIADIC_BYVAL_PARAMS(n)) \ | |
60 | { \ | |
61 | init_buffers(); \ | |
62 | typedef typename Protocol::resolver resolver_type; \ | |
92f5a8d4 | 63 | resolver_type resolver(socket().get_executor()); \ |
b32b8144 FG |
64 | connect_to_endpoints( \ |
65 | resolver.resolve(BOOST_ASIO_VARIADIC_BYVAL_ARGS(n), ec_)); \ | |
66 | return !ec_ ? this : 0; \ | |
67 | } \ | |
68 | /**/ | |
69 | ||
70 | #endif // !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) | |
71 | ||
b32b8144 FG |
72 | #include <boost/asio/detail/push_options.hpp> |
73 | ||
74 | namespace boost { | |
75 | namespace asio { | |
76 | namespace detail { | |
77 | ||
78 | // A separate base class is used to ensure that the io_context member is | |
79 | // initialised prior to the basic_socket_streambuf's basic_socket base class. | |
80 | class socket_streambuf_io_context | |
81 | { | |
82 | protected: | |
83 | socket_streambuf_io_context(io_context* ctx) | |
84 | : default_io_context_(ctx) | |
85 | { | |
86 | } | |
87 | ||
88 | shared_ptr<io_context> default_io_context_; | |
89 | }; | |
90 | ||
91 | // A separate base class is used to ensure that the dynamically allocated | |
92 | // buffers are constructed prior to the basic_socket_streambuf's basic_socket | |
93 | // base class. This makes moving the socket is the last potentially throwing | |
94 | // step in the streambuf's move constructor, giving the constructor a strong | |
95 | // exception safety guarantee. | |
96 | class socket_streambuf_buffers | |
97 | { | |
98 | protected: | |
99 | socket_streambuf_buffers() | |
100 | : get_buffer_(buffer_size), | |
101 | put_buffer_(buffer_size) | |
102 | { | |
103 | } | |
104 | ||
105 | enum { buffer_size = 512 }; | |
106 | std::vector<char> get_buffer_; | |
107 | std::vector<char> put_buffer_; | |
108 | }; | |
109 | ||
110 | } // namespace detail | |
111 | ||
112 | #if !defined(BOOST_ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL) | |
113 | #define BOOST_ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL | |
114 | ||
115 | // Forward declaration with defaulted arguments. | |
92f5a8d4 | 116 | template <typename Protocol, |
11fdf7f2 TL |
117 | #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) \ |
118 | && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |
b32b8144 | 119 | typename Clock = boost::posix_time::ptime, |
92f5a8d4 | 120 | typename WaitTraits = time_traits<Clock> > |
11fdf7f2 TL |
121 | #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
122 | // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |
b32b8144 | 123 | typename Clock = chrono::steady_clock, |
92f5a8d4 | 124 | typename WaitTraits = wait_traits<Clock> > |
11fdf7f2 TL |
125 | #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
126 | // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |
b32b8144 FG |
127 | class basic_socket_streambuf; |
128 | ||
129 | #endif // !defined(BOOST_ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL) | |
130 | ||
131 | /// Iostream streambuf for a socket. | |
132 | #if defined(GENERATING_DOCUMENTATION) | |
133 | template <typename Protocol, | |
134 | typename Clock = chrono::steady_clock, | |
135 | typename WaitTraits = wait_traits<Clock> > | |
136 | #else // defined(GENERATING_DOCUMENTATION) | |
92f5a8d4 | 137 | template <typename Protocol, typename Clock, typename WaitTraits> |
b32b8144 FG |
138 | #endif // defined(GENERATING_DOCUMENTATION) |
139 | class basic_socket_streambuf | |
140 | : public std::streambuf, | |
141 | private detail::socket_streambuf_io_context, | |
142 | private detail::socket_streambuf_buffers, | |
143 | #if defined(BOOST_ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) | |
92f5a8d4 | 144 | private basic_socket<Protocol> |
b32b8144 | 145 | #else // defined(BOOST_ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) |
92f5a8d4 | 146 | public basic_socket<Protocol> |
b32b8144 FG |
147 | #endif // defined(BOOST_ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) |
148 | { | |
149 | private: | |
150 | // These typedefs are intended keep this class's implementation independent | |
151 | // of whether it's using Boost.DateClock, Boost.Chrono or std::chrono. | |
11fdf7f2 TL |
152 | #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) \ |
153 | && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |
b32b8144 | 154 | typedef WaitTraits traits_helper; |
11fdf7f2 TL |
155 | #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
156 | // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |
b32b8144 | 157 | typedef detail::chrono_time_traits<Clock, WaitTraits> traits_helper; |
11fdf7f2 TL |
158 | #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
159 | // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |
b32b8144 FG |
160 | |
161 | public: | |
162 | /// The protocol type. | |
163 | typedef Protocol protocol_type; | |
164 | ||
165 | /// The endpoint type. | |
166 | typedef typename Protocol::endpoint endpoint_type; | |
167 | ||
168 | /// The clock type. | |
169 | typedef Clock clock_type; | |
170 | ||
171 | #if defined(GENERATING_DOCUMENTATION) | |
172 | /// (Deprecated: Use time_point.) The time type. | |
173 | typedef typename WaitTraits::time_type time_type; | |
174 | ||
175 | /// The time type. | |
176 | typedef typename WaitTraits::time_point time_point; | |
177 | ||
178 | /// (Deprecated: Use duration.) The duration type. | |
179 | typedef typename WaitTraits::duration_type duration_type; | |
180 | ||
181 | /// The duration type. | |
182 | typedef typename WaitTraits::duration duration; | |
183 | #else | |
184 | # if !defined(BOOST_ASIO_NO_DEPRECATED) | |
185 | typedef typename traits_helper::time_type time_type; | |
186 | typedef typename traits_helper::duration_type duration_type; | |
187 | # endif // !defined(BOOST_ASIO_NO_DEPRECATED) | |
188 | typedef typename traits_helper::time_type time_point; | |
189 | typedef typename traits_helper::duration_type duration; | |
190 | #endif | |
191 | ||
192 | /// Construct a basic_socket_streambuf without establishing a connection. | |
193 | basic_socket_streambuf() | |
194 | : detail::socket_streambuf_io_context(new io_context), | |
92f5a8d4 | 195 | basic_socket<Protocol>(*default_io_context_), |
b32b8144 FG |
196 | expiry_time_(max_expiry_time()) |
197 | { | |
198 | init_buffers(); | |
199 | } | |
200 | ||
201 | #if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | |
202 | /// Construct a basic_socket_streambuf from the supplied socket. | |
203 | explicit basic_socket_streambuf(basic_stream_socket<protocol_type> s) | |
204 | : detail::socket_streambuf_io_context(0), | |
92f5a8d4 | 205 | basic_socket<Protocol>(std::move(s)), |
b32b8144 FG |
206 | expiry_time_(max_expiry_time()) |
207 | { | |
208 | init_buffers(); | |
209 | } | |
210 | ||
211 | /// Move-construct a basic_socket_streambuf from another. | |
212 | basic_socket_streambuf(basic_socket_streambuf&& other) | |
213 | : detail::socket_streambuf_io_context(other), | |
92f5a8d4 | 214 | basic_socket<Protocol>(std::move(other.socket())), |
b32b8144 FG |
215 | ec_(other.ec_), |
216 | expiry_time_(other.expiry_time_) | |
217 | { | |
218 | get_buffer_.swap(other.get_buffer_); | |
219 | put_buffer_.swap(other.put_buffer_); | |
220 | setg(other.eback(), other.gptr(), other.egptr()); | |
221 | setp(other.pptr(), other.epptr()); | |
222 | other.ec_ = boost::system::error_code(); | |
223 | other.expiry_time_ = max_expiry_time(); | |
224 | other.init_buffers(); | |
225 | } | |
226 | ||
227 | /// Move-assign a basic_socket_streambuf from another. | |
228 | basic_socket_streambuf& operator=(basic_socket_streambuf&& other) | |
229 | { | |
230 | this->close(); | |
231 | socket() = std::move(other.socket()); | |
232 | detail::socket_streambuf_io_context::operator=(other); | |
233 | ec_ = other.ec_; | |
234 | expiry_time_ = other.expiry_time_; | |
235 | get_buffer_.swap(other.get_buffer_); | |
236 | put_buffer_.swap(other.put_buffer_); | |
237 | setg(other.eback(), other.gptr(), other.egptr()); | |
238 | setp(other.pptr(), other.epptr()); | |
239 | other.ec_ = boost::system::error_code(); | |
240 | other.expiry_time_ = max_expiry_time(); | |
241 | other.put_buffer_.resize(buffer_size); | |
242 | other.init_buffers(); | |
243 | return *this; | |
244 | } | |
245 | #endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | |
246 | ||
247 | /// Destructor flushes buffered data. | |
248 | virtual ~basic_socket_streambuf() | |
249 | { | |
250 | if (pptr() != pbase()) | |
251 | overflow(traits_type::eof()); | |
252 | } | |
253 | ||
254 | /// Establish a connection. | |
255 | /** | |
256 | * This function establishes a connection to the specified endpoint. | |
257 | * | |
258 | * @return \c this if a connection was successfully established, a null | |
259 | * pointer otherwise. | |
260 | */ | |
261 | basic_socket_streambuf* connect(const endpoint_type& endpoint) | |
262 | { | |
263 | init_buffers(); | |
264 | ec_ = boost::system::error_code(); | |
265 | this->connect_to_endpoints(&endpoint, &endpoint + 1); | |
266 | return !ec_ ? this : 0; | |
267 | } | |
268 | ||
269 | #if defined(GENERATING_DOCUMENTATION) | |
270 | /// Establish a connection. | |
271 | /** | |
272 | * This function automatically establishes a connection based on the supplied | |
273 | * resolver query parameters. The arguments are used to construct a resolver | |
274 | * query object. | |
275 | * | |
276 | * @return \c this if a connection was successfully established, a null | |
277 | * pointer otherwise. | |
278 | */ | |
279 | template <typename T1, ..., typename TN> | |
280 | basic_socket_streambuf* connect(T1 t1, ..., TN tn); | |
281 | #elif defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) | |
282 | template <typename... T> | |
283 | basic_socket_streambuf* connect(T... x) | |
284 | { | |
285 | init_buffers(); | |
286 | typedef typename Protocol::resolver resolver_type; | |
92f5a8d4 | 287 | resolver_type resolver(socket().get_executor()); |
b32b8144 FG |
288 | connect_to_endpoints(resolver.resolve(x..., ec_)); |
289 | return !ec_ ? this : 0; | |
290 | } | |
291 | #else | |
292 | BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_CONNECT_DEF) | |
293 | #endif | |
294 | ||
295 | /// Close the connection. | |
296 | /** | |
297 | * @return \c this if a connection was successfully established, a null | |
298 | * pointer otherwise. | |
299 | */ | |
300 | basic_socket_streambuf* close() | |
301 | { | |
302 | sync(); | |
303 | socket().close(ec_); | |
304 | if (!ec_) | |
305 | init_buffers(); | |
306 | return !ec_ ? this : 0; | |
307 | } | |
308 | ||
309 | /// Get a reference to the underlying socket. | |
92f5a8d4 | 310 | basic_socket<Protocol>& socket() |
b32b8144 FG |
311 | { |
312 | return *this; | |
313 | } | |
314 | ||
315 | /// Get the last error associated with the stream buffer. | |
316 | /** | |
317 | * @return An \c error_code corresponding to the last error from the stream | |
318 | * buffer. | |
319 | */ | |
320 | const boost::system::error_code& error() const | |
321 | { | |
322 | return ec_; | |
323 | } | |
324 | ||
325 | #if !defined(BOOST_ASIO_NO_DEPRECATED) | |
326 | /// (Deprecated: Use error().) Get the last error associated with the stream | |
327 | /// buffer. | |
328 | /** | |
329 | * @return An \c error_code corresponding to the last error from the stream | |
330 | * buffer. | |
331 | */ | |
332 | const boost::system::error_code& puberror() const | |
333 | { | |
334 | return error(); | |
335 | } | |
336 | ||
337 | /// (Deprecated: Use expiry().) Get the stream buffer's expiry time as an | |
338 | /// absolute time. | |
339 | /** | |
340 | * @return An absolute time value representing the stream buffer's expiry | |
341 | * time. | |
342 | */ | |
343 | time_point expires_at() const | |
344 | { | |
345 | return expiry_time_; | |
346 | } | |
347 | #endif // !defined(BOOST_ASIO_NO_DEPRECATED) | |
348 | ||
349 | /// Get the stream buffer's expiry time as an absolute time. | |
350 | /** | |
351 | * @return An absolute time value representing the stream buffer's expiry | |
352 | * time. | |
353 | */ | |
354 | time_point expiry() const | |
355 | { | |
356 | return expiry_time_; | |
357 | } | |
358 | ||
359 | /// Set the stream buffer's expiry time as an absolute time. | |
360 | /** | |
361 | * This function sets the expiry time associated with the stream. Stream | |
362 | * operations performed after this time (where the operations cannot be | |
363 | * completed using the internal buffers) will fail with the error | |
364 | * boost::asio::error::operation_aborted. | |
365 | * | |
366 | * @param expiry_time The expiry time to be used for the stream. | |
367 | */ | |
368 | void expires_at(const time_point& expiry_time) | |
369 | { | |
370 | expiry_time_ = expiry_time; | |
371 | } | |
372 | ||
373 | /// Set the stream buffer's expiry time relative to now. | |
374 | /** | |
375 | * This function sets the expiry time associated with the stream. Stream | |
376 | * operations performed after this time (where the operations cannot be | |
377 | * completed using the internal buffers) will fail with the error | |
378 | * boost::asio::error::operation_aborted. | |
379 | * | |
380 | * @param expiry_time The expiry time to be used for the timer. | |
381 | */ | |
382 | void expires_after(const duration& expiry_time) | |
383 | { | |
384 | expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time); | |
385 | } | |
386 | ||
387 | #if !defined(BOOST_ASIO_NO_DEPRECATED) | |
388 | /// (Deprecated: Use expiry().) Get the stream buffer's expiry time relative | |
389 | /// to now. | |
390 | /** | |
391 | * @return A relative time value representing the stream buffer's expiry time. | |
392 | */ | |
393 | duration expires_from_now() const | |
394 | { | |
395 | return traits_helper::subtract(expires_at(), traits_helper::now()); | |
396 | } | |
397 | ||
398 | /// (Deprecated: Use expires_after().) Set the stream buffer's expiry time | |
399 | /// relative to now. | |
400 | /** | |
401 | * This function sets the expiry time associated with the stream. Stream | |
402 | * operations performed after this time (where the operations cannot be | |
403 | * completed using the internal buffers) will fail with the error | |
404 | * boost::asio::error::operation_aborted. | |
405 | * | |
406 | * @param expiry_time The expiry time to be used for the timer. | |
407 | */ | |
408 | void expires_from_now(const duration& expiry_time) | |
409 | { | |
410 | expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time); | |
411 | } | |
412 | #endif // !defined(BOOST_ASIO_NO_DEPRECATED) | |
413 | ||
414 | protected: | |
415 | int_type underflow() | |
416 | { | |
417 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | |
418 | ec_ = boost::asio::error::operation_not_supported; | |
419 | return traits_type::eof(); | |
420 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | |
421 | if (gptr() != egptr()) | |
422 | return traits_type::eof(); | |
423 | ||
424 | for (;;) | |
425 | { | |
426 | // Check if we are past the expiry time. | |
427 | if (traits_helper::less_than(expiry_time_, traits_helper::now())) | |
428 | { | |
429 | ec_ = boost::asio::error::timed_out; | |
430 | return traits_type::eof(); | |
431 | } | |
432 | ||
433 | // Try to complete the operation without blocking. | |
434 | if (!socket().native_non_blocking()) | |
435 | socket().native_non_blocking(true, ec_); | |
436 | detail::buffer_sequence_adapter<mutable_buffer, mutable_buffer> | |
437 | bufs(boost::asio::buffer(get_buffer_) + putback_max); | |
438 | detail::signed_size_type bytes = detail::socket_ops::recv( | |
439 | socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_); | |
440 | ||
441 | // Check if operation succeeded. | |
442 | if (bytes > 0) | |
443 | { | |
444 | setg(&get_buffer_[0], &get_buffer_[0] + putback_max, | |
445 | &get_buffer_[0] + putback_max + bytes); | |
446 | return traits_type::to_int_type(*gptr()); | |
447 | } | |
448 | ||
449 | // Check for EOF. | |
450 | if (bytes == 0) | |
451 | { | |
452 | ec_ = boost::asio::error::eof; | |
453 | return traits_type::eof(); | |
454 | } | |
455 | ||
456 | // Operation failed. | |
457 | if (ec_ != boost::asio::error::would_block | |
458 | && ec_ != boost::asio::error::try_again) | |
459 | return traits_type::eof(); | |
460 | ||
461 | // Wait for socket to become ready. | |
462 | if (detail::socket_ops::poll_read( | |
463 | socket().native_handle(), 0, timeout(), ec_) < 0) | |
464 | return traits_type::eof(); | |
465 | } | |
466 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | |
467 | } | |
468 | ||
469 | int_type overflow(int_type c) | |
470 | { | |
471 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | |
472 | ec_ = boost::asio::error::operation_not_supported; | |
473 | return traits_type::eof(); | |
474 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | |
475 | char_type ch = traits_type::to_char_type(c); | |
476 | ||
477 | // Determine what needs to be sent. | |
478 | const_buffer output_buffer; | |
479 | if (put_buffer_.empty()) | |
480 | { | |
481 | if (traits_type::eq_int_type(c, traits_type::eof())) | |
482 | return traits_type::not_eof(c); // Nothing to do. | |
483 | output_buffer = boost::asio::buffer(&ch, sizeof(char_type)); | |
484 | } | |
485 | else | |
486 | { | |
487 | output_buffer = boost::asio::buffer(pbase(), | |
488 | (pptr() - pbase()) * sizeof(char_type)); | |
489 | } | |
490 | ||
491 | while (output_buffer.size() > 0) | |
492 | { | |
493 | // Check if we are past the expiry time. | |
494 | if (traits_helper::less_than(expiry_time_, traits_helper::now())) | |
495 | { | |
496 | ec_ = boost::asio::error::timed_out; | |
497 | return traits_type::eof(); | |
498 | } | |
499 | ||
500 | // Try to complete the operation without blocking. | |
501 | if (!socket().native_non_blocking()) | |
502 | socket().native_non_blocking(true, ec_); | |
503 | detail::buffer_sequence_adapter< | |
504 | const_buffer, const_buffer> bufs(output_buffer); | |
505 | detail::signed_size_type bytes = detail::socket_ops::send( | |
506 | socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_); | |
507 | ||
508 | // Check if operation succeeded. | |
509 | if (bytes > 0) | |
510 | { | |
511 | output_buffer += static_cast<std::size_t>(bytes); | |
512 | continue; | |
513 | } | |
514 | ||
515 | // Operation failed. | |
516 | if (ec_ != boost::asio::error::would_block | |
517 | && ec_ != boost::asio::error::try_again) | |
518 | return traits_type::eof(); | |
519 | ||
520 | // Wait for socket to become ready. | |
521 | if (detail::socket_ops::poll_write( | |
522 | socket().native_handle(), 0, timeout(), ec_) < 0) | |
523 | return traits_type::eof(); | |
524 | } | |
525 | ||
526 | if (!put_buffer_.empty()) | |
527 | { | |
528 | setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); | |
529 | ||
530 | // If the new character is eof then our work here is done. | |
531 | if (traits_type::eq_int_type(c, traits_type::eof())) | |
532 | return traits_type::not_eof(c); | |
533 | ||
534 | // Add the new character to the output buffer. | |
535 | *pptr() = ch; | |
536 | pbump(1); | |
537 | } | |
538 | ||
539 | return c; | |
540 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | |
541 | } | |
542 | ||
543 | int sync() | |
544 | { | |
545 | return overflow(traits_type::eof()); | |
546 | } | |
547 | ||
548 | std::streambuf* setbuf(char_type* s, std::streamsize n) | |
549 | { | |
550 | if (pptr() == pbase() && s == 0 && n == 0) | |
551 | { | |
552 | put_buffer_.clear(); | |
553 | setp(0, 0); | |
554 | sync(); | |
555 | return this; | |
556 | } | |
557 | ||
558 | return 0; | |
559 | } | |
560 | ||
561 | private: | |
562 | // Disallow copying and assignment. | |
563 | basic_socket_streambuf(const basic_socket_streambuf&) BOOST_ASIO_DELETED; | |
564 | basic_socket_streambuf& operator=( | |
565 | const basic_socket_streambuf&) BOOST_ASIO_DELETED; | |
566 | ||
567 | void init_buffers() | |
568 | { | |
569 | setg(&get_buffer_[0], | |
570 | &get_buffer_[0] + putback_max, | |
571 | &get_buffer_[0] + putback_max); | |
572 | ||
573 | if (put_buffer_.empty()) | |
574 | setp(0, 0); | |
575 | else | |
576 | setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); | |
577 | } | |
578 | ||
579 | int timeout() const | |
580 | { | |
581 | int64_t msec = traits_helper::to_posix_duration( | |
582 | traits_helper::subtract(expiry_time_, | |
583 | traits_helper::now())).total_milliseconds(); | |
584 | if (msec > (std::numeric_limits<int>::max)()) | |
585 | msec = (std::numeric_limits<int>::max)(); | |
586 | else if (msec < 0) | |
587 | msec = 0; | |
588 | return static_cast<int>(msec); | |
589 | } | |
590 | ||
591 | template <typename EndpointSequence> | |
592 | void connect_to_endpoints(const EndpointSequence& endpoints) | |
593 | { | |
594 | this->connect_to_endpoints(endpoints.begin(), endpoints.end()); | |
595 | } | |
596 | ||
597 | template <typename EndpointIterator> | |
598 | void connect_to_endpoints(EndpointIterator begin, EndpointIterator end) | |
599 | { | |
600 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | |
601 | ec_ = boost::asio::error::operation_not_supported; | |
602 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | |
603 | if (ec_) | |
604 | return; | |
605 | ||
606 | ec_ = boost::asio::error::not_found; | |
607 | for (EndpointIterator i = begin; i != end; ++i) | |
608 | { | |
609 | // Check if we are past the expiry time. | |
610 | if (traits_helper::less_than(expiry_time_, traits_helper::now())) | |
611 | { | |
612 | ec_ = boost::asio::error::timed_out; | |
613 | return; | |
614 | } | |
615 | ||
616 | // Close and reopen the socket. | |
617 | typename Protocol::endpoint ep(*i); | |
618 | socket().close(ec_); | |
619 | socket().open(ep.protocol(), ec_); | |
620 | if (ec_) | |
621 | continue; | |
622 | ||
623 | // Try to complete the operation without blocking. | |
624 | if (!socket().native_non_blocking()) | |
625 | socket().native_non_blocking(true, ec_); | |
626 | detail::socket_ops::connect(socket().native_handle(), | |
627 | ep.data(), ep.size(), ec_); | |
628 | ||
629 | // Check if operation succeeded. | |
630 | if (!ec_) | |
631 | return; | |
632 | ||
633 | // Operation failed. | |
634 | if (ec_ != boost::asio::error::in_progress | |
635 | && ec_ != boost::asio::error::would_block) | |
636 | continue; | |
637 | ||
638 | // Wait for socket to become ready. | |
639 | if (detail::socket_ops::poll_connect( | |
640 | socket().native_handle(), timeout(), ec_) < 0) | |
641 | continue; | |
642 | ||
643 | // Get the error code from the connect operation. | |
644 | int connect_error = 0; | |
645 | size_t connect_error_len = sizeof(connect_error); | |
646 | if (detail::socket_ops::getsockopt(socket().native_handle(), 0, | |
647 | SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec_) | |
648 | == detail::socket_error_retval) | |
649 | return; | |
650 | ||
651 | // Check the result of the connect operation. | |
652 | ec_ = boost::system::error_code(connect_error, | |
653 | boost::asio::error::get_system_category()); | |
654 | if (!ec_) | |
655 | return; | |
656 | } | |
657 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | |
658 | } | |
659 | ||
660 | // Helper function to get the maximum expiry time. | |
661 | static time_point max_expiry_time() | |
662 | { | |
11fdf7f2 TL |
663 | #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) \ |
664 | && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |
b32b8144 FG |
665 | return boost::posix_time::pos_infin; |
666 | #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) | |
11fdf7f2 | 667 | // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) |
b32b8144 FG |
668 | return (time_point::max)(); |
669 | #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) | |
11fdf7f2 | 670 | // && defined(BOOST_ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) |
b32b8144 FG |
671 | } |
672 | ||
673 | enum { putback_max = 8 }; | |
674 | boost::system::error_code ec_; | |
675 | time_point expiry_time_; | |
676 | }; | |
677 | ||
678 | } // namespace asio | |
679 | } // namespace boost | |
680 | ||
681 | #include <boost/asio/detail/pop_options.hpp> | |
682 | ||
b32b8144 FG |
683 | #if !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) |
684 | # undef BOOST_ASIO_PRIVATE_CONNECT_DEF | |
685 | #endif // !defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) | |
686 | ||
687 | #endif // !defined(BOOST_ASIO_NO_IOSTREAM) | |
688 | ||
689 | #endif // BOOST_ASIO_BASIC_SOCKET_STREAMBUF_HPP |