connect.hpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. //
  2. // impl/connect.hpp
  3. // ~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2025 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. #ifndef BOOST_ASIO_IMPL_CONNECT_HPP
  11. #define BOOST_ASIO_IMPL_CONNECT_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <algorithm>
  16. #include <boost/asio/associator.hpp>
  17. #include <boost/asio/detail/base_from_cancellation_state.hpp>
  18. #include <boost/asio/detail/bind_handler.hpp>
  19. #include <boost/asio/detail/handler_cont_helpers.hpp>
  20. #include <boost/asio/detail/handler_tracking.hpp>
  21. #include <boost/asio/detail/handler_type_requirements.hpp>
  22. #include <boost/asio/detail/non_const_lvalue.hpp>
  23. #include <boost/asio/detail/throw_error.hpp>
  24. #include <boost/asio/detail/type_traits.hpp>
  25. #include <boost/asio/error.hpp>
  26. #include <boost/asio/post.hpp>
  27. #include <boost/asio/detail/push_options.hpp>
  28. namespace boost {
  29. namespace asio {
  30. namespace detail
  31. {
  32. template <typename Protocol, typename Iterator>
  33. inline typename Protocol::endpoint deref_connect_result(
  34. Iterator iter, boost::system::error_code& ec)
  35. {
  36. return ec ? typename Protocol::endpoint() : *iter;
  37. }
  38. template <typename ConnectCondition, typename Iterator>
  39. inline Iterator call_connect_condition(ConnectCondition& connect_condition,
  40. const boost::system::error_code& ec, Iterator next, Iterator end,
  41. constraint_t<
  42. is_same<
  43. result_of_t<ConnectCondition(boost::system::error_code, Iterator)>,
  44. Iterator
  45. >::value
  46. > = 0)
  47. {
  48. if (next != end)
  49. return connect_condition(ec, next);
  50. return end;
  51. }
  52. template <typename ConnectCondition, typename Iterator>
  53. inline Iterator call_connect_condition(ConnectCondition& connect_condition,
  54. const boost::system::error_code& ec, Iterator next, Iterator end,
  55. constraint_t<
  56. is_same<
  57. result_of_t<ConnectCondition(boost::system::error_code,
  58. decltype(*declval<Iterator>()))>,
  59. bool
  60. >::value
  61. > = 0)
  62. {
  63. for (;next != end; ++next)
  64. if (connect_condition(ec, *next))
  65. return next;
  66. return end;
  67. }
  68. } // namespace detail
  69. template <typename Protocol, typename Executor, typename EndpointSequence>
  70. typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
  71. const EndpointSequence& endpoints,
  72. constraint_t<
  73. is_endpoint_sequence<EndpointSequence>::value
  74. >)
  75. {
  76. boost::system::error_code ec;
  77. typename Protocol::endpoint result = connect(s, endpoints, ec);
  78. boost::asio::detail::throw_error(ec, "connect");
  79. return result;
  80. }
  81. template <typename Protocol, typename Executor, typename EndpointSequence>
  82. typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
  83. const EndpointSequence& endpoints, boost::system::error_code& ec,
  84. constraint_t<
  85. is_endpoint_sequence<EndpointSequence>::value
  86. >)
  87. {
  88. return detail::deref_connect_result<Protocol>(
  89. connect(s, endpoints.begin(), endpoints.end(),
  90. detail::default_connect_condition(), ec), ec);
  91. }
  92. template <typename Protocol, typename Executor, typename Iterator>
  93. Iterator connect(basic_socket<Protocol, Executor>& s,
  94. Iterator begin, Iterator end)
  95. {
  96. boost::system::error_code ec;
  97. Iterator result = connect(s, begin, end, ec);
  98. boost::asio::detail::throw_error(ec, "connect");
  99. return result;
  100. }
  101. template <typename Protocol, typename Executor, typename Iterator>
  102. inline Iterator connect(basic_socket<Protocol, Executor>& s,
  103. Iterator begin, Iterator end, boost::system::error_code& ec)
  104. {
  105. return connect(s, begin, end, detail::default_connect_condition(), ec);
  106. }
  107. template <typename Protocol, typename Executor,
  108. typename EndpointSequence, typename ConnectCondition>
  109. typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
  110. const EndpointSequence& endpoints, ConnectCondition connect_condition,
  111. constraint_t<
  112. is_endpoint_sequence<EndpointSequence>::value
  113. >,
  114. constraint_t<
  115. is_connect_condition<ConnectCondition,
  116. decltype(declval<const EndpointSequence&>().begin())>::value
  117. >)
  118. {
  119. boost::system::error_code ec;
  120. typename Protocol::endpoint result = connect(
  121. s, endpoints, connect_condition, ec);
  122. boost::asio::detail::throw_error(ec, "connect");
  123. return result;
  124. }
  125. template <typename Protocol, typename Executor,
  126. typename EndpointSequence, typename ConnectCondition>
  127. typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
  128. const EndpointSequence& endpoints, ConnectCondition connect_condition,
  129. boost::system::error_code& ec,
  130. constraint_t<
  131. is_endpoint_sequence<EndpointSequence>::value
  132. >,
  133. constraint_t<
  134. is_connect_condition<ConnectCondition,
  135. decltype(declval<const EndpointSequence&>().begin())>::value
  136. >)
  137. {
  138. return detail::deref_connect_result<Protocol>(
  139. connect(s, endpoints.begin(), endpoints.end(),
  140. connect_condition, ec), ec);
  141. }
  142. template <typename Protocol, typename Executor,
  143. typename Iterator, typename ConnectCondition>
  144. Iterator connect(basic_socket<Protocol, Executor>& s, Iterator begin,
  145. Iterator end, ConnectCondition connect_condition,
  146. constraint_t<
  147. is_connect_condition<ConnectCondition, Iterator>::value
  148. >)
  149. {
  150. boost::system::error_code ec;
  151. Iterator result = connect(s, begin, end, connect_condition, ec);
  152. boost::asio::detail::throw_error(ec, "connect");
  153. return result;
  154. }
  155. template <typename Protocol, typename Executor,
  156. typename Iterator, typename ConnectCondition>
  157. Iterator connect(basic_socket<Protocol, Executor>& s, Iterator begin,
  158. Iterator end, ConnectCondition connect_condition,
  159. boost::system::error_code& ec,
  160. constraint_t<
  161. is_connect_condition<ConnectCondition, Iterator>::value
  162. >)
  163. {
  164. ec = boost::system::error_code();
  165. for (Iterator iter = begin; iter != end; ++iter)
  166. {
  167. iter = (detail::call_connect_condition(connect_condition, ec, iter, end));
  168. if (iter != end)
  169. {
  170. s.close(ec);
  171. s.connect(*iter, ec);
  172. if (!ec)
  173. return iter;
  174. }
  175. else
  176. break;
  177. }
  178. if (!ec)
  179. ec = boost::asio::error::not_found;
  180. return end;
  181. }
  182. namespace detail
  183. {
  184. // Enable the empty base class optimisation for the connect condition.
  185. template <typename ConnectCondition>
  186. class base_from_connect_condition
  187. {
  188. protected:
  189. explicit base_from_connect_condition(
  190. const ConnectCondition& connect_condition)
  191. : connect_condition_(connect_condition)
  192. {
  193. }
  194. template <typename Iterator>
  195. void check_condition(const boost::system::error_code& ec,
  196. Iterator& iter, Iterator& end)
  197. {
  198. iter = detail::call_connect_condition(connect_condition_, ec, iter, end);
  199. }
  200. private:
  201. ConnectCondition connect_condition_;
  202. };
  203. // The default_connect_condition implementation is essentially a no-op. This
  204. // template specialisation lets us eliminate all costs associated with it.
  205. template <>
  206. class base_from_connect_condition<default_connect_condition>
  207. {
  208. protected:
  209. explicit base_from_connect_condition(const default_connect_condition&)
  210. {
  211. }
  212. template <typename Iterator>
  213. void check_condition(const boost::system::error_code&, Iterator&, Iterator&)
  214. {
  215. }
  216. };
  217. template <typename Protocol, typename Executor, typename EndpointSequence,
  218. typename ConnectCondition, typename RangeConnectHandler>
  219. class range_connect_op
  220. : public base_from_cancellation_state<RangeConnectHandler>,
  221. base_from_connect_condition<ConnectCondition>
  222. {
  223. public:
  224. range_connect_op(basic_socket<Protocol, Executor>& sock,
  225. const EndpointSequence& endpoints,
  226. const ConnectCondition& connect_condition,
  227. RangeConnectHandler& handler)
  228. : base_from_cancellation_state<RangeConnectHandler>(
  229. handler, enable_partial_cancellation()),
  230. base_from_connect_condition<ConnectCondition>(connect_condition),
  231. socket_(sock),
  232. endpoints_(endpoints),
  233. index_(0),
  234. start_(0),
  235. handler_(static_cast<RangeConnectHandler&&>(handler))
  236. {
  237. }
  238. range_connect_op(const range_connect_op& other)
  239. : base_from_cancellation_state<RangeConnectHandler>(other),
  240. base_from_connect_condition<ConnectCondition>(other),
  241. socket_(other.socket_),
  242. endpoints_(other.endpoints_),
  243. index_(other.index_),
  244. start_(other.start_),
  245. handler_(other.handler_)
  246. {
  247. }
  248. range_connect_op(range_connect_op&& other)
  249. : base_from_cancellation_state<RangeConnectHandler>(
  250. static_cast<base_from_cancellation_state<RangeConnectHandler>&&>(
  251. other)),
  252. base_from_connect_condition<ConnectCondition>(other),
  253. socket_(other.socket_),
  254. endpoints_(other.endpoints_),
  255. index_(other.index_),
  256. start_(other.start_),
  257. handler_(static_cast<RangeConnectHandler&&>(other.handler_))
  258. {
  259. }
  260. void operator()(boost::system::error_code ec, int start = 0)
  261. {
  262. this->process(ec, start,
  263. const_cast<const EndpointSequence&>(endpoints_).begin(),
  264. const_cast<const EndpointSequence&>(endpoints_).end());
  265. }
  266. //private:
  267. template <typename Iterator>
  268. void process(boost::system::error_code ec,
  269. int start, Iterator begin, Iterator end)
  270. {
  271. Iterator iter = begin;
  272. std::advance(iter, index_);
  273. switch (start_ = start)
  274. {
  275. case 1:
  276. for (;;)
  277. {
  278. this->check_condition(ec, iter, end);
  279. index_ = std::distance(begin, iter);
  280. if (iter != end)
  281. {
  282. socket_.close(ec);
  283. BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, "async_connect"));
  284. socket_.async_connect(*iter,
  285. static_cast<range_connect_op&&>(*this));
  286. return;
  287. }
  288. if (start)
  289. {
  290. ec = boost::asio::error::not_found;
  291. BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, "async_connect"));
  292. boost::asio::post(socket_.get_executor(),
  293. detail::bind_handler(
  294. static_cast<range_connect_op&&>(*this), ec));
  295. return;
  296. }
  297. /* fall-through */ default:
  298. if (iter == end)
  299. break;
  300. if (!socket_.is_open())
  301. {
  302. ec = boost::asio::error::operation_aborted;
  303. break;
  304. }
  305. if (!ec)
  306. break;
  307. if (this->cancelled() != cancellation_type::none)
  308. {
  309. ec = boost::asio::error::operation_aborted;
  310. break;
  311. }
  312. ++iter;
  313. ++index_;
  314. }
  315. static_cast<RangeConnectHandler&&>(handler_)(
  316. static_cast<const boost::system::error_code&>(ec),
  317. static_cast<const typename Protocol::endpoint&>(
  318. ec || iter == end ? typename Protocol::endpoint() : *iter));
  319. }
  320. }
  321. basic_socket<Protocol, Executor>& socket_;
  322. EndpointSequence endpoints_;
  323. std::size_t index_;
  324. int start_;
  325. RangeConnectHandler handler_;
  326. };
  327. template <typename Protocol, typename Executor, typename EndpointSequence,
  328. typename ConnectCondition, typename RangeConnectHandler>
  329. inline bool asio_handler_is_continuation(
  330. range_connect_op<Protocol, Executor, EndpointSequence,
  331. ConnectCondition, RangeConnectHandler>* this_handler)
  332. {
  333. return boost_asio_handler_cont_helpers::is_continuation(
  334. this_handler->handler_);
  335. }
  336. template <typename Protocol, typename Executor>
  337. class initiate_async_range_connect
  338. {
  339. public:
  340. typedef Executor executor_type;
  341. explicit initiate_async_range_connect(basic_socket<Protocol, Executor>& s)
  342. : socket_(s)
  343. {
  344. }
  345. executor_type get_executor() const noexcept
  346. {
  347. return socket_.get_executor();
  348. }
  349. template <typename RangeConnectHandler,
  350. typename EndpointSequence, typename ConnectCondition>
  351. void operator()(RangeConnectHandler&& handler,
  352. const EndpointSequence& endpoints,
  353. const ConnectCondition& connect_condition) const
  354. {
  355. // If you get an error on the following line it means that your
  356. // handler does not meet the documented type requirements for an
  357. // RangeConnectHandler.
  358. BOOST_ASIO_RANGE_CONNECT_HANDLER_CHECK(RangeConnectHandler,
  359. handler, typename Protocol::endpoint) type_check;
  360. non_const_lvalue<RangeConnectHandler> handler2(handler);
  361. range_connect_op<Protocol, Executor, EndpointSequence, ConnectCondition,
  362. decay_t<RangeConnectHandler>>(socket_, endpoints,
  363. connect_condition, handler2.value)(boost::system::error_code(), 1);
  364. }
  365. private:
  366. basic_socket<Protocol, Executor>& socket_;
  367. };
  368. template <typename Protocol, typename Executor, typename Iterator,
  369. typename ConnectCondition, typename IteratorConnectHandler>
  370. class iterator_connect_op
  371. : public base_from_cancellation_state<IteratorConnectHandler>,
  372. base_from_connect_condition<ConnectCondition>
  373. {
  374. public:
  375. iterator_connect_op(basic_socket<Protocol, Executor>& sock,
  376. const Iterator& begin, const Iterator& end,
  377. const ConnectCondition& connect_condition,
  378. IteratorConnectHandler& handler)
  379. : base_from_cancellation_state<IteratorConnectHandler>(
  380. handler, enable_partial_cancellation()),
  381. base_from_connect_condition<ConnectCondition>(connect_condition),
  382. socket_(sock),
  383. iter_(begin),
  384. end_(end),
  385. start_(0),
  386. handler_(static_cast<IteratorConnectHandler&&>(handler))
  387. {
  388. }
  389. iterator_connect_op(const iterator_connect_op& other)
  390. : base_from_cancellation_state<IteratorConnectHandler>(other),
  391. base_from_connect_condition<ConnectCondition>(other),
  392. socket_(other.socket_),
  393. iter_(other.iter_),
  394. end_(other.end_),
  395. start_(other.start_),
  396. handler_(other.handler_)
  397. {
  398. }
  399. iterator_connect_op(iterator_connect_op&& other)
  400. : base_from_cancellation_state<IteratorConnectHandler>(
  401. static_cast<base_from_cancellation_state<IteratorConnectHandler>&&>(
  402. other)),
  403. base_from_connect_condition<ConnectCondition>(other),
  404. socket_(other.socket_),
  405. iter_(other.iter_),
  406. end_(other.end_),
  407. start_(other.start_),
  408. handler_(static_cast<IteratorConnectHandler&&>(other.handler_))
  409. {
  410. }
  411. void operator()(boost::system::error_code ec, int start = 0)
  412. {
  413. switch (start_ = start)
  414. {
  415. case 1:
  416. for (;;)
  417. {
  418. this->check_condition(ec, iter_, end_);
  419. if (iter_ != end_)
  420. {
  421. socket_.close(ec);
  422. BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, "async_connect"));
  423. socket_.async_connect(*iter_,
  424. static_cast<iterator_connect_op&&>(*this));
  425. return;
  426. }
  427. if (start)
  428. {
  429. ec = boost::asio::error::not_found;
  430. BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, "async_connect"));
  431. boost::asio::post(socket_.get_executor(),
  432. detail::bind_handler(
  433. static_cast<iterator_connect_op&&>(*this), ec));
  434. return;
  435. }
  436. /* fall-through */ default:
  437. if (iter_ == end_)
  438. break;
  439. if (!socket_.is_open())
  440. {
  441. ec = boost::asio::error::operation_aborted;
  442. break;
  443. }
  444. if (!ec)
  445. break;
  446. if (this->cancelled() != cancellation_type::none)
  447. {
  448. ec = boost::asio::error::operation_aborted;
  449. break;
  450. }
  451. ++iter_;
  452. }
  453. static_cast<IteratorConnectHandler&&>(handler_)(
  454. static_cast<const boost::system::error_code&>(ec),
  455. static_cast<const Iterator&>(iter_));
  456. }
  457. }
  458. //private:
  459. basic_socket<Protocol, Executor>& socket_;
  460. Iterator iter_;
  461. Iterator end_;
  462. int start_;
  463. IteratorConnectHandler handler_;
  464. };
  465. template <typename Protocol, typename Executor, typename Iterator,
  466. typename ConnectCondition, typename IteratorConnectHandler>
  467. inline bool asio_handler_is_continuation(
  468. iterator_connect_op<Protocol, Executor, Iterator,
  469. ConnectCondition, IteratorConnectHandler>* this_handler)
  470. {
  471. return boost_asio_handler_cont_helpers::is_continuation(
  472. this_handler->handler_);
  473. }
  474. template <typename Protocol, typename Executor>
  475. class initiate_async_iterator_connect
  476. {
  477. public:
  478. typedef Executor executor_type;
  479. explicit initiate_async_iterator_connect(
  480. basic_socket<Protocol, Executor>& s)
  481. : socket_(s)
  482. {
  483. }
  484. executor_type get_executor() const noexcept
  485. {
  486. return socket_.get_executor();
  487. }
  488. template <typename IteratorConnectHandler,
  489. typename Iterator, typename ConnectCondition>
  490. void operator()(IteratorConnectHandler&& handler,
  491. Iterator begin, Iterator end,
  492. const ConnectCondition& connect_condition) const
  493. {
  494. // If you get an error on the following line it means that your
  495. // handler does not meet the documented type requirements for an
  496. // IteratorConnectHandler.
  497. BOOST_ASIO_ITERATOR_CONNECT_HANDLER_CHECK(
  498. IteratorConnectHandler, handler, Iterator) type_check;
  499. non_const_lvalue<IteratorConnectHandler> handler2(handler);
  500. iterator_connect_op<Protocol, Executor, Iterator, ConnectCondition,
  501. decay_t<IteratorConnectHandler>>(socket_, begin, end,
  502. connect_condition, handler2.value)(boost::system::error_code(), 1);
  503. }
  504. private:
  505. basic_socket<Protocol, Executor>& socket_;
  506. };
  507. } // namespace detail
  508. #if !defined(GENERATING_DOCUMENTATION)
  509. template <template <typename, typename> class Associator,
  510. typename Protocol, typename Executor, typename EndpointSequence,
  511. typename ConnectCondition, typename RangeConnectHandler,
  512. typename DefaultCandidate>
  513. struct associator<Associator,
  514. detail::range_connect_op<Protocol, Executor,
  515. EndpointSequence, ConnectCondition, RangeConnectHandler>,
  516. DefaultCandidate>
  517. : Associator<RangeConnectHandler, DefaultCandidate>
  518. {
  519. static typename Associator<RangeConnectHandler, DefaultCandidate>::type get(
  520. const detail::range_connect_op<Protocol, Executor, EndpointSequence,
  521. ConnectCondition, RangeConnectHandler>& h) noexcept
  522. {
  523. return Associator<RangeConnectHandler, DefaultCandidate>::get(h.handler_);
  524. }
  525. static auto get(
  526. const detail::range_connect_op<Protocol, Executor,
  527. EndpointSequence, ConnectCondition, RangeConnectHandler>& h,
  528. const DefaultCandidate& c) noexcept
  529. -> decltype(
  530. Associator<RangeConnectHandler, DefaultCandidate>::get(
  531. h.handler_, c))
  532. {
  533. return Associator<RangeConnectHandler, DefaultCandidate>::get(
  534. h.handler_, c);
  535. }
  536. };
  537. template <template <typename, typename> class Associator,
  538. typename Protocol, typename Executor, typename Iterator,
  539. typename ConnectCondition, typename IteratorConnectHandler,
  540. typename DefaultCandidate>
  541. struct associator<Associator,
  542. detail::iterator_connect_op<Protocol, Executor,
  543. Iterator, ConnectCondition, IteratorConnectHandler>,
  544. DefaultCandidate>
  545. : Associator<IteratorConnectHandler, DefaultCandidate>
  546. {
  547. static typename Associator<IteratorConnectHandler, DefaultCandidate>::type
  548. get(const detail::iterator_connect_op<Protocol, Executor, Iterator,
  549. ConnectCondition, IteratorConnectHandler>& h) noexcept
  550. {
  551. return Associator<IteratorConnectHandler, DefaultCandidate>::get(
  552. h.handler_);
  553. }
  554. static auto get(
  555. const detail::iterator_connect_op<Protocol, Executor,
  556. Iterator, ConnectCondition, IteratorConnectHandler>& h,
  557. const DefaultCandidate& c) noexcept
  558. -> decltype(
  559. Associator<IteratorConnectHandler, DefaultCandidate>::get(
  560. h.handler_, c))
  561. {
  562. return Associator<IteratorConnectHandler, DefaultCandidate>::get(
  563. h.handler_, c);
  564. }
  565. };
  566. #endif // !defined(GENERATING_DOCUMENTATION)
  567. } // namespace asio
  568. } // namespace boost
  569. #include <boost/asio/detail/pop_options.hpp>
  570. #endif // BOOST_ASIO_IMPL_CONNECT_HPP