| File: | usr/include/boost/asio/detail/impl/socket_ops.ipp |
| Warning: | line 876, column 29 Call to blocking function 'recv' inside of critical section |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // Copyright (C) 2022-2025 Internet Systems Consortium, Inc. ("ISC") | |||
| 2 | // | |||
| 3 | // This Source Code Form is subject to the terms of the Mozilla Public | |||
| 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this | |||
| 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
| 6 | #include <config.h> | |||
| 7 | ||||
| 8 | #include <asiolink/asio_wrapper.h> | |||
| 9 | #include <asiolink/interval_timer.h> | |||
| 10 | #include <asiolink/testutils/test_tls.h> | |||
| 11 | #include <cc/data.h> | |||
| 12 | #include <cc/command_interpreter.h> | |||
| 13 | #include <tcp/mt_tcp_listener_mgr.h> | |||
| 14 | #include <tcp_test_listener.h> | |||
| 15 | #include <tcp_test_client.h> | |||
| 16 | #include <util/multi_threading_mgr.h> | |||
| 17 | #include <testutils/gtest_utils.h> | |||
| 18 | ||||
| 19 | #include <gtest/gtest.h> | |||
| 20 | ||||
| 21 | #include <thread> | |||
| 22 | #include <list> | |||
| 23 | #include <sstream> | |||
| 24 | ||||
| 25 | using namespace isc; | |||
| 26 | using namespace isc::asiolink; | |||
| 27 | using namespace isc::asiolink::test; | |||
| 28 | using namespace isc::config; | |||
| 29 | using namespace isc::data; | |||
| 30 | using namespace boost::asio::ip; | |||
| 31 | using namespace isc::tcp; | |||
| 32 | using namespace isc::util; | |||
| 33 | namespace ph = std::placeholders; | |||
| 34 | ||||
| 35 | namespace { | |||
| 36 | ||||
| 37 | /// @brief IP address to which TCP service is bound. | |||
| 38 | const std::string SERVER_ADDRESS = "127.0.0.1"; | |||
| 39 | ||||
| 40 | /// @brief Port number to which TCP service is bound. | |||
| 41 | const unsigned short SERVER_PORT = 18123; | |||
| 42 | ||||
| 43 | /// @brief Test timeout (ms). | |||
| 44 | const long TEST_TIMEOUT = 10000; | |||
| 45 | ||||
| 46 | /// @brief Test fixture class for @ref MtTcpListenerMgr. | |||
| 47 | class MtTcpListenerMgrTest : public ::testing::Test { | |||
| 48 | public: | |||
| 49 | ||||
| 50 | /// @brief Constructor. | |||
| 51 | /// | |||
| 52 | /// Starts test timer which detects timeouts, and enables multi-threading mode. | |||
| 53 | MtTcpListenerMgrTest() | |||
| 54 | : mt_listener_mgr_(), io_service_(new IOService()), test_timer_(io_service_), | |||
| 55 | run_io_service_timer_(io_service_), clients_(), num_threads_(), | |||
| 56 | num_clients_(), num_in_progress_(0), num_finished_(0), chunk_size_(0), | |||
| 57 | pause_cnt_(0), response_handler_(0) { | |||
| 58 | test_timer_.setup(std::bind(&MtTcpListenerMgrTest::timeoutHandler, this, true), | |||
| 59 | TEST_TIMEOUT, IntervalTimer::ONE_SHOT); | |||
| 60 | ||||
| 61 | // Enable multi-threading. | |||
| 62 | MultiThreadingMgr::instance().setMode(true); | |||
| 63 | } | |||
| 64 | ||||
| 65 | /// @brief Destructor. | |||
| 66 | /// | |||
| 67 | /// Removes TCP clients and disables MT. | |||
| 68 | virtual ~MtTcpListenerMgrTest() { | |||
| 69 | // Wipe out the listener. | |||
| 70 | mt_listener_mgr_.reset(); | |||
| 71 | ||||
| 72 | // Destroy all remaining clients. | |||
| 73 | for (auto const& client : clients_) { | |||
| 74 | client->close(); | |||
| 75 | } | |||
| 76 | ||||
| 77 | test_timer_.cancel(); | |||
| 78 | io_service_->stopAndPoll(); | |||
| 79 | ||||
| 80 | // Disable multi-threading. | |||
| 81 | MultiThreadingMgr::instance().setMode(false); | |||
| 82 | } | |||
| 83 | ||||
| 84 | /// @brief Replaces the test's listener with a new listener | |||
| 85 | /// | |||
| 86 | /// @param num_threads Number of threads the listener should use. | |||
| 87 | /// @param response_handler Response handler connections should use | |||
| 88 | void createMtTcpListenerMgr(size_t num_threads, | |||
| 89 | TcpTestConnection::ResponseHandler response_handler = 0) { | |||
| 90 | // Create a listener with prescribed number of threads. | |||
| 91 | ASSERT_NO_THROW_LOG(mt_listener_mgr_.reset(new MtTcpListenerMgr({ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, num_threads)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 95, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, num_threads))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 95, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, num_threads))" << " threw non-std::exception"; } } | |||
| 92 | std::bind(&MtTcpListenerMgrTest::listenerFactory, this,{ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, num_threads)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 95, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, num_threads))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 95, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, num_threads))" << " threw non-std::exception"; } } | |||
| 93 | ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6),{ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, num_threads)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 95, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, num_threads))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 95, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, num_threads))" << " threw non-std::exception"; } } | |||
| 94 | IOAddress(SERVER_ADDRESS),{ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, num_threads)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 95, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, num_threads))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 95, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, num_threads))" << " threw non-std::exception"; } } | |||
| 95 | SERVER_PORT, num_threads))){ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, num_threads)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 95, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, num_threads))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 95, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, num_threads))" << " threw non-std::exception"; } }; | |||
| 96 | ||||
| 97 | ASSERT_TRUE(mt_listener_mgr_)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 97, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_", "false", "true") .c_str()) = ::testing:: Message(); | |||
| 98 | setResponseHandler(response_handler); | |||
| 99 | } | |||
| 100 | ||||
| 101 | /// @brief Return the inner TcpTestListener's audit trail | |||
| 102 | AuditTrailPtr getAuditTrail() { | |||
| 103 | TcpListenerPtr l = mt_listener_mgr_->getTcpListener(); | |||
| 104 | if (!l) { | |||
| 105 | isc_throw(Unexpected, "Test is broken? Listener is null?")do { std::ostringstream oss__; oss__ << "Test is broken? Listener is null?" ; throw Unexpected("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 105, oss__.str().c_str()); } while (1); | |||
| 106 | } | |||
| 107 | ||||
| 108 | TcpTestListenerPtr listener = boost::dynamic_pointer_cast<TcpTestListener>( | |||
| 109 | mt_listener_mgr_->getTcpListener()); | |||
| 110 | if (!listener) { | |||
| 111 | isc_throw(Unexpected, "Test is broken? Listener is not a TcpTestListener")do { std::ostringstream oss__; oss__ << "Test is broken? Listener is not a TcpTestListener" ; throw Unexpected("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 111, oss__.str().c_str()); } while (1); | |||
| 112 | } | |||
| 113 | ||||
| 114 | return (listener->audit_trail_); | |||
| 115 | } | |||
| 116 | ||||
| 117 | /// @brief TcpListener factory for MtTcpListener to instantiate new listeners. | |||
| 118 | TcpListenerPtr listenerFactory(const asiolink::IOServicePtr& io_service, | |||
| 119 | const asiolink::IOAddress& server_address, | |||
| 120 | const unsigned short server_port, | |||
| 121 | const asiolink::TlsContextPtr& tls_context, | |||
| 122 | const TcpListener::IdleTimeout& idle_timeout, | |||
| 123 | const TcpConnectionFilterCallback& connection_filter) { | |||
| 124 | TcpTestListenerPtr listener(new TcpTestListener(io_service, | |||
| 125 | server_address, | |||
| 126 | server_port, | |||
| 127 | tls_context, | |||
| 128 | idle_timeout, | |||
| 129 | connection_filter)); | |||
| 130 | // Set the response handler the listener will pass into each connection. | |||
| 131 | listener->setResponseHandler(response_handler_); | |||
| 132 | return (listener); | |||
| 133 | } | |||
| 134 | ||||
| 135 | /// @brief Callback function each client invokes when done. | |||
| 136 | /// | |||
| 137 | /// It stops the IO service | |||
| 138 | /// | |||
| 139 | /// @param fail_on_timeout Specifies if test failure should be reported. | |||
| 140 | void clientDone() { | |||
| 141 | io_service_->stop(); | |||
| 142 | } | |||
| 143 | ||||
| 144 | /// @brief Initiates a command via a new TCP client. | |||
| 145 | /// | |||
| 146 | /// This method creates a TcpTestClient instance, adds the | |||
| 147 | /// client to the list of clients, and starts a request. | |||
| 148 | /// The client will run on the main thread and be driven by | |||
| 149 | /// the test's IOService instance. | |||
| 150 | /// | |||
| 151 | /// @param request_str String containing the request | |||
| 152 | /// to be sent. | |||
| 153 | void startRequest(const std::string& request_str) { | |||
| 154 | // Instantiate the client. | |||
| 155 | TcpTestClientPtr client(new TcpTestClient(io_service_, | |||
| 156 | std::bind(&MtTcpListenerMgrTest::clientDone, this), | |||
| 157 | TlsContextPtr(), | |||
| 158 | SERVER_ADDRESS, SERVER_PORT)); | |||
| 159 | // Add it to the list of clients. | |||
| 160 | clients_.push_back(client); | |||
| 161 | ||||
| 162 | // Start the request. Note, nothing happens until the IOService runs. | |||
| 163 | client->startRequest(request_str); | |||
| 164 | } | |||
| 165 | ||||
| 166 | /// @brief Initiates a "thread" command via a new TCP client. | |||
| 167 | /// | |||
| 168 | /// This method creates a TcpTestClient instance, adds the | |||
| 169 | /// client to the list of clients, and starts a request based | |||
| 170 | /// on the given command. The client will run on the main | |||
| 171 | /// thread and be driven by the test's IOService instance. | |||
| 172 | /// | |||
| 173 | /// The command has a single argument, "client-ptr". The function creates | |||
| 174 | /// the value for this argument from the pointer address of client instance | |||
| 175 | /// it creates. This argument should be echoed back in the response, along | |||
| 176 | /// with the thread-id of the MtTcpListener thread which handled the | |||
| 177 | /// command. The response body should look this: | |||
| 178 | /// | |||
| 179 | /// ``` | |||
| 180 | /// [ { "arguments": { "client-ptr": "xxxxx", "thread-id": "zzzzz" }, "result": 0} ] | |||
| 181 | /// ``` | |||
| 182 | void startThreadCommand(std::string request_str) { | |||
| 183 | // Create a new client. | |||
| 184 | TcpTestClientPtr client(new TcpTestClient(io_service_, | |||
| 185 | std::bind(&MtTcpListenerMgrTest::clientDone, this), | |||
| 186 | TlsContextPtr(), | |||
| 187 | SERVER_ADDRESS, SERVER_PORT)); | |||
| 188 | ||||
| 189 | // Construct the "thread" command post including the argument, | |||
| 190 | // "client-ptr", whose value is the stringified pointer to the | |||
| 191 | // newly created client. | |||
| 192 | std::stringstream request_body; | |||
| 193 | request_body << "{\"command\": \"thread\", \"arguments\": { \"client-ptr\": \"" | |||
| 194 | << client << "\", \"request\": \"" << request_str << "\" } }"; | |||
| 195 | ||||
| 196 | // Add it to the list of clients. | |||
| 197 | clients_.push_back(client); | |||
| 198 | ||||
| 199 | // Start the request. Note, nothing happens until the IOService runs. | |||
| 200 | ASSERT_NO_THROW_LOG(client->startRequest(request_body.str())){ try { client->startRequest(request_body.str()); } catch ( const std::exception& ex) { return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 200, "Failed") = ::testing::Message() << "client->startRequest(request_body.str())" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 200, "Failed") = ::testing::Message() << "client->startRequest(request_body.str())" << " threw non-std::exception"; } }; | |||
| 201 | } | |||
| 202 | ||||
| 203 | /// @brief Callback function invoke upon test timeout. | |||
| 204 | /// | |||
| 205 | /// It stops the IO service and reports test timeout. | |||
| 206 | /// | |||
| 207 | /// @param fail_on_timeout Specifies if test failure should be reported. | |||
| 208 | void timeoutHandler(const bool fail_on_timeout) { | |||
| 209 | if (fail_on_timeout) { | |||
| 210 | ADD_FAILURE()::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 210, "Failed") = ::testing::Message() << "Timeout occurred while running the test!"; | |||
| 211 | } | |||
| 212 | io_service_->stop(); | |||
| 213 | } | |||
| 214 | ||||
| 215 | /// @brief Runs IO service with optional timeout. | |||
| 216 | /// | |||
| 217 | /// We iterate over calls to asio::io_context.run(), until | |||
| 218 | /// all the clients have completed their requests. We do it this way | |||
| 219 | /// because the test clients stop the io_service when they're | |||
| 220 | /// through with a request. | |||
| 221 | /// | |||
| 222 | /// @param request_limit Desired number of requests the function should wait | |||
| 223 | /// to be processed before returning. | |||
| 224 | void runIOService(size_t request_limit = 0) { | |||
| 225 | if (!request_limit) { | |||
| 226 | request_limit = clients_.size(); | |||
| 227 | } | |||
| 228 | ||||
| 229 | // Loop until the clients are done, an error occurs, or the time runs out. | |||
| 230 | size_t num_done = 0; | |||
| 231 | while (num_done != request_limit) { | |||
| 232 | io_service_->stop(); | |||
| 233 | io_service_->restart(); | |||
| 234 | ||||
| 235 | // Run until a client stops the service. | |||
| 236 | io_service_->run(); | |||
| 237 | ||||
| 238 | // If all the clients are done receiving, the test is done. | |||
| 239 | num_done = 0; | |||
| 240 | for (auto const& client : clients_) { | |||
| 241 | if (client->receiveDone()) { | |||
| 242 | ++num_done; | |||
| 243 | } | |||
| 244 | } | |||
| 245 | } | |||
| 246 | } | |||
| 247 | ||||
| 248 | /// @brief Set the response handler use by each connection. | |||
| 249 | /// | |||
| 250 | /// Sets the response handler invoked by requestReceived. | |||
| 251 | /// | |||
| 252 | /// @param response_handler Handler function to invoke | |||
| 253 | void setResponseHandler(TcpTestConnection::ResponseHandler response_handler) { | |||
| 254 | response_handler_ = response_handler; | |||
| 255 | }; | |||
| 256 | ||||
| 257 | /// @brief Response handler for the 'thread' command. | |||
| 258 | /// | |||
| 259 | /// @param request JSON text containing thread command and arguments | |||
| 260 | /// which should contain one string element, "client-ptr", whose value is | |||
| 261 | /// the stringified pointer to the client that issued the command. | |||
| 262 | /// | |||
| 263 | /// @return Returns JSON text containing the response which should include | |||
| 264 | /// a string value 'thread-id': <thread id> | |||
| 265 | std::string synchronizedCommandHandler(const std::string& request) { | |||
| 266 | // If the number of in progress commands is less than the number | |||
| 267 | // of threads, then wait here until we're notified. Otherwise, | |||
| 268 | // notify everyone and finish. The idea is to force each thread | |||
| 269 | // to handle the same number of requests over the course of the | |||
| 270 | // test, making verification reliable. | |||
| 271 | { | |||
| 272 | std::unique_lock<std::mutex> lck(mutex_); | |||
| 273 | ++num_in_progress_; | |||
| 274 | if (num_in_progress_ == chunk_size_) { | |||
| 275 | num_finished_ = 0; | |||
| 276 | cv_.notify_all(); | |||
| 277 | } else { | |||
| 278 | bool ret = cv_.wait_for(lck, std::chrono::seconds(10), | |||
| 279 | [&]() { return (num_in_progress_ == chunk_size_); }); | |||
| 280 | if (!ret) { | |||
| 281 | ADD_FAILURE()::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 281, "Failed") = ::testing::Message() << "clients failed to start work"; | |||
| 282 | } | |||
| 283 | } | |||
| 284 | } | |||
| 285 | ||||
| 286 | // Create the map of response arguments. | |||
| 287 | ElementPtr arguments = Element::createMap(); | |||
| 288 | ||||
| 289 | // Parse the command. | |||
| 290 | ConstElementPtr command = Element::fromJSON(request); | |||
| 291 | ConstElementPtr command_arguments; | |||
| 292 | std::string command_str = parseCommand(command_arguments, command); | |||
| 293 | ||||
| 294 | // First we echo the client-ptr command argument. | |||
| 295 | ConstElementPtr client_ptr = command_arguments->get("client-ptr"); | |||
| 296 | if (!client_ptr) { | |||
| 297 | return (createAnswerString(CONTROL_RESULT_ERROR, "missing client-ptr")); | |||
| 298 | } | |||
| 299 | ||||
| 300 | arguments->set("client-ptr", client_ptr); | |||
| 301 | ||||
| 302 | // Now we add the thread-id. | |||
| 303 | std::stringstream ss; | |||
| 304 | ss << std::this_thread::get_id(); | |||
| 305 | arguments->set("thread-id", Element::create(ss.str())); | |||
| 306 | arguments->set("sign-off", Element::create("good bye")); | |||
| 307 | ||||
| 308 | { | |||
| 309 | std::unique_lock<std::mutex> lck(mutex_); | |||
| 310 | num_finished_++; | |||
| 311 | if (num_finished_ == chunk_size_) { | |||
| 312 | // We're all done, notify the others and finish. | |||
| 313 | num_in_progress_ = 0; | |||
| 314 | cv_.notify_all(); | |||
| 315 | } else { | |||
| 316 | // I'm done but others aren't wait here. | |||
| 317 | bool ret = cv_.wait_for(lck, std::chrono::seconds(10), | |||
| 318 | [&]() { return (num_finished_ == chunk_size_); }); | |||
| 319 | if (!ret) { | |||
| 320 | ADD_FAILURE()::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 320, "Failed") = ::testing::Message() << "clients failed to finish work"; | |||
| 321 | } | |||
| 322 | } | |||
| 323 | } | |||
| 324 | ||||
| 325 | EXPECT_THROW(mt_listener_mgr_->start(), InvalidOperation)switch (0) case 0: default: if (::testing::internal::TrueWithString gtest_msg{}) { bool gtest_caught_expected = false; try { if ( ::testing::internal::AlwaysTrue()) { mt_listener_mgr_->start (); } else static_assert(true, ""); } catch (InvalidOperation const&) { gtest_caught_expected = true; } catch (typename std::conditional< std::is_same<typename std::remove_cv <typename std::remove_reference< InvalidOperation>:: type>::type, std::exception>::value, const ::testing::internal ::NeverThrown&, const std::exception&>::type e) { gtest_msg .value = "Expected: " "mt_listener_mgr_->start()" " throws an exception of type " "InvalidOperation" ".\n Actual: it throws "; gtest_msg.value += ::testing::internal::GetTypeName(typeid(e)); gtest_msg.value += " with description \""; gtest_msg.value += e.what(); gtest_msg .value += "\"."; goto gtest_label_testthrow_325; } catch (... ) { gtest_msg.value = "Expected: " "mt_listener_mgr_->start()" " throws an exception of type " "InvalidOperation" ".\n Actual: it throws a different type." ; goto gtest_label_testthrow_325; } if (!gtest_caught_expected ) { gtest_msg.value = "Expected: " "mt_listener_mgr_->start()" " throws an exception of type " "InvalidOperation" ".\n Actual: it throws nothing." ; goto gtest_label_testthrow_325; } } else gtest_label_testthrow_325 : ::testing::internal::AssertHelper(::testing::TestPartResult ::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 325, gtest_msg.value.c_str()) = ::testing::Message(); | |||
| 326 | EXPECT_THROW(mt_listener_mgr_->pause(), MultiThreadingInvalidOperation)switch (0) case 0: default: if (::testing::internal::TrueWithString gtest_msg{}) { bool gtest_caught_expected = false; try { if ( ::testing::internal::AlwaysTrue()) { mt_listener_mgr_->pause (); } else static_assert(true, ""); } catch (MultiThreadingInvalidOperation const&) { gtest_caught_expected = true; } catch (typename std::conditional< std::is_same<typename std::remove_cv <typename std::remove_reference< MultiThreadingInvalidOperation >::type>::type, std::exception>::value, const ::testing ::internal::NeverThrown&, const std::exception&>:: type e) { gtest_msg.value = "Expected: " "mt_listener_mgr_->pause()" " throws an exception of type " "MultiThreadingInvalidOperation" ".\n Actual: it throws "; gtest_msg.value += ::testing::internal ::GetTypeName(typeid(e)); gtest_msg.value += " with description \"" ; gtest_msg.value += e.what(); gtest_msg.value += "\"."; goto gtest_label_testthrow_326; } catch (...) { gtest_msg.value = "Expected: " "mt_listener_mgr_->pause()" " throws an exception of type " "MultiThreadingInvalidOperation" ".\n Actual: it throws a different type." ; goto gtest_label_testthrow_326; } if (!gtest_caught_expected ) { gtest_msg.value = "Expected: " "mt_listener_mgr_->pause()" " throws an exception of type " "MultiThreadingInvalidOperation" ".\n Actual: it throws nothing."; goto gtest_label_testthrow_326 ; } } else gtest_label_testthrow_326 : ::testing::internal::AssertHelper (::testing::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 326, gtest_msg.value.c_str()) = ::testing::Message(); | |||
| 327 | EXPECT_THROW(mt_listener_mgr_->resume(), MultiThreadingInvalidOperation)switch (0) case 0: default: if (::testing::internal::TrueWithString gtest_msg{}) { bool gtest_caught_expected = false; try { if ( ::testing::internal::AlwaysTrue()) { mt_listener_mgr_->resume (); } else static_assert(true, ""); } catch (MultiThreadingInvalidOperation const&) { gtest_caught_expected = true; } catch (typename std::conditional< std::is_same<typename std::remove_cv <typename std::remove_reference< MultiThreadingInvalidOperation >::type>::type, std::exception>::value, const ::testing ::internal::NeverThrown&, const std::exception&>:: type e) { gtest_msg.value = "Expected: " "mt_listener_mgr_->resume()" " throws an exception of type " "MultiThreadingInvalidOperation" ".\n Actual: it throws "; gtest_msg.value += ::testing::internal ::GetTypeName(typeid(e)); gtest_msg.value += " with description \"" ; gtest_msg.value += e.what(); gtest_msg.value += "\"."; goto gtest_label_testthrow_327; } catch (...) { gtest_msg.value = "Expected: " "mt_listener_mgr_->resume()" " throws an exception of type " "MultiThreadingInvalidOperation" ".\n Actual: it throws a different type." ; goto gtest_label_testthrow_327; } if (!gtest_caught_expected ) { gtest_msg.value = "Expected: " "mt_listener_mgr_->resume()" " throws an exception of type " "MultiThreadingInvalidOperation" ".\n Actual: it throws nothing."; goto gtest_label_testthrow_327 ; } } else gtest_label_testthrow_327 : ::testing::internal::AssertHelper (::testing::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 327, gtest_msg.value.c_str()) = ::testing::Message(); | |||
| 328 | EXPECT_THROW(mt_listener_mgr_->stop(), MultiThreadingInvalidOperation)switch (0) case 0: default: if (::testing::internal::TrueWithString gtest_msg{}) { bool gtest_caught_expected = false; try { if ( ::testing::internal::AlwaysTrue()) { mt_listener_mgr_->stop (); } else static_assert(true, ""); } catch (MultiThreadingInvalidOperation const&) { gtest_caught_expected = true; } catch (typename std::conditional< std::is_same<typename std::remove_cv <typename std::remove_reference< MultiThreadingInvalidOperation >::type>::type, std::exception>::value, const ::testing ::internal::NeverThrown&, const std::exception&>:: type e) { gtest_msg.value = "Expected: " "mt_listener_mgr_->stop()" " throws an exception of type " "MultiThreadingInvalidOperation" ".\n Actual: it throws "; gtest_msg.value += ::testing::internal ::GetTypeName(typeid(e)); gtest_msg.value += " with description \"" ; gtest_msg.value += e.what(); gtest_msg.value += "\"."; goto gtest_label_testthrow_328; } catch (...) { gtest_msg.value = "Expected: " "mt_listener_mgr_->stop()" " throws an exception of type " "MultiThreadingInvalidOperation" ".\n Actual: it throws a different type." ; goto gtest_label_testthrow_328; } if (!gtest_caught_expected ) { gtest_msg.value = "Expected: " "mt_listener_mgr_->stop()" " throws an exception of type " "MultiThreadingInvalidOperation" ".\n Actual: it throws nothing."; goto gtest_label_testthrow_328 ; } } else gtest_label_testthrow_328 : ::testing::internal::AssertHelper (::testing::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 328, gtest_msg.value.c_str()) = ::testing::Message(); | |||
| 329 | ||||
| 330 | // We're done, ship it! | |||
| 331 | std::string str = createAnswerString(CONTROL_RESULT_SUCCESS, arguments); | |||
| 332 | return (str); | |||
| 333 | } | |||
| 334 | ||||
| 335 | /// @brief Create a response string of JSON | |||
| 336 | /// | |||
| 337 | /// @param status_code Indicates outcome of the command | |||
| 338 | /// @param arguments Element tree of response arguments | |||
| 339 | /// | |||
| 340 | /// @return JSON text containing the response | |||
| 341 | std::string createAnswerString(const int status_code, std::string text) { | |||
| 342 | ConstElementPtr answer = createAnswer(status_code, text); | |||
| 343 | std::stringstream os; | |||
| 344 | answer->toJSON(os); | |||
| 345 | return(os.str()); | |||
| 346 | } | |||
| 347 | ||||
| 348 | /// @brief Create a response string of JSON | |||
| 349 | /// | |||
| 350 | /// @param status_code Indicates outcome of the command | |||
| 351 | /// @param arguments Element tree of response arguments | |||
| 352 | /// | |||
| 353 | /// @return JSON text containing the response | |||
| 354 | std::string createAnswerString(const int status_code, | |||
| 355 | ConstElementPtr arguments) { | |||
| 356 | ConstElementPtr answer = createAnswer(status_code, arguments); | |||
| 357 | std::stringstream os; | |||
| 358 | answer->toJSON(os); | |||
| 359 | return(os.str()); | |||
| 360 | } | |||
| 361 | ||||
| 362 | /// @brief Simple response handler for the 'thread' command. | |||
| 363 | /// | |||
| 364 | /// @param command_name Command name, i.e. 'thread'. | |||
| 365 | /// @param command_arguments Command arguments should contain | |||
| 366 | /// one string element, "client-ptr", whose value is the stringified | |||
| 367 | /// pointer to the client that issued the command. | |||
| 368 | /// | |||
| 369 | /// @return Returns response with map of arguments containing | |||
| 370 | /// a string value 'thread-id': <thread id> | |||
| 371 | std::string simpleCommandHandler(const std::string& request) { | |||
| 372 | // Create the map of response arguments. | |||
| 373 | ElementPtr arguments = Element::createMap(); | |||
| 374 | ||||
| 375 | // Parse the command. | |||
| 376 | ConstElementPtr command = Element::fromJSON(request); | |||
| 377 | ConstElementPtr command_arguments; | |||
| 378 | std::string command_str = parseCommand(command_arguments, command); | |||
| 379 | ||||
| 380 | // First we echo the client-ptr command argument. | |||
| 381 | ConstElementPtr client_ptr = command_arguments->get("client-ptr"); | |||
| 382 | if (!client_ptr) { | |||
| 383 | return (createAnswerString(CONTROL_RESULT_ERROR, "missing client-ptr")); | |||
| 384 | } | |||
| 385 | ||||
| 386 | arguments->set("client-ptr", client_ptr); | |||
| 387 | ||||
| 388 | // Now we add the thread-id. | |||
| 389 | std::stringstream ss; | |||
| 390 | ss << std::this_thread::get_id(); | |||
| 391 | arguments->set("thread-id", Element::create(ss.str())); | |||
| 392 | arguments->set("sign-off", Element::create("good bye")); | |||
| 393 | ||||
| 394 | // We're done, ship it! | |||
| 395 | std::string str = createAnswerString(CONTROL_RESULT_SUCCESS, arguments); | |||
| 396 | return (str); | |||
| 397 | } | |||
| 398 | ||||
| 399 | /// @brief Submits one or more thread commands to a MtTcpListener. | |||
| 400 | /// | |||
| 401 | /// This function command will create a MtTcpListener | |||
| 402 | /// with the given number of threads, initiates the given | |||
| 403 | /// number of clients, each requesting the "thread" command, | |||
| 404 | /// and then iteratively runs the test's IOService until all | |||
| 405 | /// the clients have received their responses or an error occurs. | |||
| 406 | /// | |||
| 407 | /// It requires that the number of clients, when greater than the | |||
| 408 | /// number of threads, be a multiple of the number of threads. The | |||
| 409 | /// thread command handler is structured in such a way as to ensure | |||
| 410 | /// (we hope) that each thread handles the same number of commands. | |||
| 411 | /// | |||
| 412 | /// @param num_threads - the number of threads the MtTcpListener | |||
| 413 | /// should use. Must be greater than 0. | |||
| 414 | /// @param num_clients - the number of clients that should issue the | |||
| 415 | /// thread command. Each client is used to carry out a single thread | |||
| 416 | /// command request. Must be greater than 0 and a multiple of num_threads | |||
| 417 | /// if it is greater than num_threads. | |||
| 418 | void threadListenAndRespond(size_t num_threads, size_t num_clients) { | |||
| 419 | // First we makes sure the parameter rules apply. | |||
| 420 | ASSERT_TRUE(num_threads)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(num_threads)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 420, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "num_threads", "false", "true") .c_str()) = ::testing::Message (); | |||
| 421 | ASSERT_TRUE(num_clients)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(num_clients)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 421, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "num_clients", "false", "true") .c_str()) = ::testing::Message (); | |||
| 422 | ASSERT_TRUE((num_clients < num_threads) || (num_clients % num_threads == 0))switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult((num_clients < num_threads ) || (num_clients % num_threads == 0))) ; else return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 422, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "(num_clients < num_threads) || (num_clients % num_threads == 0)" , "false", "true") .c_str()) = ::testing::Message(); | |||
| 423 | ||||
| 424 | num_threads_ = num_threads; | |||
| 425 | num_clients_ = num_clients; | |||
| 426 | chunk_size_ = num_threads_; | |||
| 427 | if (num_clients_ < chunk_size_) { | |||
| 428 | chunk_size_ = num_clients_; | |||
| 429 | } | |||
| 430 | ||||
| 431 | // Create an MtTcpListenerMgr with prescribed number of threads. | |||
| 432 | createMtTcpListenerMgr(num_threads, | |||
| 433 | std::bind(&MtTcpListenerMgrTest::synchronizedCommandHandler, | |||
| 434 | this, ph::_1)); | |||
| 435 | ||||
| 436 | // Start it and verify it is running. | |||
| 437 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->start()){ try { mt_listener_mgr_->start(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 437, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 437, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 438 | ASSERT_TRUE(mt_listener_mgr_->isRunning())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isRunning())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 438, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 439 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), num_threads)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "num_threads", mt_listener_mgr_->getThreadCount(), num_threads ))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult ::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 439, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 440 | ||||
| 441 | // Maps the number of clients served by a given thread-id. | |||
| 442 | std::map<std::string, int> clients_per_thread; | |||
| 443 | ||||
| 444 | // Initiate the prescribed number of command requests. | |||
| 445 | num_in_progress_ = 0; | |||
| 446 | while (clients_.size() < num_clients) { | |||
| 447 | ASSERT_NO_THROW_LOG(startThreadCommand("I am done")){ try { startThreadCommand("I am done"); } catch (const std:: exception& ex) { return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 447, "Failed") = ::testing::Message() << "startThreadCommand(\"I am done\")" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 447, "Failed") = ::testing::Message() << "startThreadCommand(\"I am done\")" << " threw non-std::exception"; } }; | |||
| 448 | } | |||
| 449 | ||||
| 450 | // Now we run the client-side IOService until all requests are done, | |||
| 451 | // errors occur or the test times out. | |||
| 452 | ASSERT_NO_FATAL_FAILURE(runIOService())switch (0) case 0: default: if (::testing::internal::AlwaysTrue ()) { const ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker ; if (::testing::internal::AlwaysTrue()) { runIOService(); } else static_assert(true, ""); if (gtest_fatal_failure_checker.has_new_fatal_failure ()) { goto gtest_label_testnofatal_452; } } else gtest_label_testnofatal_452 : return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 452, "Expected: " "runIOService()" " doesn't generate new fatal " "failures in the current thread.\n" " Actual: it does.") = :: testing::Message(); | |||
| 453 | ||||
| 454 | // Stop the listener and then verify it has stopped. | |||
| 455 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->stop()){ try { mt_listener_mgr_->stop(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 455, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 455, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw non-std::exception"; } }; | |||
| 456 | ASSERT_TRUE(mt_listener_mgr_->isStopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isStopped())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 456, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 457 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0U", mt_listener_mgr_->getThreadCount(), 0U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 457, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 458 | ||||
| 459 | // Iterate over the clients, checking their outcomes. | |||
| 460 | size_t total_responses = 0; | |||
| 461 | for (auto const& client : clients_) { | |||
| 462 | // Client should have completed its receive successfully. | |||
| 463 | ASSERT_TRUE(client->receiveDone())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(client->receiveDone ())) ; else return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 463, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "client->receiveDone()", "false", "true") .c_str()) = :: testing::Message(); | |||
| 464 | ||||
| 465 | // Now we walk the element tree to get the response data. It should look | |||
| 466 | // this: | |||
| 467 | // | |||
| 468 | // { | |||
| 469 | // "arguments": { "client-ptr": "xxxxx", | |||
| 470 | // "thread-id": "zzzzz" }, | |||
| 471 | // "result": 0 | |||
| 472 | // ] | |||
| 473 | // | |||
| 474 | // We expect 1 response. | |||
| 475 | auto responses = client->getResponses(); | |||
| 476 | ASSERT_EQ(responses.size(), 1U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("responses.size()" , "1U", responses.size(), 1U))) ; else return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 476, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 477 | ||||
| 478 | // First we turn it into an Element tree. | |||
| 479 | ConstElementPtr answer; | |||
| 480 | ASSERT_NO_THROW_LOG(answer = Element::fromJSON(responses.front())){ try { answer = Element::fromJSON(responses.front()); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 480, "Failed") = ::testing::Message() << "answer = Element::fromJSON(responses.front())" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 480, "Failed") = ::testing::Message() << "answer = Element::fromJSON(responses.front())" << " threw non-std::exception"; } }; | |||
| 481 | ||||
| 482 | // Answer should be a map containing "arguments" and "results". | |||
| 483 | ASSERT_EQ(answer->getType(), Element::map)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("answer->getType()" , "Element::map", answer->getType(), Element::map))) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 483, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 484 | ||||
| 485 | // "result" should be 0. | |||
| 486 | ConstElementPtr result = answer->get("result"); | |||
| 487 | ASSERT_TRUE(result)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(result)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 487, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "result", "false", "true") .c_str()) = ::testing::Message(); | |||
| 488 | ASSERT_EQ(result->getType(), Element::integer)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("result->getType()" , "Element::integer", result->getType(), Element::integer) )) ; else return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 488, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 489 | ASSERT_EQ(result->intValue(), 0)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("result->intValue()" , "0", result->intValue(), 0))) ; else return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 489, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 490 | ||||
| 491 | // "arguments" is a map containing "client-ptr" and "thread-id". | |||
| 492 | ConstElementPtr arguments = answer->get("arguments"); | |||
| 493 | ASSERT_TRUE(arguments)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(arguments)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 493, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "arguments", "false", "true") .c_str()) = ::testing::Message (); | |||
| 494 | ASSERT_EQ(arguments->getType(), Element::map)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("arguments->getType()" , "Element::map", arguments->getType(), Element::map))) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 494, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 495 | ||||
| 496 | // "client-ptr" is a string. | |||
| 497 | ConstElementPtr client_ptr = arguments->get("client-ptr"); | |||
| 498 | ASSERT_TRUE(client_ptr)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(client_ptr)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 498, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "client_ptr", "false", "true") .c_str()) = ::testing::Message (); | |||
| 499 | ASSERT_EQ(client_ptr->getType(), Element::string)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("client_ptr->getType()" , "Element::string", client_ptr->getType(), Element::string ))) ; else return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 499, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 500 | ||||
| 501 | // "thread-id" is a string. | |||
| 502 | ConstElementPtr thread_id = arguments->get("thread-id"); | |||
| 503 | ASSERT_TRUE(thread_id)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(thread_id)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 503, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "thread_id", "false", "true") .c_str()) = ::testing::Message (); | |||
| 504 | ASSERT_EQ(thread_id->getType(), Element::string)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("thread_id->getType()" , "Element::string", thread_id->getType(), Element::string ))) ; else return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 504, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 505 | std::string thread_id_str = thread_id->stringValue(); | |||
| 506 | ||||
| 507 | // Make sure the response received was for this client. | |||
| 508 | std::stringstream ss; | |||
| 509 | ss << client; | |||
| 510 | ASSERT_EQ(client_ptr->stringValue(), ss.str())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("client_ptr->stringValue()" , "ss.str()", client_ptr->stringValue(), ss.str()))) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 510, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 511 | ||||
| 512 | // Bump the client count for the given thread-id. | |||
| 513 | auto it = clients_per_thread.find(thread_id_str); | |||
| 514 | if (it != clients_per_thread.end()) { | |||
| 515 | clients_per_thread[thread_id_str] = it->second + 1; | |||
| 516 | } else { | |||
| 517 | clients_per_thread[thread_id_str] = 1; | |||
| 518 | } | |||
| 519 | ++total_responses; | |||
| 520 | } | |||
| 521 | ||||
| 522 | // We should have responses for all our clients. | |||
| 523 | EXPECT_EQ(total_responses, num_clients)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("total_responses" , "num_clients", total_responses, num_clients))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 523, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 524 | ||||
| 525 | // Verify we have the expected number of entries in our map. | |||
| 526 | size_t expected_thread_count = (num_clients < num_threads ? | |||
| 527 | num_clients : num_threads); | |||
| 528 | ||||
| 529 | ASSERT_EQ(clients_per_thread.size(), expected_thread_count)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("clients_per_thread.size()" , "expected_thread_count", clients_per_thread.size(), expected_thread_count ))) ; else return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 529, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 530 | ||||
| 531 | // Each thread-id ought to have handled the same number of clients. | |||
| 532 | for (auto const& it : clients_per_thread) { | |||
| 533 | EXPECT_EQ(it.second,switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("it.second" , "static_cast<long>(num_clients / clients_per_thread.size())" , it.second, static_cast<long>(num_clients / clients_per_thread .size())))) ; else ::testing::internal::AssertHelper(::testing ::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 534, gtest_ar.failure_message()) = ::testing::Message() | |||
| 534 | static_cast<long>(num_clients / clients_per_thread.size()))switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("it.second" , "static_cast<long>(num_clients / clients_per_thread.size())" , it.second, static_cast<long>(num_clients / clients_per_thread .size())))) ; else ::testing::internal::AssertHelper(::testing ::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 534, gtest_ar.failure_message()) = ::testing::Message() | |||
| 535 | << "thread-id: " << it.first | |||
| 536 | << ", clients: " << it.second << std::endl; | |||
| 537 | } | |||
| 538 | } | |||
| 539 | ||||
| 540 | /// @brief Pauses and resumes a MtTcpListener while it processes command | |||
| 541 | /// requests. | |||
| 542 | /// | |||
| 543 | /// This function command will create a MtTcpListenerMgr | |||
| 544 | /// with the given number of threads, initiates the given | |||
| 545 | /// number of clients, each requesting the "thread" command, | |||
| 546 | /// and then iteratively runs the test's IOService until all | |||
| 547 | /// the clients have received their responses or an error occurs. | |||
| 548 | /// It will pause and resume the listener at intervals governed | |||
| 549 | /// by the given number of pauses. | |||
| 550 | /// | |||
| 551 | /// @param num_threads - the number of threads the MtTcpListener | |||
| 552 | /// should use. Must be greater than 0. | |||
| 553 | /// @param num_clients - the number of clients that should issue the | |||
| 554 | /// thread command. Each client is used to carry out a single thread | |||
| 555 | /// command request. Must be greater than 0. | |||
| 556 | /// @param num_pauses Desired number of times the listener should be | |||
| 557 | /// paused during the test. Must be greater than 0. | |||
| 558 | void workPauseAndResume(size_t num_threads, size_t num_clients, | |||
| 559 | size_t num_pauses) { | |||
| 560 | // First we makes sure the parameter rules apply. | |||
| 561 | ASSERT_TRUE(num_threads)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(num_threads)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 561, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "num_threads", "false", "true") .c_str()) = ::testing::Message (); | |||
| ||||
| 562 | ASSERT_TRUE(num_clients)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(num_clients)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 562, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "num_clients", "false", "true") .c_str()) = ::testing::Message (); | |||
| 563 | ASSERT_TRUE(num_pauses)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(num_pauses)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 563, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "num_pauses", "false", "true") .c_str()) = ::testing::Message (); | |||
| 564 | num_threads_ = num_threads; | |||
| 565 | num_clients_ = num_clients; | |||
| 566 | ||||
| 567 | // Create an MtTcpListenerMgr with prescribed number of threads and the | |||
| 568 | // simple handler. | |||
| 569 | createMtTcpListenerMgr(num_threads, | |||
| 570 | std::bind(&MtTcpListenerMgrTest::simpleCommandHandler, | |||
| 571 | this, ph::_1)); | |||
| 572 | ||||
| 573 | ASSERT_TRUE(mt_listener_mgr_)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 573, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_", "false", "true") .c_str()) = ::testing:: Message(); | |||
| 574 | ||||
| 575 | // Start it and verify it is running. | |||
| 576 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->start()){ try { mt_listener_mgr_->start(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 576, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 576, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 577 | ASSERT_TRUE(mt_listener_mgr_->isRunning())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isRunning())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 577, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 578 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), num_threads)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "num_threads", mt_listener_mgr_->getThreadCount(), num_threads ))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult ::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 578, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 579 | ||||
| 580 | // Initiate the prescribed number of command requests. | |||
| 581 | num_in_progress_ = 0; | |||
| 582 | while (clients_.size() < num_clients) { | |||
| 583 | ASSERT_NO_THROW_LOG(startThreadCommand("I am done")){ try { startThreadCommand("I am done"); } catch (const std:: exception& ex) { return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 583, "Failed") = ::testing::Message() << "startThreadCommand(\"I am done\")" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 583, "Failed") = ::testing::Message() << "startThreadCommand(\"I am done\")" << " threw non-std::exception"; } }; | |||
| 584 | } | |||
| 585 | ||||
| 586 | // Now we run the client-side IOService until all requests are done, | |||
| 587 | // errors occur or the test times out. We'll pause and resume the | |||
| 588 | // number of times given by num_pauses. | |||
| 589 | size_t num_done = 0; | |||
| 590 | size_t total_requests = clients_.size(); | |||
| 591 | while (num_done < total_requests) { | |||
| 592 | // Calculate how many more requests to process before we pause again. | |||
| 593 | // We divide the number of outstanding requests by the number of pauses | |||
| 594 | // and stop after we've done at least that many more requests. | |||
| 595 | size_t request_limit = (pause_cnt_ < num_pauses ? | |||
| 596 | (num_done + ((total_requests - num_done) / num_pauses)) | |||
| 597 | : total_requests); | |||
| 598 | ||||
| 599 | // Run test IOService until we hit the limit. | |||
| 600 | runIOService(request_limit); | |||
| 601 | ||||
| 602 | // If we've done all our pauses we should be through. | |||
| 603 | if (pause_cnt_ == num_pauses) { | |||
| 604 | break; | |||
| 605 | } | |||
| 606 | ||||
| 607 | // Pause the client. | |||
| 608 | ASSERT_NO_THROW(mt_listener_mgr_->pause())switch (0) case 0: default: if (::testing::internal::TrueWithString gtest_msg{}) { try { if (::testing::internal::AlwaysTrue()) { mt_listener_mgr_->pause(); } else static_assert(true, "") ; } catch (std::exception const& e) { gtest_msg.value = "it throws " ; gtest_msg.value += ::testing::internal::GetTypeName(typeid( e)); gtest_msg.value += " with description \""; gtest_msg.value += e.what(); gtest_msg.value += "\"."; goto gtest_label_testnothrow_608 ; } catch (...) { gtest_msg.value = "it throws."; goto gtest_label_testnothrow_608 ; } } else gtest_label_testnothrow_608 : return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 608, ("Expected: " "mt_listener_mgr_->pause()" " doesn't throw an exception.\n" " Actual: " + gtest_msg.value) .c_str()) = ::testing::Message (); | |||
| 609 | ASSERT_TRUE(mt_listener_mgr_->isPaused())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isPaused())) ; else return ::testing::internal::AssertHelper( ::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 609, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isPaused()", "false", "true") .c_str( )) = ::testing::Message(); | |||
| 610 | ++pause_cnt_; | |||
| 611 | ||||
| 612 | // Check our progress. | |||
| 613 | num_done = 0; | |||
| 614 | for (auto const& client : clients_) { | |||
| 615 | if (client->receiveDone()) { | |||
| 616 | ++num_done; | |||
| 617 | } | |||
| 618 | } | |||
| 619 | ||||
| 620 | // We should completed at least as many as our | |||
| 621 | // target limit. | |||
| 622 | ASSERT_GE(num_done, request_limit)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::CmpHelperGE("num_done", "request_limit" , num_done, request_limit))) ; else return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 622, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 623 | ||||
| 624 | // Resume the listener. | |||
| 625 | ASSERT_NO_THROW(mt_listener_mgr_->resume())switch (0) case 0: default: if (::testing::internal::TrueWithString gtest_msg{}) { try { if (::testing::internal::AlwaysTrue()) { mt_listener_mgr_->resume(); } else static_assert(true, "" ); } catch (std::exception const& e) { gtest_msg.value = "it throws " ; gtest_msg.value += ::testing::internal::GetTypeName(typeid( e)); gtest_msg.value += " with description \""; gtest_msg.value += e.what(); gtest_msg.value += "\"."; goto gtest_label_testnothrow_625 ; } catch (...) { gtest_msg.value = "it throws."; goto gtest_label_testnothrow_625 ; } } else gtest_label_testnothrow_625 : return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 625, ("Expected: " "mt_listener_mgr_->resume()" " doesn't throw an exception.\n" " Actual: " + gtest_msg.value) .c_str()) = ::testing::Message (); | |||
| 626 | ASSERT_TRUE(mt_listener_mgr_->isRunning())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isRunning())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 626, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 627 | } | |||
| 628 | ||||
| 629 | // Stop the listener and then verify it has stopped. | |||
| 630 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->stop()){ try { mt_listener_mgr_->stop(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 630, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 630, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw non-std::exception"; } }; | |||
| 631 | ASSERT_TRUE(mt_listener_mgr_->isStopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isStopped())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 631, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 632 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0U", mt_listener_mgr_->getThreadCount(), 0U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 632, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 633 | ||||
| 634 | // Iterate over the clients, checking their outcomes. | |||
| 635 | size_t total_responses = 0; | |||
| 636 | for (auto const& client : clients_) { | |||
| 637 | // Client should have completed its receive successfully. | |||
| 638 | ASSERT_TRUE(client->receiveDone())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(client->receiveDone ())) ; else return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 638, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "client->receiveDone()", "false", "true") .c_str()) = :: testing::Message(); | |||
| 639 | ||||
| 640 | // Now we walk the element tree to get the response data. It should look | |||
| 641 | // this: | |||
| 642 | // | |||
| 643 | // { | |||
| 644 | // "arguments": { "client-ptr": "xxxxx", | |||
| 645 | // "sign-off": "good bye", | |||
| 646 | // "thread-id": "zzzzz" }, | |||
| 647 | // "result": 0 | |||
| 648 | // } | |||
| 649 | // | |||
| 650 | // We expect one response. | |||
| 651 | auto responses = client->getResponses(); | |||
| 652 | ASSERT_EQ(responses.size(), 1U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("responses.size()" , "1U", responses.size(), 1U))) ; else return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 652, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 653 | ||||
| 654 | // First we turn it into an Element tree. | |||
| 655 | ConstElementPtr answer; | |||
| 656 | ASSERT_NO_THROW_LOG(answer = Element::fromJSON(responses.front())){ try { answer = Element::fromJSON(responses.front()); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 656, "Failed") = ::testing::Message() << "answer = Element::fromJSON(responses.front())" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 656, "Failed") = ::testing::Message() << "answer = Element::fromJSON(responses.front())" << " threw non-std::exception"; } }; | |||
| 657 | ||||
| 658 | // Answer should be a map containing "arguments" and "results". | |||
| 659 | ASSERT_EQ(answer->getType(), Element::map)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("answer->getType()" , "Element::map", answer->getType(), Element::map))) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 659, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 660 | ||||
| 661 | // "result" should be 0. | |||
| 662 | ConstElementPtr result = answer->get("result"); | |||
| 663 | ASSERT_TRUE(result)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(result)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 663, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "result", "false", "true") .c_str()) = ::testing::Message(); | |||
| 664 | ASSERT_EQ(result->getType(), Element::integer)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("result->getType()" , "Element::integer", result->getType(), Element::integer) )) ; else return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 664, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 665 | ASSERT_EQ(result->intValue(), 0)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("result->intValue()" , "0", result->intValue(), 0))) ; else return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 665, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 666 | ||||
| 667 | // "arguments" is a map containing "client-ptr" and "thread-id". | |||
| 668 | ConstElementPtr arguments = answer->get("arguments"); | |||
| 669 | ASSERT_TRUE(arguments)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(arguments)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 669, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "arguments", "false", "true") .c_str()) = ::testing::Message (); | |||
| 670 | ASSERT_EQ(arguments->getType(), Element::map)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("arguments->getType()" , "Element::map", arguments->getType(), Element::map))) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 670, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 671 | ||||
| 672 | // "client-ptr" is a string. | |||
| 673 | ConstElementPtr client_ptr = arguments->get("client-ptr"); | |||
| 674 | ASSERT_TRUE(client_ptr)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(client_ptr)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 674, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "client_ptr", "false", "true") .c_str()) = ::testing::Message (); | |||
| 675 | ASSERT_EQ(client_ptr->getType(), Element::string)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("client_ptr->getType()" , "Element::string", client_ptr->getType(), Element::string ))) ; else return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 675, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 676 | ||||
| 677 | // "thread-id" is a string. | |||
| 678 | ConstElementPtr thread_id = arguments->get("thread-id"); | |||
| 679 | ASSERT_TRUE(thread_id)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(thread_id)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult:: kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 679, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "thread_id", "false", "true") .c_str()) = ::testing::Message (); | |||
| 680 | ASSERT_EQ(thread_id->getType(), Element::string)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("thread_id->getType()" , "Element::string", thread_id->getType(), Element::string ))) ; else return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 680, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 681 | std::string thread_id_str = thread_id->stringValue(); | |||
| 682 | ||||
| 683 | // Make sure the response received was for this client. | |||
| 684 | std::stringstream ss; | |||
| 685 | ss << client; | |||
| 686 | ASSERT_EQ(client_ptr->stringValue(), ss.str())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("client_ptr->stringValue()" , "ss.str()", client_ptr->stringValue(), ss.str()))) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 686, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 687 | ||||
| 688 | ++total_responses; | |||
| 689 | } | |||
| 690 | ||||
| 691 | // We should have responses for all our clients. | |||
| 692 | EXPECT_EQ(total_responses, num_clients)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("total_responses" , "num_clients", total_responses, num_clients))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 692, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 693 | ||||
| 694 | // We should have had the expected number of pauses. | |||
| 695 | if (!num_pauses) { | |||
| 696 | ASSERT_EQ(pause_cnt_, 0U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("pause_cnt_" , "0U", pause_cnt_, 0U))) ; else return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 696, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 697 | } else { | |||
| 698 | // We allow a range on pauses of +-1. | |||
| 699 | ASSERT_TRUE((num_pauses - 1) <= pause_cnt_ &&switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult((num_pauses - 1) <= pause_cnt_ && (pause_cnt_ <= (num_pauses + 1)))) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 700, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "(num_pauses - 1) <= pause_cnt_ && (pause_cnt_ <= (num_pauses + 1))" , "false", "true") .c_str()) = ::testing::Message() | |||
| 700 | (pause_cnt_ <= (num_pauses + 1)))switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult((num_pauses - 1) <= pause_cnt_ && (pause_cnt_ <= (num_pauses + 1)))) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 700, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "(num_pauses - 1) <= pause_cnt_ && (pause_cnt_ <= (num_pauses + 1))" , "false", "true") .c_str()) = ::testing::Message() | |||
| 701 | << " num_pauses: " << num_pauses | |||
| 702 | << ", pause_cnt_" << pause_cnt_; | |||
| 703 | } | |||
| 704 | } | |||
| 705 | ||||
| 706 | /// @brief MtTcpListenerMgr instance under test. | |||
| 707 | MtTcpListenerMgrPtr mt_listener_mgr_; | |||
| 708 | ||||
| 709 | /// @brief IO service used in drive the test and test clients. | |||
| 710 | IOServicePtr io_service_; | |||
| 711 | ||||
| 712 | /// @brief Asynchronous timer service to detect timeouts. | |||
| 713 | IntervalTimer test_timer_; | |||
| 714 | ||||
| 715 | /// @brief Asynchronous timer for running IO service for a specified amount | |||
| 716 | /// of time. | |||
| 717 | IntervalTimer run_io_service_timer_; | |||
| 718 | ||||
| 719 | /// @brief List of client connections. | |||
| 720 | std::list<TcpTestClientPtr> clients_; | |||
| 721 | ||||
| 722 | /// @brief Number of threads the listener should use for the test. | |||
| 723 | size_t num_threads_; | |||
| 724 | ||||
| 725 | /// @brief Number of client requests to make during the test. | |||
| 726 | size_t num_clients_; | |||
| 727 | ||||
| 728 | /// @brief Number of requests currently in progress. | |||
| 729 | size_t num_in_progress_; | |||
| 730 | ||||
| 731 | /// @brief Number of requests that have finished. | |||
| 732 | size_t num_finished_; | |||
| 733 | ||||
| 734 | /// @brief Chunk size of requests that need to be processed in parallel. | |||
| 735 | /// | |||
| 736 | /// This can either be the number of threads (if the number of requests is | |||
| 737 | /// greater than the number of threads) or the number of requests (if the | |||
| 738 | /// number of threads is greater than the number of requests). | |||
| 739 | size_t chunk_size_; | |||
| 740 | ||||
| 741 | /// @brief Mutex used to lock during thread coordination. | |||
| 742 | std::mutex mutex_; | |||
| 743 | ||||
| 744 | /// @brief Condition variable used to coordinate threads. | |||
| 745 | std::condition_variable cv_; | |||
| 746 | ||||
| 747 | /// @brief Number of times client has been paused during the test. | |||
| 748 | size_t pause_cnt_; | |||
| 749 | ||||
| 750 | /// @brief Number of clients that have completed their assignment or | |||
| 751 | /// failed | |||
| 752 | size_t clients_done_; | |||
| 753 | ||||
| 754 | /// @brief Response Handler passed down to each connection. | |||
| 755 | TcpTestConnection::ResponseHandler response_handler_; | |||
| 756 | }; | |||
| 757 | ||||
| 758 | /// Verifies the construction, starting, stopping, pausing, resuming, | |||
| 759 | /// and destruction of MtTcpListener. | |||
| 760 | TEST_F(MtTcpListenerMgrTest, basics)static_assert(sizeof("MtTcpListenerMgrTest") > 1, "test_suite_name must not be empty" ); static_assert(sizeof("basics") > 1, "test_name must not be empty" ); class MtTcpListenerMgrTest_basics_Test : public MtTcpListenerMgrTest { public: MtTcpListenerMgrTest_basics_Test() = default; ~MtTcpListenerMgrTest_basics_Test () override = default; MtTcpListenerMgrTest_basics_Test (const MtTcpListenerMgrTest_basics_Test &) = delete; MtTcpListenerMgrTest_basics_Test & operator=( const MtTcpListenerMgrTest_basics_Test & ) = delete; MtTcpListenerMgrTest_basics_Test (MtTcpListenerMgrTest_basics_Test &&) noexcept = delete; MtTcpListenerMgrTest_basics_Test & operator=( MtTcpListenerMgrTest_basics_Test && ) noexcept = delete; private: void TestBody() override; [[maybe_unused ]] static ::testing::TestInfo* const test_info_; }; ::testing ::TestInfo* const MtTcpListenerMgrTest_basics_Test::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "basics", nullptr, nullptr, ::testing::internal::CodeLocation ("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 760), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 760), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 760), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_basics_Test >); void MtTcpListenerMgrTest_basics_Test::TestBody() { | |||
| 761 | // Make sure multi-threading is off. | |||
| 762 | MultiThreadingMgr::instance().setMode(false); | |||
| 763 | IOAddress address(SERVER_ADDRESS); | |||
| 764 | uint16_t port = SERVER_PORT; | |||
| 765 | ||||
| 766 | // Make sure we can create one. | |||
| 767 | ASSERT_NO_THROW_LOG(mt_listener_mgr_.reset({ try { mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port)); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 771, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 771, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port))" << " threw non-std::exception"; } } | |||
| 768 | new MtTcpListenerMgr({ try { mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port)); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 771, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 771, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port))" << " threw non-std::exception"; } } | |||
| 769 | std::bind(&MtTcpListenerMgrTest::listenerFactory, this,{ try { mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port)); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 771, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 771, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port))" << " threw non-std::exception"; } } | |||
| 770 | ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6),{ try { mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port)); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 771, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 771, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port))" << " threw non-std::exception"; } } | |||
| 771 | address, port))){ try { mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port)); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 771, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 771, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port))" << " threw non-std::exception"; } }; | |||
| 772 | ||||
| 773 | ASSERT_TRUE(mt_listener_mgr_)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 773, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_", "false", "true") .c_str()) = ::testing:: Message(); | |||
| 774 | ||||
| 775 | // Verify the getters do what we expect. | |||
| 776 | EXPECT_EQ(mt_listener_mgr_->getAddress(), address)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getAddress()" , "address", mt_listener_mgr_->getAddress(), address))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 776, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 777 | EXPECT_EQ(mt_listener_mgr_->getPort(), port)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getPort()" , "port", mt_listener_mgr_->getPort(), port))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 777, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 778 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 1U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "1U", mt_listener_mgr_->getThreadPoolSize(), 1U))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 778, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 779 | EXPECT_FALSE(mt_listener_mgr_->getTlsContext())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(mt_listener_mgr_-> getTlsContext()))) ; else ::testing::internal::AssertHelper(:: testing::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 779, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getTlsContext()", "true", "false") .c_str ()) = ::testing::Message(); | |||
| 780 | ||||
| 781 | // It should not have an IOService, should not be listening and | |||
| 782 | // should have no threads. | |||
| 783 | ASSERT_FALSE(mt_listener_mgr_->getThreadIOService())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(mt_listener_mgr_-> getThreadIOService()))) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 783, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "true", "false" ) .c_str()) = ::testing::Message(); | |||
| 784 | EXPECT_TRUE(mt_listener_mgr_->isStopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isStopped())) ; else ::testing::internal::AssertHelper(::testing ::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 784, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 785 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0U", mt_listener_mgr_->getThreadCount(), 0U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 785, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 786 | ||||
| 787 | // Verify that we cannot start it when multi-threading is disabled. | |||
| 788 | ASSERT_FALSE(MultiThreadingMgr::instance().getMode())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(MultiThreadingMgr:: instance().getMode()))) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 788, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "MultiThreadingMgr::instance().getMode()", "true", "false") .c_str()) = ::testing::Message(); | |||
| 789 | ASSERT_THROW_MSG(mt_listener_mgr_->start(), InvalidOperation,{ try { mt_listener_mgr_->start(); return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 791, "Failed") = ::testing::Message() << "no exception, expected: " << "InvalidOperation"; } catch (const InvalidOperation & ex) { switch (0) case 0: default: if (const ::testing:: AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare ("std::string(ex.what())", "\"MtTcpListenerMgr cannot be started\" \" when multi-threading is disabled\"" , std::string(ex.what()), "MtTcpListenerMgr cannot be started" " when multi-threading is disabled"))) ; else return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 791, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 791, "Failed") = ::testing::Message() << "wrong exception type thrown, expected: " << "InvalidOperation"; } } | |||
| 790 | "MtTcpListenerMgr cannot be started"{ try { mt_listener_mgr_->start(); return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 791, "Failed") = ::testing::Message() << "no exception, expected: " << "InvalidOperation"; } catch (const InvalidOperation & ex) { switch (0) case 0: default: if (const ::testing:: AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare ("std::string(ex.what())", "\"MtTcpListenerMgr cannot be started\" \" when multi-threading is disabled\"" , std::string(ex.what()), "MtTcpListenerMgr cannot be started" " when multi-threading is disabled"))) ; else return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 791, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 791, "Failed") = ::testing::Message() << "wrong exception type thrown, expected: " << "InvalidOperation"; } } | |||
| 791 | " when multi-threading is disabled"){ try { mt_listener_mgr_->start(); return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 791, "Failed") = ::testing::Message() << "no exception, expected: " << "InvalidOperation"; } catch (const InvalidOperation & ex) { switch (0) case 0: default: if (const ::testing:: AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare ("std::string(ex.what())", "\"MtTcpListenerMgr cannot be started\" \" when multi-threading is disabled\"" , std::string(ex.what()), "MtTcpListenerMgr cannot be started" " when multi-threading is disabled"))) ; else return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 791, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 791, "Failed") = ::testing::Message() << "wrong exception type thrown, expected: " << "InvalidOperation"; } }; | |||
| 792 | ||||
| 793 | // It should still not be listening and have no threads. | |||
| 794 | EXPECT_TRUE(mt_listener_mgr_->isStopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isStopped())) ; else ::testing::internal::AssertHelper(::testing ::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 794, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 795 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0U", mt_listener_mgr_->getThreadCount(), 0U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 795, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 796 | ||||
| 797 | // Enable multi-threading. | |||
| 798 | MultiThreadingMgr::instance().setMode(true); | |||
| 799 | ||||
| 800 | // Make sure we can start it and it's listening with 1 thread. | |||
| 801 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->start()){ try { mt_listener_mgr_->start(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 801, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 801, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 802 | ASSERT_TRUE(mt_listener_mgr_->isRunning())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isRunning())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 802, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 803 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 1U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "1U", mt_listener_mgr_->getThreadCount(), 1U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 803, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 804 | ASSERT_TRUE(mt_listener_mgr_->getThreadIOService())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> getThreadIOService())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 804, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 805 | EXPECT_FALSE(mt_listener_mgr_->getThreadIOService()->stopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(mt_listener_mgr_-> getThreadIOService()->stopped()))) ; else ::testing::internal ::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 805, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "true" , "false") .c_str()) = ::testing::Message(); | |||
| 806 | ||||
| 807 | // Trying to start it again should fail. | |||
| 808 | ASSERT_THROW_MSG(mt_listener_mgr_->start(), InvalidOperation,{ try { mt_listener_mgr_->start(); return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 809, "Failed") = ::testing::Message() << "no exception, expected: " << "InvalidOperation"; } catch (const InvalidOperation & ex) { switch (0) case 0: default: if (const ::testing:: AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare ("std::string(ex.what())", "\"MtTcpListenerMgr already started!\"" , std::string(ex.what()), "MtTcpListenerMgr already started!" ))) ; else return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 809, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 809, "Failed") = ::testing::Message() << "wrong exception type thrown, expected: " << "InvalidOperation"; } } | |||
| 809 | "MtTcpListenerMgr already started!"){ try { mt_listener_mgr_->start(); return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 809, "Failed") = ::testing::Message() << "no exception, expected: " << "InvalidOperation"; } catch (const InvalidOperation & ex) { switch (0) case 0: default: if (const ::testing:: AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare ("std::string(ex.what())", "\"MtTcpListenerMgr already started!\"" , std::string(ex.what()), "MtTcpListenerMgr already started!" ))) ; else return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 809, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 809, "Failed") = ::testing::Message() << "wrong exception type thrown, expected: " << "InvalidOperation"; } }; | |||
| 810 | ||||
| 811 | // Stop it and verify we're no longer listening. | |||
| 812 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->stop()){ try { mt_listener_mgr_->stop(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 812, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 812, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw non-std::exception"; } }; | |||
| 813 | ASSERT_TRUE(mt_listener_mgr_->isStopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isStopped())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 813, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 814 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0U", mt_listener_mgr_->getThreadCount(), 0U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 814, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 815 | ASSERT_FALSE(mt_listener_mgr_->getThreadIOService())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(mt_listener_mgr_-> getThreadIOService()))) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 815, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "true", "false" ) .c_str()) = ::testing::Message(); | |||
| 816 | ||||
| 817 | // Make sure we can call stop again without problems. | |||
| 818 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->stop()){ try { mt_listener_mgr_->stop(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 818, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 818, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw non-std::exception"; } }; | |||
| 819 | ||||
| 820 | // We should be able to restart it. | |||
| 821 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->start()){ try { mt_listener_mgr_->start(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 821, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 821, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 822 | ASSERT_TRUE(mt_listener_mgr_->isRunning())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isRunning())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 822, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 823 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 1U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "1U", mt_listener_mgr_->getThreadCount(), 1U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 823, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 824 | ASSERT_TRUE(mt_listener_mgr_->getThreadIOService())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> getThreadIOService())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 824, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 825 | EXPECT_FALSE(mt_listener_mgr_->getThreadIOService()->stopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(mt_listener_mgr_-> getThreadIOService()->stopped()))) ; else ::testing::internal ::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 825, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "true" , "false") .c_str()) = ::testing::Message(); | |||
| 826 | ||||
| 827 | // Destroying it should also stop it. | |||
| 828 | // If the test timeouts we know it didn't! | |||
| 829 | ASSERT_NO_THROW_LOG(mt_listener_mgr_.reset()){ try { mt_listener_mgr_.reset(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 829, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 829, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset()" << " threw non-std::exception"; } }; | |||
| 830 | ||||
| 831 | // Verify we can construct with more than one thread. | |||
| 832 | ASSERT_NO_THROW_LOG(mt_listener_mgr_.reset({ try { mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4)); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 836, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 836, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4))" << " threw non-std::exception"; } } | |||
| 833 | new MtTcpListenerMgr({ try { mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4)); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 836, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 836, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4))" << " threw non-std::exception"; } } | |||
| 834 | std::bind(&MtTcpListenerMgrTest::listenerFactory, this,{ try { mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4)); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 836, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 836, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4))" << " threw non-std::exception"; } } | |||
| 835 | ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6),{ try { mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4)); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 836, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 836, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4))" << " threw non-std::exception"; } } | |||
| 836 | address, port, 4))){ try { mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4)); } catch (const std::exception& ex) { return ::testing::internal:: AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 836, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 836, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset( new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), address, port, 4))" << " threw non-std::exception"; } }; | |||
| 837 | ||||
| 838 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->start()){ try { mt_listener_mgr_->start(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 838, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 838, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 839 | EXPECT_EQ(mt_listener_mgr_->getAddress(), address)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getAddress()" , "address", mt_listener_mgr_->getAddress(), address))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 839, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 840 | EXPECT_EQ(mt_listener_mgr_->getPort(), port)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getPort()" , "port", mt_listener_mgr_->getPort(), port))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 840, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 841 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 4U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "4U", mt_listener_mgr_->getThreadCount(), 4U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 841, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 842 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 4U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "4U", mt_listener_mgr_->getThreadPoolSize(), 4U))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 842, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 843 | ASSERT_TRUE(mt_listener_mgr_->isRunning())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isRunning())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 843, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 844 | ASSERT_TRUE(mt_listener_mgr_->getThreadIOService())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> getThreadIOService())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 844, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 845 | EXPECT_FALSE(mt_listener_mgr_->getThreadIOService()->stopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(mt_listener_mgr_-> getThreadIOService()->stopped()))) ; else ::testing::internal ::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 845, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "true" , "false") .c_str()) = ::testing::Message(); | |||
| 846 | ||||
| 847 | // Verify we can pause it. We should still be listening, threads intact, | |||
| 848 | // IOService stopped, state set to PAUSED. | |||
| 849 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->pause()){ try { mt_listener_mgr_->pause(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 849, "Failed") = ::testing::Message() << "mt_listener_mgr_->pause()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 849, "Failed") = ::testing::Message() << "mt_listener_mgr_->pause()" << " threw non-std::exception"; } }; | |||
| 850 | ASSERT_TRUE(mt_listener_mgr_->isPaused())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isPaused())) ; else return ::testing::internal::AssertHelper( ::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 850, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isPaused()", "false", "true") .c_str( )) = ::testing::Message(); | |||
| 851 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 4U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "4U", mt_listener_mgr_->getThreadCount(), 4U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 851, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 852 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 4U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "4U", mt_listener_mgr_->getThreadPoolSize(), 4U))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 852, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 853 | ASSERT_TRUE(mt_listener_mgr_->getThreadIOService())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> getThreadIOService())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 853, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 854 | EXPECT_TRUE(mt_listener_mgr_->getThreadIOService()->stopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> getThreadIOService()->stopped())) ; else ::testing::internal ::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 854, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "false" , "true") .c_str()) = ::testing::Message(); | |||
| 855 | ||||
| 856 | // Verify we can resume it. | |||
| 857 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->resume()){ try { mt_listener_mgr_->resume(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 857, "Failed") = ::testing::Message() << "mt_listener_mgr_->resume()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 857, "Failed") = ::testing::Message() << "mt_listener_mgr_->resume()" << " threw non-std::exception"; } }; | |||
| 858 | ASSERT_TRUE(mt_listener_mgr_->isRunning())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isRunning())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 858, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 859 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 4U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "4U", mt_listener_mgr_->getThreadCount(), 4U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 859, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 860 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 4U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "4U", mt_listener_mgr_->getThreadPoolSize(), 4U))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 860, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 861 | ASSERT_TRUE(mt_listener_mgr_->getThreadIOService())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> getThreadIOService())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 861, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 862 | EXPECT_FALSE(mt_listener_mgr_->getThreadIOService()->stopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(mt_listener_mgr_-> getThreadIOService()->stopped()))) ; else ::testing::internal ::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 862, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "true" , "false") .c_str()) = ::testing::Message(); | |||
| 863 | ||||
| 864 | // Stop it and verify we're no longer listening. | |||
| 865 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->stop()){ try { mt_listener_mgr_->stop(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 865, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 865, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw non-std::exception"; } }; | |||
| 866 | ASSERT_TRUE(mt_listener_mgr_->isStopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isStopped())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 866, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 867 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0U", mt_listener_mgr_->getThreadCount(), 0U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 867, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 868 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 4U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "4U", mt_listener_mgr_->getThreadPoolSize(), 4U))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 868, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 869 | ASSERT_FALSE(mt_listener_mgr_->getThreadIOService())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(mt_listener_mgr_-> getThreadIOService()))) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 869, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "true", "false" ) .c_str()) = ::testing::Message(); | |||
| 870 | EXPECT_TRUE(mt_listener_mgr_->isStopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isStopped())) ; else ::testing::internal::AssertHelper(::testing ::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 870, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 871 | } | |||
| 872 | ||||
| 873 | // Now we'll run some permutations of the number of listener threads | |||
| 874 | // and the number of client requests. | |||
| 875 | ||||
| 876 | // One thread, one client. | |||
| 877 | TEST_F(MtTcpListenerMgrTest, oneByOne)static_assert(sizeof("MtTcpListenerMgrTest") > 1, "test_suite_name must not be empty" ); static_assert(sizeof("oneByOne") > 1, "test_name must not be empty" ); class MtTcpListenerMgrTest_oneByOne_Test : public MtTcpListenerMgrTest { public: MtTcpListenerMgrTest_oneByOne_Test() = default; ~MtTcpListenerMgrTest_oneByOne_Test () override = default; MtTcpListenerMgrTest_oneByOne_Test (const MtTcpListenerMgrTest_oneByOne_Test &) = delete; MtTcpListenerMgrTest_oneByOne_Test & operator=( const MtTcpListenerMgrTest_oneByOne_Test & ) = delete; MtTcpListenerMgrTest_oneByOne_Test (MtTcpListenerMgrTest_oneByOne_Test &&) noexcept = delete; MtTcpListenerMgrTest_oneByOne_Test & operator=( MtTcpListenerMgrTest_oneByOne_Test && ) noexcept = delete; private: void TestBody() override; [[maybe_unused ]] static ::testing::TestInfo* const test_info_; }; ::testing ::TestInfo* const MtTcpListenerMgrTest_oneByOne_Test::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "oneByOne", nullptr, nullptr, ::testing::internal::CodeLocation ("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 877), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 877), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 877), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_oneByOne_Test >); void MtTcpListenerMgrTest_oneByOne_Test::TestBody() { | |||
| 878 | size_t num_threads = 1; | |||
| 879 | size_t num_clients = 1; | |||
| 880 | threadListenAndRespond(num_threads, num_clients); | |||
| 881 | } | |||
| 882 | ||||
| 883 | // One thread, four clients. | |||
| 884 | TEST_F(MtTcpListenerMgrTest, oneByFour)static_assert(sizeof("MtTcpListenerMgrTest") > 1, "test_suite_name must not be empty" ); static_assert(sizeof("oneByFour") > 1, "test_name must not be empty" ); class MtTcpListenerMgrTest_oneByFour_Test : public MtTcpListenerMgrTest { public: MtTcpListenerMgrTest_oneByFour_Test() = default; ~ MtTcpListenerMgrTest_oneByFour_Test() override = default; MtTcpListenerMgrTest_oneByFour_Test (const MtTcpListenerMgrTest_oneByFour_Test &) = delete; MtTcpListenerMgrTest_oneByFour_Test & operator=( const MtTcpListenerMgrTest_oneByFour_Test & ) = delete; MtTcpListenerMgrTest_oneByFour_Test (MtTcpListenerMgrTest_oneByFour_Test &&) noexcept = delete; MtTcpListenerMgrTest_oneByFour_Test & operator=( MtTcpListenerMgrTest_oneByFour_Test && ) noexcept = delete; private: void TestBody() override; [[maybe_unused ]] static ::testing::TestInfo* const test_info_; }; ::testing ::TestInfo* const MtTcpListenerMgrTest_oneByFour_Test::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "oneByFour", nullptr, nullptr, ::testing::internal::CodeLocation ("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 884), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 884), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 884), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_oneByFour_Test >); void MtTcpListenerMgrTest_oneByFour_Test::TestBody() { | |||
| 885 | size_t num_threads = 1; | |||
| 886 | size_t num_clients = 4; | |||
| 887 | threadListenAndRespond(num_threads, num_clients); | |||
| 888 | } | |||
| 889 | ||||
| 890 | // Four threads, one clients. | |||
| 891 | TEST_F(MtTcpListenerMgrTest, fourByOne)static_assert(sizeof("MtTcpListenerMgrTest") > 1, "test_suite_name must not be empty" ); static_assert(sizeof("fourByOne") > 1, "test_name must not be empty" ); class MtTcpListenerMgrTest_fourByOne_Test : public MtTcpListenerMgrTest { public: MtTcpListenerMgrTest_fourByOne_Test() = default; ~ MtTcpListenerMgrTest_fourByOne_Test() override = default; MtTcpListenerMgrTest_fourByOne_Test (const MtTcpListenerMgrTest_fourByOne_Test &) = delete; MtTcpListenerMgrTest_fourByOne_Test & operator=( const MtTcpListenerMgrTest_fourByOne_Test & ) = delete; MtTcpListenerMgrTest_fourByOne_Test (MtTcpListenerMgrTest_fourByOne_Test &&) noexcept = delete; MtTcpListenerMgrTest_fourByOne_Test & operator=( MtTcpListenerMgrTest_fourByOne_Test && ) noexcept = delete; private: void TestBody() override; [[maybe_unused ]] static ::testing::TestInfo* const test_info_; }; ::testing ::TestInfo* const MtTcpListenerMgrTest_fourByOne_Test::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "fourByOne", nullptr, nullptr, ::testing::internal::CodeLocation ("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 891), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 891), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 891), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_fourByOne_Test >); void MtTcpListenerMgrTest_fourByOne_Test::TestBody() { | |||
| 892 | size_t num_threads = 4; | |||
| 893 | size_t num_clients = 1; | |||
| 894 | threadListenAndRespond(num_threads, num_clients); | |||
| 895 | } | |||
| 896 | ||||
| 897 | // Four threads, four clients. | |||
| 898 | TEST_F(MtTcpListenerMgrTest, fourByFour)static_assert(sizeof("MtTcpListenerMgrTest") > 1, "test_suite_name must not be empty" ); static_assert(sizeof("fourByFour") > 1, "test_name must not be empty" ); class MtTcpListenerMgrTest_fourByFour_Test : public MtTcpListenerMgrTest { public: MtTcpListenerMgrTest_fourByFour_Test() = default; ~ MtTcpListenerMgrTest_fourByFour_Test() override = default; MtTcpListenerMgrTest_fourByFour_Test (const MtTcpListenerMgrTest_fourByFour_Test &) = delete; MtTcpListenerMgrTest_fourByFour_Test & operator=( const MtTcpListenerMgrTest_fourByFour_Test &) = delete; MtTcpListenerMgrTest_fourByFour_Test (MtTcpListenerMgrTest_fourByFour_Test &&) noexcept = delete; MtTcpListenerMgrTest_fourByFour_Test & operator=( MtTcpListenerMgrTest_fourByFour_Test && ) noexcept = delete; private: void TestBody() override; [[maybe_unused ]] static ::testing::TestInfo* const test_info_; }; ::testing ::TestInfo* const MtTcpListenerMgrTest_fourByFour_Test::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "fourByFour", nullptr, nullptr, ::testing::internal::CodeLocation ("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 898), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 898), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 898), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_fourByFour_Test >); void MtTcpListenerMgrTest_fourByFour_Test::TestBody() { | |||
| 899 | size_t num_threads = 4; | |||
| 900 | size_t num_clients = 4; | |||
| 901 | threadListenAndRespond(num_threads, num_clients); | |||
| 902 | } | |||
| 903 | ||||
| 904 | // Four threads, eight clients. | |||
| 905 | TEST_F(MtTcpListenerMgrTest, fourByEight)static_assert(sizeof("MtTcpListenerMgrTest") > 1, "test_suite_name must not be empty" ); static_assert(sizeof("fourByEight") > 1, "test_name must not be empty" ); class MtTcpListenerMgrTest_fourByEight_Test : public MtTcpListenerMgrTest { public: MtTcpListenerMgrTest_fourByEight_Test() = default; ~MtTcpListenerMgrTest_fourByEight_Test() override = default; MtTcpListenerMgrTest_fourByEight_Test (const MtTcpListenerMgrTest_fourByEight_Test &) = delete; MtTcpListenerMgrTest_fourByEight_Test & operator=( const MtTcpListenerMgrTest_fourByEight_Test & ) = delete; MtTcpListenerMgrTest_fourByEight_Test (MtTcpListenerMgrTest_fourByEight_Test &&) noexcept = delete; MtTcpListenerMgrTest_fourByEight_Test & operator=( MtTcpListenerMgrTest_fourByEight_Test && ) noexcept = delete; private: void TestBody() override; [[maybe_unused ]] static ::testing::TestInfo* const test_info_; }; ::testing ::TestInfo* const MtTcpListenerMgrTest_fourByEight_Test::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "fourByEight", nullptr, nullptr, ::testing::internal::CodeLocation ("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 905), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 905), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 905), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_fourByEight_Test >); void MtTcpListenerMgrTest_fourByEight_Test::TestBody() { | |||
| 906 | size_t num_threads = 4; | |||
| 907 | size_t num_clients = 8; | |||
| 908 | threadListenAndRespond(num_threads, num_clients); | |||
| 909 | } | |||
| 910 | ||||
| 911 | // Six threads, eighteen clients. | |||
| 912 | TEST_F(MtTcpListenerMgrTest, sixByEighteen)static_assert(sizeof("MtTcpListenerMgrTest") > 1, "test_suite_name must not be empty" ); static_assert(sizeof("sixByEighteen") > 1, "test_name must not be empty" ); class MtTcpListenerMgrTest_sixByEighteen_Test : public MtTcpListenerMgrTest { public: MtTcpListenerMgrTest_sixByEighteen_Test() = default ; ~MtTcpListenerMgrTest_sixByEighteen_Test() override = default ; MtTcpListenerMgrTest_sixByEighteen_Test (const MtTcpListenerMgrTest_sixByEighteen_Test &) = delete; MtTcpListenerMgrTest_sixByEighteen_Test & operator=( const MtTcpListenerMgrTest_sixByEighteen_Test & ) = delete; MtTcpListenerMgrTest_sixByEighteen_Test (MtTcpListenerMgrTest_sixByEighteen_Test &&) noexcept = delete; MtTcpListenerMgrTest_sixByEighteen_Test & operator=( MtTcpListenerMgrTest_sixByEighteen_Test && ) noexcept = delete; private: void TestBody() override; [[maybe_unused ]] static ::testing::TestInfo* const test_info_; }; ::testing ::TestInfo* const MtTcpListenerMgrTest_sixByEighteen_Test::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "sixByEighteen", nullptr, nullptr, ::testing::internal::CodeLocation ("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 912), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 912), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 912), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_sixByEighteen_Test >); void MtTcpListenerMgrTest_sixByEighteen_Test::TestBody () { | |||
| 913 | size_t num_threads = 6; | |||
| 914 | size_t num_clients = 18; | |||
| 915 | threadListenAndRespond(num_threads, num_clients); | |||
| 916 | } | |||
| 917 | ||||
| 918 | // Pauses and resumes the listener while it is processing | |||
| 919 | // requests. | |||
| 920 | TEST_F(MtTcpListenerMgrTest, pauseAndResume)static_assert(sizeof("MtTcpListenerMgrTest") > 1, "test_suite_name must not be empty" ); static_assert(sizeof("pauseAndResume") > 1, "test_name must not be empty" ); class MtTcpListenerMgrTest_pauseAndResume_Test : public MtTcpListenerMgrTest { public: MtTcpListenerMgrTest_pauseAndResume_Test() = default ; ~MtTcpListenerMgrTest_pauseAndResume_Test() override = default ; MtTcpListenerMgrTest_pauseAndResume_Test (const MtTcpListenerMgrTest_pauseAndResume_Test &) = delete; MtTcpListenerMgrTest_pauseAndResume_Test & operator=( const MtTcpListenerMgrTest_pauseAndResume_Test & ) = delete; MtTcpListenerMgrTest_pauseAndResume_Test (MtTcpListenerMgrTest_pauseAndResume_Test &&) noexcept = delete; MtTcpListenerMgrTest_pauseAndResume_Test & operator=( MtTcpListenerMgrTest_pauseAndResume_Test && ) noexcept = delete; private: void TestBody() override; [[maybe_unused ]] static ::testing::TestInfo* const test_info_; }; ::testing ::TestInfo* const MtTcpListenerMgrTest_pauseAndResume_Test::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "pauseAndResume", nullptr, nullptr, ::testing::internal::CodeLocation ("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 920), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 920), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 920), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_pauseAndResume_Test >); void MtTcpListenerMgrTest_pauseAndResume_Test::TestBody () { | |||
| 921 | size_t num_threads = 6; | |||
| 922 | size_t num_clients = 18; | |||
| 923 | size_t num_pauses = 3; | |||
| 924 | workPauseAndResume(num_threads, num_clients, num_pauses); | |||
| 925 | } | |||
| 926 | ||||
| 927 | // Check if a TLS listener can be created. | |||
| 928 | TEST_F(MtTcpListenerMgrTest, tls)static_assert(sizeof("MtTcpListenerMgrTest") > 1, "test_suite_name must not be empty" ); static_assert(sizeof("tls") > 1, "test_name must not be empty" ); class MtTcpListenerMgrTest_tls_Test : public MtTcpListenerMgrTest { public: MtTcpListenerMgrTest_tls_Test() = default; ~MtTcpListenerMgrTest_tls_Test () override = default; MtTcpListenerMgrTest_tls_Test (const MtTcpListenerMgrTest_tls_Test &) = delete; MtTcpListenerMgrTest_tls_Test & operator =( const MtTcpListenerMgrTest_tls_Test &) = delete; MtTcpListenerMgrTest_tls_Test (MtTcpListenerMgrTest_tls_Test &&) noexcept = delete ; MtTcpListenerMgrTest_tls_Test & operator=( MtTcpListenerMgrTest_tls_Test &&) noexcept = delete; private: void TestBody() override ; [[maybe_unused]] static ::testing::TestInfo* const test_info_ ; }; ::testing::TestInfo* const MtTcpListenerMgrTest_tls_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "tls", nullptr, nullptr, ::testing::internal::CodeLocation( "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 928), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 928), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 928), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_tls_Test >); void MtTcpListenerMgrTest_tls_Test::TestBody() { | |||
| 929 | IOAddress address(SERVER_ADDRESS); | |||
| 930 | uint16_t port = SERVER_PORT; | |||
| 931 | TlsContextPtr context; | |||
| 932 | configServer(context); | |||
| 933 | ||||
| 934 | // Make sure we can create the listener. | |||
| 935 | ASSERT_NO_THROW_LOG({ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, 1, context)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw non-std::exception"; } } | |||
| 936 | mt_listener_mgr_.reset(new MtTcpListenerMgr({ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, 1, context)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw non-std::exception"; } } | |||
| 937 | std::bind(&MtTcpListenerMgrTest::listenerFactory,{ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, 1, context)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw non-std::exception"; } } | |||
| 938 | this,{ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, 1, context)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw non-std::exception"; } } | |||
| 939 | ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6),{ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, 1, context)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw non-std::exception"; } } | |||
| 940 | IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context)){ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, 1, context)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw non-std::exception"; } } | |||
| 941 | ){ try { mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind (&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph ::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS ), SERVER_PORT, 1, context)); } catch (const std::exception& ex) { return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 941, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset(new MtTcpListenerMgr( std::bind(&MtTcpListenerMgrTest::listenerFactory, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6), IOAddress(SERVER_ADDRESS), SERVER_PORT, 1, context))" << " threw non-std::exception"; } }; | |||
| 942 | ||||
| 943 | EXPECT_EQ(mt_listener_mgr_->getAddress(), address)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getAddress()" , "address", mt_listener_mgr_->getAddress(), address))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 943, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 944 | EXPECT_EQ(mt_listener_mgr_->getPort(), port)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getPort()" , "port", mt_listener_mgr_->getPort(), port))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 944, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 945 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 1U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "1U", mt_listener_mgr_->getThreadPoolSize(), 1U))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 945, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 946 | EXPECT_EQ(mt_listener_mgr_->getTlsContext(), context)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getTlsContext()" , "context", mt_listener_mgr_->getTlsContext(), context))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult ::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 946, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 947 | EXPECT_TRUE(mt_listener_mgr_->isStopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isStopped())) ; else ::testing::internal::AssertHelper(::testing ::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 947, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 948 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0U", mt_listener_mgr_->getThreadCount(), 0U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 948, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 949 | ||||
| 950 | // Make sure we can start it and it's listening with 1 thread. | |||
| 951 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->start()){ try { mt_listener_mgr_->start(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 951, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 951, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 952 | ASSERT_TRUE(mt_listener_mgr_->isRunning())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isRunning())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 952, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 953 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 1U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "1U", mt_listener_mgr_->getThreadCount(), 1U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 953, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 954 | ASSERT_TRUE(mt_listener_mgr_->getThreadIOService())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> getThreadIOService())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 954, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 955 | EXPECT_FALSE(mt_listener_mgr_->getThreadIOService()->stopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(mt_listener_mgr_-> getThreadIOService()->stopped()))) ; else ::testing::internal ::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 955, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "true" , "false") .c_str()) = ::testing::Message(); | |||
| 956 | ||||
| 957 | // Stop it. | |||
| 958 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->stop()){ try { mt_listener_mgr_->stop(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 958, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 958, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw non-std::exception"; } }; | |||
| 959 | ASSERT_TRUE(mt_listener_mgr_->isStopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isStopped())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 959, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 960 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0U", mt_listener_mgr_->getThreadCount(), 0U))) ; else :: testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 960, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 961 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 1U)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "1U", mt_listener_mgr_->getThreadPoolSize(), 1U))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 961, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 962 | ASSERT_FALSE(mt_listener_mgr_->getThreadIOService())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(mt_listener_mgr_-> getThreadIOService()))) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 962, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "true", "false" ) .c_str()) = ::testing::Message(); | |||
| 963 | EXPECT_TRUE(mt_listener_mgr_->isStopped())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isStopped())) ; else ::testing::internal::AssertHelper(::testing ::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 963, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 964 | } | |||
| 965 | ||||
| 966 | /// Verifies that idle timeout can be passed down to the internal listener. | |||
| 967 | TEST_F(MtTcpListenerMgrTest, idleTimeout)static_assert(sizeof("MtTcpListenerMgrTest") > 1, "test_suite_name must not be empty" ); static_assert(sizeof("idleTimeout") > 1, "test_name must not be empty" ); class MtTcpListenerMgrTest_idleTimeout_Test : public MtTcpListenerMgrTest { public: MtTcpListenerMgrTest_idleTimeout_Test() = default; ~MtTcpListenerMgrTest_idleTimeout_Test() override = default; MtTcpListenerMgrTest_idleTimeout_Test (const MtTcpListenerMgrTest_idleTimeout_Test &) = delete; MtTcpListenerMgrTest_idleTimeout_Test & operator=( const MtTcpListenerMgrTest_idleTimeout_Test & ) = delete; MtTcpListenerMgrTest_idleTimeout_Test (MtTcpListenerMgrTest_idleTimeout_Test &&) noexcept = delete; MtTcpListenerMgrTest_idleTimeout_Test & operator=( MtTcpListenerMgrTest_idleTimeout_Test && ) noexcept = delete; private: void TestBody() override; [[maybe_unused ]] static ::testing::TestInfo* const test_info_; }; ::testing ::TestInfo* const MtTcpListenerMgrTest_idleTimeout_Test::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "idleTimeout", nullptr, nullptr, ::testing::internal::CodeLocation ("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 967), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 967), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 967), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_idleTimeout_Test >); void MtTcpListenerMgrTest_idleTimeout_Test::TestBody() { | |||
| 968 | // Create an MtTcpListenerMgr. | |||
| 969 | createMtTcpListenerMgr(1, std::bind(&MtTcpListenerMgrTest::synchronizedCommandHandler, | |||
| 970 | this, ph::_1)); | |||
| 971 | // Verify the default timeout value. | |||
| 972 | EXPECT_EQ(TCP_IDLE_CONNECTION_TIMEOUT, mt_listener_mgr_->getIdleTimeout())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("TCP_IDLE_CONNECTION_TIMEOUT" , "mt_listener_mgr_->getIdleTimeout()", TCP_IDLE_CONNECTION_TIMEOUT , mt_listener_mgr_->getIdleTimeout()))) ; else ::testing:: internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 972, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 973 | ||||
| 974 | // Set a new timeout value. | |||
| 975 | mt_listener_mgr_->setIdleTimeout(200); | |||
| 976 | EXPECT_EQ(200, mt_listener_mgr_->getIdleTimeout())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("200", "mt_listener_mgr_->getIdleTimeout()" , 200, mt_listener_mgr_->getIdleTimeout()))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 976, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 977 | ||||
| 978 | // Start the listener, which should instantiate the internal listener. | |||
| 979 | ASSERT_NO_THROW_LOG(mt_listener_mgr_->start()){ try { mt_listener_mgr_->start(); } catch (const std::exception & ex) { return ::testing::internal::AssertHelper(::testing ::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 979, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw type: " << typeid(ex).name() << ", what: " << ex.what(); } catch (...) { return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 979, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 980 | ASSERT_TRUE(mt_listener_mgr_->isRunning())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(mt_listener_mgr_-> isRunning())) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 980, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 981 | ||||
| 982 | // Verify the internal listener's timeout value. | |||
| 983 | auto tcp_listener = mt_listener_mgr_->getTcpListener(); | |||
| 984 | ASSERT_TRUE(tcp_listener)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(tcp_listener)) ; else return ::testing::internal::AssertHelper(::testing::TestPartResult ::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 984, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "tcp_listener", "false", "true") .c_str()) = ::testing::Message (); | |||
| 985 | EXPECT_EQ(200, tcp_listener->getIdleTimeout())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("200", "tcp_listener->getIdleTimeout()" , 200, tcp_listener->getIdleTimeout()))) ; else ::testing:: internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 985, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 986 | } | |||
| 987 | ||||
| 988 | } // end of anonymous namespace |
| 1 | // Copyright (C) 2022-2025 Internet Systems Consortium, Inc. ("ISC") |
| 2 | // |
| 3 | // This Source Code Form is subject to the terms of the Mozilla Public |
| 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this |
| 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 6 | |
| 7 | #ifndef TCP_TEST_CLIENT_H |
| 8 | #define TCP_TEST_CLIENT_H |
| 9 | |
| 10 | #include <cc/data.h> |
| 11 | #include <asiolink/tcp_socket.h> |
| 12 | #include <asiolink/tls_socket.h> |
| 13 | #include <asiolink/testutils/test_tls.h> |
| 14 | #include <tcp/tcp_connection.h> |
| 15 | #include <tcp/tcp_stream_msg.h> |
| 16 | #include <boost/asio/read.hpp> |
| 17 | #include <boost/asio/buffer.hpp> |
| 18 | #include <boost/asio/ip/tcp.hpp> |
| 19 | #include <gtest/gtest.h> |
| 20 | |
| 21 | /// @brief Entity which can connect to the TCP server endpoint with or |
| 22 | /// or without TLS. |
| 23 | class TcpTestClient : public boost::noncopyable { |
| 24 | |
| 25 | private: |
| 26 | /// @brief Type of the function implementing a callback invoked by the |
| 27 | /// @c SocketCallback functor. |
| 28 | typedef std::function<void(boost::system::error_code ec, size_t length)> |
| 29 | SocketCallbackFunction; |
| 30 | |
| 31 | /// @brief Functor associated with the socket object. |
| 32 | /// |
| 33 | /// This functor calls a callback function specified in the constructor. |
| 34 | class SocketCallback { |
| 35 | public: |
| 36 | |
| 37 | /// @brief Constructor. |
| 38 | /// |
| 39 | /// @param socket_callback Callback to be invoked by the functor upon |
| 40 | /// an event associated with the socket. |
| 41 | SocketCallback(SocketCallbackFunction socket_callback) |
| 42 | : callback_(socket_callback) { |
| 43 | } |
| 44 | |
| 45 | /// @brief Operator called when event associated with a socket occurs. |
| 46 | /// |
| 47 | /// This operator returns immediately when received error code is |
| 48 | /// @c boost::system::error_code is equal to |
| 49 | /// @c boost::asio::error::operation_aborted, i.e. the callback is not |
| 50 | /// invoked. |
| 51 | /// |
| 52 | /// @param ec Error code. |
| 53 | /// @param length Data length. |
| 54 | void operator()(boost::system::error_code ec, size_t length = 0) { |
| 55 | if (ec.value() == boost::asio::error::operation_aborted) { |
| 56 | return; |
| 57 | } |
| 58 | |
| 59 | callback_(ec, length); |
| 60 | } |
| 61 | |
| 62 | private: |
| 63 | /// @brief Supplied callback. |
| 64 | SocketCallbackFunction callback_; |
| 65 | }; |
| 66 | |
| 67 | public: |
| 68 | |
| 69 | /// @brief Constructor. |
| 70 | /// |
| 71 | /// This constructor creates new socket instance. It doesn't connect. Call |
| 72 | /// start() to connect to the server. |
| 73 | /// |
| 74 | /// @param io_service IO service to be stopped on error or completion. |
| 75 | /// @param done_callback Function cient should invoke when it has finished |
| 76 | /// all its requests or failed. |
| 77 | /// @param tls_context |
| 78 | /// @param server_address string containing the IP address of the server. |
| 79 | /// @param port port number of the server. |
| 80 | explicit TcpTestClient(const isc::asiolink::IOServicePtr& io_service, |
| 81 | std::function<void()> done_callback, |
| 82 | isc::asiolink::TlsContextPtr tls_context = |
| 83 | isc::asiolink::TlsContextPtr(), |
| 84 | const std::string& server_address = "127.0.0.1", |
| 85 | uint16_t port = 18123) |
| 86 | : io_service_(io_service), |
| 87 | tls_context_(tls_context), |
| 88 | tcp_socket_(), tls_socket_(), |
| 89 | done_callback_(done_callback), |
| 90 | server_address_(server_address), server_port_(port), |
| 91 | buf_(), response_(), |
| 92 | receive_done_(false), expected_eof_(false), handshake_failed_(false) { |
| 93 | if (!tls_context_) { |
| 94 | tcp_socket_.reset(new isc::asiolink::TCPSocket<SocketCallback>(io_service)); |
| 95 | } else { |
| 96 | tls_socket_.reset(new isc::asiolink::TLSSocket<SocketCallback>(io_service, |
| 97 | tls_context)); |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | bool useTls() { |
| 102 | return (!!tls_context_); |
| 103 | } |
| 104 | |
| 105 | /// @brief Destructor. |
| 106 | /// |
| 107 | /// Closes the underlying socket if it is open. |
| 108 | virtual ~TcpTestClient() { |
| 109 | close(); |
| 110 | } |
| 111 | |
| 112 | /// @brief Connect to the listener and initiate request processing. |
| 113 | /// |
| 114 | /// Upon successful connection, carry out the TLS handshake. If the handshake |
| 115 | /// completes successful start sending requests. |
| 116 | void start() { |
| 117 | isc::asiolink::TCPEndpoint endpoint(boost::asio::ip::make_address(server_address_), server_port_); |
| 118 | SocketCallback socket_cb( |
| 119 | [this](boost::system::error_code ec, size_t /*length */) { |
| 120 | receive_done_ = false; |
| 121 | expected_eof_ = false; |
| 122 | handshake_failed_ = false; |
| 123 | if (ec) { |
| 124 | // One would expect that open wouldn't return |
| 125 | // EINPROGRESS error code, but simply wait for the connection |
| 126 | // to get established before the handler is invoked. It turns out, |
| 127 | // however, that on some OSes the connect handler may receive this |
| 128 | // error code which doesn't necessarily indicate a problem. |
| 129 | // Making an attempt to write and read from this socket will |
| 130 | // typically succeed. So, we ignore this error. |
| 131 | if (ec.value() != boost::asio::error::in_progress) { |
| 132 | ADD_FAILURE()::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/tcp_test_client.h" , 132, "Failed") = ::testing::Message() << "error occurred while connecting: " |
| 133 | << ec.message(); |
| 134 | done_callback_(); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | if (useTls()) { |
| 139 | SocketCallback tls_socket_cb( |
| 140 | [this](boost::system::error_code erc, size_t /*length */) { |
| 141 | if (erc) { |
| 142 | handshake_failed_ = true; |
| 143 | done_callback_(); |
| 144 | } else { |
| 145 | sendNextRequest(); |
| 146 | } |
| 147 | }); |
| 148 | |
| 149 | tls_socket_->handshake(tls_socket_cb); |
| 150 | } else { |
| 151 | sendNextRequest(); |
| 152 | } |
| 153 | }); |
| 154 | |
| 155 | if (useTls()) { |
| 156 | tls_socket_->open(&endpoint, socket_cb); |
| 157 | } else { |
| 158 | tcp_socket_->open(&endpoint, socket_cb); |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | /// @brief Send request specified in textual format. |
| 163 | /// |
| 164 | /// @param request request in the textual format. |
| 165 | void startRequest(const std::string& request) { |
| 166 | requests_to_send_.push_back(request); |
| 167 | start(); |
| 168 | } |
| 169 | |
| 170 | /// @brief Send request specified in textual format. |
| 171 | /// |
| 172 | /// @param request request in the textual format. |
| 173 | void startRequests(const std::list<std::string>& requests) { |
| 174 | requests_to_send_ = requests; |
| 175 | start(); |
| 176 | } |
| 177 | |
| 178 | /// @brief Sends the next request from the list of requests to send. |
| 179 | void sendNextRequest() { |
| 180 | // If there are any requests left to send, send them. |
| 181 | if (!requests_to_send_.empty()) { |
| 182 | std::string request = requests_to_send_.front(); |
| 183 | requests_to_send_.pop_front(); |
| 184 | if (request.empty()) { |
| 185 | waitForEof(); |
| 186 | } else { |
| 187 | sendRequest(request); |
| 188 | } |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | /// @brief Send a stream request. |
| 193 | /// |
| 194 | /// @param request request data to send textual format. |
| 195 | /// @param send_length number of bytes to send. If not zero, can be used |
| 196 | /// to truncate the amount of data sent. |
| 197 | void sendRequest(const std::string& request, const size_t send_length = 0) { |
| 198 | // Prepend the length of the request. |
| 199 | uint16_t size = static_cast<uint16_t>(request.size()); |
| 200 | isc::tcp::WireData wire_request; |
| 201 | if (!request.empty()) { |
| 202 | wire_request.push_back(static_cast<uint8_t>((size & 0xff00U) >> 8)); |
| 203 | wire_request.push_back(static_cast<uint8_t>(size & 0x00ffU)); |
| 204 | wire_request.insert(wire_request.end(), request.begin(), request.end()); |
| 205 | } |
| 206 | |
| 207 | sendPartialRequest(wire_request, send_length); |
| 208 | } |
| 209 | |
| 210 | /// @brief Wait for a server to close the connection. |
| 211 | void waitForEof() { |
| 212 | stream_response_.reset(new isc::tcp::TcpStreamRequest()); |
| 213 | receivePartialResponse(true); |
| 214 | } |
| 215 | |
| 216 | /// @brief Send part of the request. |
| 217 | /// |
| 218 | /// @param request part of the request to be sent. |
| 219 | /// @param send_length number of bytes to send. If not zero, can be used |
| 220 | /// to truncate the amount of data sent. |
| 221 | void sendPartialRequest(isc::tcp::WireData& wire_request, size_t send_length = 0) { |
| 222 | if (!send_length) { |
| 223 | send_length = wire_request.size(); |
| 224 | } else { |
| 225 | ASSERT_LE(send_length, wire_request.size())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::CmpHelperLE("send_length", "wire_request.size()" , send_length, wire_request.size()))) ; else return ::testing ::internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "../../../src/lib/tcp/tests/tcp_test_client.h", 225, gtest_ar .failure_message()) = ::testing::Message() |
| 226 | << "broken test, send_length exceeds wire size"; |
| 227 | } |
| 228 | |
| 229 | SocketCallback socket_cb( |
| 230 | [this, wire_request](boost::system::error_code ec, size_t bytes_transferred) mutable { |
| 231 | if (ec) { |
| 232 | if (ec.value() == boost::asio::error::operation_aborted) { |
| 233 | return; |
| 234 | |
| 235 | } else if ((ec.value() == boost::asio::error::try_again) || |
| 236 | (ec.value() == boost::asio::error::would_block)) { |
| 237 | // If we should try again make sure there is no garbage in the |
| 238 | // bytes_transferred. |
| 239 | bytes_transferred = 0; |
| 240 | } else { |
| 241 | ADD_FAILURE()::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/tcp_test_client.h" , 241, "Failed") = ::testing::Message() << "error occurred while connecting: " |
| 242 | << ec.message(); |
| 243 | done_callback_(); |
| 244 | return; |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | // Remove the part of the request which has been sent. |
| 249 | if (bytes_transferred > 0 && (wire_request.size() <= bytes_transferred)) { |
| 250 | wire_request.erase(wire_request.begin(), |
| 251 | (wire_request.begin() + bytes_transferred)); |
| 252 | } |
| 253 | |
| 254 | // Continue sending request data if there are still some data to be |
| 255 | // sent. |
| 256 | if (!wire_request.empty()) { |
| 257 | sendPartialRequest(wire_request); |
| 258 | } else { |
| 259 | // Request has been sent. Start receiving response. |
| 260 | receivePartialResponse(); |
| 261 | } |
| 262 | }); |
| 263 | |
| 264 | if (useTls()) { |
| 265 | tls_socket_->asyncSend(static_cast<const void *>(wire_request.data()), |
| 266 | send_length, socket_cb); |
| 267 | } else { |
| 268 | tcp_socket_->asyncSend(static_cast<const void *>(wire_request.data()), |
| 269 | send_length, socket_cb); |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | /// @brief Receive response from the server. |
| 274 | void receivePartialResponse(bool expect_eof = false) { |
| 275 | SocketCallback socket_cb( |
| 276 | [this, expect_eof](const boost::system::error_code& ec, |
| 277 | std::size_t bytes_transferred) { |
| 278 | if (!stream_response_) { |
| 279 | stream_response_.reset(new isc::tcp::TcpStreamRequest()); |
| 280 | } |
| 281 | |
| 282 | if (ec) { |
| 283 | // IO service stopped so simply return. |
| 284 | if (ec.value() == boost::asio::error::operation_aborted) { |
| 285 | return; |
| 286 | } else if ((ec.value() == boost::asio::error::try_again) || |
| 287 | (ec.value() == boost::asio::error::would_block)) { |
| 288 | // If we should try again, make sure that there is no garbage |
| 289 | // in the bytes_transferred. |
| 290 | bytes_transferred = 0; |
| 291 | } else if (expect_eof) { |
| 292 | expected_eof_ = true; |
| 293 | done_callback_(); |
| 294 | return; |
| 295 | } else { |
| 296 | // Error occurred, bail... |
| 297 | ADD_FAILURE()::testing::internal::AssertHelper(::testing::TestPartResult:: kNonFatalFailure, "../../../src/lib/tcp/tests/tcp_test_client.h" , 297, "Failed") = ::testing::Message() << "client: " << this |
| 298 | << " error occurred while receiving TCP" |
| 299 | << " response from the server: " << ec.message(); |
| 300 | done_callback_(); |
| 301 | return; |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | // Post received data to the current response. |
| 306 | if (bytes_transferred > 0) { |
| 307 | stream_response_->postBuffer(buf_.data(), bytes_transferred); |
| 308 | } |
| 309 | |
| 310 | if (stream_response_->needData()) { |
| 311 | // Response is incomplete, keep reading. |
| 312 | receivePartialResponse(); |
| 313 | } else { |
| 314 | // Response is complete, process it. |
| 315 | responseReceived(); |
| 316 | } |
| 317 | }); |
| 318 | |
| 319 | isc::asiolink::TCPEndpoint from; |
| 320 | if (useTls()) { |
| 321 | tls_socket_->asyncReceive(static_cast<void*>(buf_.data()), buf_.size(), 0, |
| 322 | &from, socket_cb); |
| 323 | } else { |
| 324 | tcp_socket_->asyncReceive(static_cast<void*>(buf_.data()), buf_.size(), 0, |
| 325 | &from, socket_cb); |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | /// @brief Process a completed response received from the server. |
| 330 | virtual void responseReceived() { |
| 331 | /// Unpack wire data into a string. |
| 332 | ASSERT_NO_THROW(stream_response_->unpack())switch (0) case 0: default: if (::testing::internal::TrueWithString gtest_msg{}) { try { if (::testing::internal::AlwaysTrue()) { stream_response_->unpack(); } else static_assert(true, "" ); } catch (std::exception const& e) { gtest_msg.value = "it throws " ; gtest_msg.value += ::testing::internal::GetTypeName(typeid( e)); gtest_msg.value += " with description \""; gtest_msg.value += e.what(); gtest_msg.value += "\"."; goto gtest_label_testnothrow_332 ; } catch (...) { gtest_msg.value = "it throws."; goto gtest_label_testnothrow_332 ; } } else gtest_label_testnothrow_332 : return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/tcp_test_client.h" , 332, ("Expected: " "stream_response_->unpack()" " doesn't throw an exception.\n" " Actual: " + gtest_msg.value) .c_str()) = ::testing::Message (); |
| 333 | std::string response = stream_response_->getRequestString(); |
| 334 | responses_received_.push_back(response); |
| 335 | |
| 336 | // Quit if server tells us "good bye". |
| 337 | if (response.find("good bye", 0) != std::string::npos) { |
| 338 | receive_done_ = true; |
| 339 | done_callback_(); |
| 340 | return; |
| 341 | } |
| 342 | |
| 343 | // Clear out for the next one. |
| 344 | stream_response_.reset(); |
| 345 | sendNextRequest(); |
| 346 | } |
| 347 | |
| 348 | /// @brief Close connection. |
| 349 | void close() { |
| 350 | if (useTls()) { |
| 351 | tls_socket_->close(); |
| 352 | } else { |
| 353 | tcp_socket_->close(); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | /// @brief Returns true if the receive completed without error. |
| 358 | /// |
| 359 | /// @return True if the receive completed successfully, false |
| 360 | /// otherwise. |
| 361 | bool receiveDone() { |
| 362 | return (receive_done_); |
| 363 | } |
| 364 | |
| 365 | /// @brief Returns true if the receive ended with expected EOF |
| 366 | /// |
| 367 | /// @return True if the receive ended with EOF, false otherwise |
| 368 | bool expectedEof() { |
| 369 | return (expected_eof_); |
| 370 | } |
| 371 | |
| 372 | /// @brief Returns the list of received responses. |
| 373 | /// |
| 374 | /// @return list of string responses. |
| 375 | const std::list<std::string>& getResponses() { |
| 376 | return (responses_received_); |
| 377 | } |
| 378 | |
| 379 | bool handshakeFailed() { |
| 380 | return(handshake_failed_); |
| 381 | } |
| 382 | |
| 383 | private: |
| 384 | |
| 385 | /// @brief Holds pointer to the IO service. |
| 386 | isc::asiolink::IOServicePtr io_service_; |
| 387 | |
| 388 | /// @brief TLS context. |
| 389 | isc::asiolink::TlsContextPtr tls_context_; |
| 390 | |
| 391 | /// @brief TCP socket used by this connection. |
| 392 | std::unique_ptr<isc::asiolink::TCPSocket<SocketCallback> > tcp_socket_; |
| 393 | |
| 394 | /// @brief TLS socket used by this connection. |
| 395 | std::unique_ptr<isc::asiolink::TLSSocket<SocketCallback> > tls_socket_; |
| 396 | |
| 397 | /// @brief Callback to invoke when the client has finished its work or |
| 398 | /// failed. |
| 399 | std::function<void()> done_callback_; |
| 400 | |
| 401 | /// @brief IP address of the server. |
| 402 | std::string server_address_; |
| 403 | |
| 404 | /// @brief IP port of the server. |
| 405 | uint16_t server_port_; |
| 406 | |
| 407 | /// @brief Buffer into which response is written. |
| 408 | std::array<char, 8192> buf_; |
| 409 | |
| 410 | /// @brief Response in the textual format. |
| 411 | std::string response_; |
| 412 | |
| 413 | /// @brief Set to true when the receive has completed successfully. |
| 414 | bool receive_done_; |
| 415 | |
| 416 | /// @brief Set to true when the receive ended in EOF as expected. In other |
| 417 | /// words, the server closed the connection while we were reading as we |
| 418 | /// expected it to do. |
| 419 | bool expected_eof_; |
| 420 | |
| 421 | /// @brief Set to true if the TLS handshake failed. |
| 422 | bool handshake_failed_; |
| 423 | |
| 424 | /// @brief Pointer to the server response currently being received. |
| 425 | isc::tcp::TcpStreamRequestPtr stream_response_; |
| 426 | |
| 427 | /// @brief List of string requests to send. |
| 428 | std::list<std::string> requests_to_send_; |
| 429 | |
| 430 | /// @brief List of string responses received. |
| 431 | std::list<std::string> responses_received_; |
| 432 | }; |
| 433 | |
| 434 | /// @brief Pointer to the TcpTestClient. |
| 435 | typedef boost::shared_ptr<TcpTestClient> TcpTestClientPtr; |
| 436 | |
| 437 | #endif |
| 1 | // Copyright (C) 2011-2025 Internet Systems Consortium, Inc. ("ISC") |
| 2 | // |
| 3 | // This Source Code Form is subject to the terms of the Mozilla Public |
| 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this |
| 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 6 | |
| 7 | #ifndef TCP_SOCKET_H |
| 8 | #define TCP_SOCKET_H |
| 9 | |
| 10 | #ifndef BOOST_ASIO_HPP |
| 11 | #error "asio.hpp must be included before including this, see asiolink.h as to why" |
| 12 | #endif |
| 13 | |
| 14 | #include <asiolink/io_asio_socket.h> |
| 15 | #include <asiolink/io_endpoint.h> |
| 16 | #include <asiolink/io_service.h> |
| 17 | #include <asiolink/tcp_endpoint.h> |
| 18 | #include <exceptions/isc_assert.h> |
| 19 | #include <util/buffer.h> |
| 20 | #include <util/io.h> |
| 21 | |
| 22 | #include <algorithm> |
| 23 | #include <cstddef> |
| 24 | |
| 25 | #include <boost/numeric/conversion/cast.hpp> |
| 26 | |
| 27 | #include <netinet/in.h> |
| 28 | #include <sys/socket.h> |
| 29 | #include <unistd.h> // for some IPC/network system calls |
| 30 | |
| 31 | namespace isc { |
| 32 | namespace asiolink { |
| 33 | |
| 34 | /// \brief Buffer Too Large |
| 35 | /// |
| 36 | /// Thrown on an attempt to send a buffer > 64k |
| 37 | class BufferTooLarge : public IOError { |
| 38 | public: |
| 39 | BufferTooLarge(const char* file, size_t line, const char* what) : |
| 40 | IOError(file, line, what) {} |
| 41 | }; |
| 42 | |
| 43 | /// \brief The \c TCPSocket class is a concrete derived class of \c IOAsioSocket |
| 44 | /// that represents a TCP socket. |
| 45 | /// |
| 46 | /// \param C Callback type |
| 47 | template <typename C> |
| 48 | class TCPSocket : public IOAsioSocket<C> { |
| 49 | private: |
| 50 | /// \brief Class is non-copyable |
| 51 | TCPSocket(const TCPSocket&); |
| 52 | TCPSocket& operator=(const TCPSocket&); |
| 53 | |
| 54 | public: |
| 55 | |
| 56 | /// \brief Constructor from an ASIO TCP socket. |
| 57 | /// |
| 58 | /// \param socket The ASIO representation of the TCP socket. It is assumed |
| 59 | /// that the caller will open and close the socket, so these |
| 60 | /// operations are a no-op for that socket. |
| 61 | TCPSocket(boost::asio::ip::tcp::socket& socket); |
| 62 | |
| 63 | /// \brief Constructor |
| 64 | /// |
| 65 | /// Used when the TCPSocket is being asked to manage its own internal |
| 66 | /// socket. In this case, the open() and close() methods are used. |
| 67 | /// |
| 68 | /// \param service I/O Service object used to manage the socket. |
| 69 | TCPSocket(const IOServicePtr& service); |
| 70 | |
| 71 | /// \brief Destructor |
| 72 | virtual ~TCPSocket(); |
| 73 | |
| 74 | /// \brief Return file descriptor of underlying socket |
| 75 | virtual int getNative() const { |
| 76 | return (socket_.native_handle()); |
| 77 | } |
| 78 | |
| 79 | /// \brief Return protocol of socket |
| 80 | virtual int getProtocol() const { |
| 81 | return (IPPROTO_TCPIPPROTO_TCP); |
| 82 | } |
| 83 | |
| 84 | /// \brief Is "open()" synchronous? |
| 85 | /// |
| 86 | /// Indicates that the opening of a TCP socket is asynchronous. |
| 87 | virtual bool isOpenSynchronous() const { |
| 88 | return (false); |
| 89 | } |
| 90 | |
| 91 | /// \brief Checks if the connection is usable. |
| 92 | /// |
| 93 | /// The connection is usable if the socket is open and the peer has not |
| 94 | /// closed its connection. |
| 95 | /// |
| 96 | /// \return true if the connection is usable. |
| 97 | bool isUsable() const { |
| 98 | // If the socket is open it doesn't mean that it is still usable. The connection |
| 99 | // could have been closed on the other end. We have to check if we can still |
| 100 | // use this socket. |
| 101 | if (socket_.is_open()) { |
| 102 | // Remember the current non blocking setting. |
| 103 | const bool non_blocking_orig = socket_.non_blocking(); |
| 104 | // Set the socket to non blocking mode. We're going to test if the socket |
| 105 | // returns would_block status on the attempt to read from it. |
| 106 | socket_.non_blocking(true); |
| 107 | |
| 108 | boost::system::error_code ec; |
| 109 | char data[2]; |
| 110 | |
| 111 | // Use receive with message peek flag to avoid removing the data awaiting |
| 112 | // to be read. |
| 113 | static_cast<void>( |
| 114 | socket_.receive(boost::asio::buffer(data, sizeof(data)), |
| 115 | boost::asio::socket_base::message_peek, |
| 116 | ec)); |
| 117 | |
| 118 | // Revert the original non_blocking flag on the socket. |
| 119 | socket_.non_blocking(non_blocking_orig); |
| 120 | |
| 121 | // If the connection is alive we'd typically get would_block status code. |
| 122 | // If there are any data that haven't been read we may also get success |
| 123 | // status. We're guessing that try_again may also be returned by some |
| 124 | // implementations in some situations. Any other error code indicates a |
| 125 | // problem with the connection so we assume that the connection has been |
| 126 | // closed. |
| 127 | return (!ec || (ec.value() == boost::asio::error::try_again) || |
| 128 | (ec.value() == boost::asio::error::would_block)); |
| 129 | } |
| 130 | |
| 131 | return (false); |
| 132 | } |
| 133 | |
| 134 | /// \brief Open Socket |
| 135 | /// |
| 136 | /// Opens the TCP socket. This is an asynchronous operation, completion of |
| 137 | /// which will be signalled via a call to the callback function. |
| 138 | /// |
| 139 | /// \param endpoint Endpoint to which the socket will connect. |
| 140 | /// \param callback Callback object. |
| 141 | virtual void open(const IOEndpoint* endpoint, C& callback); |
| 142 | |
| 143 | /// \brief Send Asynchronously |
| 144 | /// |
| 145 | /// Calls the underlying socket's async_send() method to send a packet of |
| 146 | /// data asynchronously to the remote endpoint. The callback will be called |
| 147 | /// on completion. |
| 148 | /// |
| 149 | /// \param data Data to send |
| 150 | /// \param length Length of data to send |
| 151 | /// \param endpoint Target of the send. (Unused for a TCP socket because |
| 152 | /// that was determined when the connection was opened.) |
| 153 | /// \param callback Callback object. |
| 154 | /// \throw BufferTooLarge on attempt to send a buffer larger than 64kB. |
| 155 | virtual void asyncSend(const void* data, size_t length, |
| 156 | const IOEndpoint* endpoint, C& callback); |
| 157 | |
| 158 | /// \brief Send Asynchronously without count. |
| 159 | /// |
| 160 | /// This variant of the method sends data over the TCP socket without |
| 161 | /// preceding the data with a data count. Eventually, we should migrate |
| 162 | /// the virtual method to not insert the count but there are existing |
| 163 | /// classes using the count. Once this migration is done, the existing |
| 164 | /// virtual method should be replaced by this method. |
| 165 | /// |
| 166 | /// \param data Data to send |
| 167 | /// \param length Length of data to send |
| 168 | /// \param callback Callback object. |
| 169 | /// \throw BufferTooLarge on attempt to send a buffer larger than 64kB. |
| 170 | void asyncSend(const void* data, size_t length, C& callback); |
| 171 | |
| 172 | /// \brief Receive Asynchronously |
| 173 | /// |
| 174 | /// Calls the underlying socket's async_receive() method to read a packet |
| 175 | /// of data from a remote endpoint. Arrival of the data is signalled via a |
| 176 | /// call to the callback function. |
| 177 | /// |
| 178 | /// \param data Buffer to receive incoming message |
| 179 | /// \param length Length of the data buffer |
| 180 | /// \param offset Offset into buffer where data is to be put |
| 181 | /// \param endpoint Source of the communication |
| 182 | /// \param callback Callback object |
| 183 | virtual void asyncReceive(void* data, size_t length, size_t offset, |
| 184 | IOEndpoint* endpoint, C& callback); |
| 185 | |
| 186 | /// \brief Process received data packet |
| 187 | /// |
| 188 | /// See the description of IOAsioSocket::receiveComplete for a complete |
| 189 | /// description of this method. |
| 190 | /// |
| 191 | /// \param staging Pointer to the start of the staging buffer. |
| 192 | /// \param length Amount of data in the staging buffer. |
| 193 | /// \param cumulative Amount of data received before the staging buffer is |
| 194 | /// processed. |
| 195 | /// \param offset Unused. |
| 196 | /// \param expected unused. |
| 197 | /// \param buff Output buffer. Data in the staging buffer is be copied |
| 198 | /// to this output buffer in the call. |
| 199 | /// |
| 200 | /// \return Always true |
| 201 | virtual bool processReceivedData(const void* staging, size_t length, |
| 202 | size_t& cumulative, size_t& offset, |
| 203 | size_t& expected, |
| 204 | isc::util::OutputBufferPtr& buff); |
| 205 | |
| 206 | /// \brief Cancel I/O On Socket |
| 207 | virtual void cancel(); |
| 208 | |
| 209 | /// \brief Close socket |
| 210 | virtual void close(); |
| 211 | |
| 212 | /// \brief Returns reference to the underlying ASIO socket. |
| 213 | /// |
| 214 | /// \return Reference to underlying ASIO socket. |
| 215 | virtual boost::asio::ip::tcp::socket& getASIOSocket() const { |
| 216 | return (socket_); |
| 217 | } |
| 218 | |
| 219 | private: |
| 220 | |
| 221 | /// @brief The IO service used to handle events. |
| 222 | IOServicePtr io_service_; |
| 223 | |
| 224 | /// Two variables to hold the socket - a socket and a pointer to it. This |
| 225 | /// handles the case where a socket is passed to the TCPSocket on |
| 226 | /// construction, or where it is asked to manage its own socket. |
| 227 | |
| 228 | /// Pointer to own socket |
| 229 | std::unique_ptr<boost::asio::ip::tcp::socket> socket_ptr_; |
| 230 | |
| 231 | /// Socket |
| 232 | boost::asio::ip::tcp::socket& socket_; |
| 233 | |
| 234 | /// @todo Remove temporary buffer |
| 235 | /// The current implementation copies the buffer passed to asyncSend() into |
| 236 | /// a temporary buffer and precedes it with a two-byte count field. As |
| 237 | /// ASIO should really be just about sending and receiving data, the TCP |
| 238 | /// code should not do this. If the protocol using this requires a two-byte |
| 239 | /// count, it should add it before calling this code. (This may be best |
| 240 | /// achieved by altering isc::dns::buffer to have pairs of methods: |
| 241 | /// getLength()/getTCPLength(), getData()/getTCPData(), with the getTCPXxx() |
| 242 | /// methods taking into account a two-byte count field.) |
| 243 | /// |
| 244 | /// The option of sending the data in two operations, the count followed by |
| 245 | /// the data was discounted as that would lead to two callbacks which would |
| 246 | /// cause problems with the stackless coroutine code. |
| 247 | |
| 248 | /// Send buffer |
| 249 | isc::util::OutputBufferPtr send_buffer_; |
| 250 | }; |
| 251 | |
| 252 | // Constructor - caller manages socket |
| 253 | |
| 254 | template <typename C> |
| 255 | TCPSocket<C>::TCPSocket(boost::asio::ip::tcp::socket& socket) : |
| 256 | socket_ptr_(), socket_(socket), send_buffer_() { |
| 257 | } |
| 258 | |
| 259 | // Constructor - create socket on the fly |
| 260 | |
| 261 | template <typename C> |
| 262 | TCPSocket<C>::TCPSocket(const IOServicePtr& io_service) : io_service_(io_service), |
| 263 | socket_ptr_(new boost::asio::ip::tcp::socket(io_service_->getInternalIOService())), |
| 264 | socket_(*socket_ptr_) { |
| 265 | } |
| 266 | |
| 267 | // Destructor. |
| 268 | |
| 269 | template <typename C> |
| 270 | TCPSocket<C>::~TCPSocket() { |
| 271 | TCPSocket<C>::close(); |
| 272 | } |
| 273 | |
| 274 | // Open the socket. |
| 275 | |
| 276 | template <typename C> void |
| 277 | TCPSocket<C>::open(const IOEndpoint* endpoint, C& callback) { |
| 278 | // If socket is open on this end but has been closed by the peer, |
| 279 | // we need to reconnect. |
| 280 | if (socket_.is_open() && !isUsable()) { |
| 281 | close(); |
| 282 | } |
| 283 | // Ignore opens on already-open socket. Don't throw a failure because |
| 284 | // of uncertainties as to what precedes when using asynchronous I/O. |
| 285 | // Also allows us a treat a passed-in socket as a self-managed socket. |
| 286 | if (!socket_.is_open()) { |
| 287 | if (endpoint->getFamily() == AF_INET2) { |
| 288 | socket_.open(boost::asio::ip::tcp::v4()); |
| 289 | } else { |
| 290 | socket_.open(boost::asio::ip::tcp::v6()); |
| 291 | } |
| 292 | |
| 293 | // Set options on the socket: |
| 294 | |
| 295 | // Reuse address - allow the socket to bind to a port even if the port |
| 296 | // is in the TIMED_WAIT state. |
| 297 | socket_.set_option(boost::asio::socket_base::reuse_address(true)); |
| 298 | } |
| 299 | |
| 300 | // Upconvert to a TCPEndpoint. We need to do this because although |
| 301 | // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it does not |
| 302 | // contain a method for getting at the underlying endpoint type - that is in |
| 303 | /// the derived class and the two classes differ on return type. |
| 304 | isc_throw_assert(endpoint->getProtocol() == IPPROTO_TCP){ if(!(static_cast<bool>(endpoint->getProtocol() == IPPROTO_TCP ))) { do { std::ostringstream oss__; oss__ << "../../../src/lib/asiolink/tcp_socket.h" << ":" << 304 << " (" << "endpoint->getProtocol() == IPPROTO_TCP" << ") failed"; throw isc::Unexpected("../../../src/lib/asiolink/tcp_socket.h" , 304, oss__.str().c_str()); } while (1); }}; |
| 305 | const TCPEndpoint* tcp_endpoint = |
| 306 | static_cast<const TCPEndpoint*>(endpoint); |
| 307 | |
| 308 | // Connect to the remote endpoint. On success, the handler will be |
| 309 | // called (with one argument - the length argument will default to |
| 310 | // zero). |
| 311 | socket_.async_connect(tcp_endpoint->getASIOEndpoint(), callback); |
| 312 | } |
| 313 | |
| 314 | // Send a message. Should never do this if the socket is not open, so throw |
| 315 | // an exception if this is the case. |
| 316 | |
| 317 | template <typename C> void |
| 318 | TCPSocket<C>::asyncSend(const void* data, size_t length, C& callback) { |
| 319 | if (socket_.is_open()) { |
| 320 | |
| 321 | try { |
| 322 | send_buffer_.reset(new isc::util::OutputBuffer(length)); |
| 323 | send_buffer_->writeData(data, length); |
| 324 | |
| 325 | // Send the data. |
| 326 | socket_.async_send(boost::asio::buffer(send_buffer_->getData(), |
| 327 | send_buffer_->getLength()), |
| 328 | callback); |
| 329 | } catch (const boost::numeric::bad_numeric_cast&) { |
| 330 | isc_throw(BufferTooLarge,do { std::ostringstream oss__; oss__ << "attempt to send buffer larger than 64kB" ; throw BufferTooLarge("../../../src/lib/asiolink/tcp_socket.h" , 331, oss__.str().c_str()); } while (1) |
| 331 | "attempt to send buffer larger than 64kB")do { std::ostringstream oss__; oss__ << "attempt to send buffer larger than 64kB" ; throw BufferTooLarge("../../../src/lib/asiolink/tcp_socket.h" , 331, oss__.str().c_str()); } while (1); |
| 332 | } |
| 333 | |
| 334 | } else { |
| 335 | isc_throw(SocketNotOpen,do { std::ostringstream oss__; oss__ << "attempt to send on a TCP socket that is not open" ; throw SocketNotOpen("../../../src/lib/asiolink/tcp_socket.h" , 336, oss__.str().c_str()); } while (1) |
| 336 | "attempt to send on a TCP socket that is not open")do { std::ostringstream oss__; oss__ << "attempt to send on a TCP socket that is not open" ; throw SocketNotOpen("../../../src/lib/asiolink/tcp_socket.h" , 336, oss__.str().c_str()); } while (1); |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | template <typename C> void |
| 341 | TCPSocket<C>::asyncSend(const void* data, size_t length, |
| 342 | const IOEndpoint*, C& callback) { |
| 343 | if (socket_.is_open()) { |
| 344 | |
| 345 | /// Need to copy the data into a temporary buffer and precede it with |
| 346 | /// a two-byte count field. |
| 347 | /// @todo arrange for the buffer passed to be preceded by the count |
| 348 | try { |
| 349 | /// Ensure it fits into 16 bits |
| 350 | uint16_t count = boost::numeric_cast<uint16_t>(length); |
| 351 | |
| 352 | /// Copy data into a buffer preceded by the count field. |
| 353 | send_buffer_.reset(new isc::util::OutputBuffer(length + 2)); |
| 354 | send_buffer_->writeUint16(count); |
| 355 | send_buffer_->writeData(data, length); |
| 356 | |
| 357 | /// ... and send it |
| 358 | socket_.async_send(boost::asio::buffer(send_buffer_->getData(), |
| 359 | send_buffer_->getLength()), callback); |
| 360 | } catch (const boost::numeric::bad_numeric_cast&) { |
| 361 | isc_throw(BufferTooLarge,do { std::ostringstream oss__; oss__ << "attempt to send buffer larger than 64kB" ; throw BufferTooLarge("../../../src/lib/asiolink/tcp_socket.h" , 362, oss__.str().c_str()); } while (1) |
| 362 | "attempt to send buffer larger than 64kB")do { std::ostringstream oss__; oss__ << "attempt to send buffer larger than 64kB" ; throw BufferTooLarge("../../../src/lib/asiolink/tcp_socket.h" , 362, oss__.str().c_str()); } while (1); |
| 363 | } |
| 364 | |
| 365 | } else { |
| 366 | isc_throw(SocketNotOpen,do { std::ostringstream oss__; oss__ << "attempt to send on a TCP socket that is not open" ; throw SocketNotOpen("../../../src/lib/asiolink/tcp_socket.h" , 367, oss__.str().c_str()); } while (1) |
| 367 | "attempt to send on a TCP socket that is not open")do { std::ostringstream oss__; oss__ << "attempt to send on a TCP socket that is not open" ; throw SocketNotOpen("../../../src/lib/asiolink/tcp_socket.h" , 367, oss__.str().c_str()); } while (1); |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | // Receive a message. Note that the "offset" argument is used as an index |
| 372 | // into the buffer in order to decide where to put the data. It is up to the |
| 373 | // caller to initialize the data to zero |
| 374 | template <typename C> void |
| 375 | TCPSocket<C>::asyncReceive(void* data, size_t length, size_t offset, |
| 376 | IOEndpoint* endpoint, C& callback) { |
| 377 | if (socket_.is_open()) { |
| 378 | // Upconvert to a TCPEndpoint. We need to do this because although |
| 379 | // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it |
| 380 | // does not contain a method for getting at the underlying endpoint |
| 381 | // type - that is in the derived class and the two classes differ on |
| 382 | // return type. |
| 383 | isc_throw_assert(endpoint->getProtocol() == IPPROTO_TCP){ if(!(static_cast<bool>(endpoint->getProtocol() == IPPROTO_TCP ))) { do { std::ostringstream oss__; oss__ << "../../../src/lib/asiolink/tcp_socket.h" << ":" << 383 << " (" << "endpoint->getProtocol() == IPPROTO_TCP" << ") failed"; throw isc::Unexpected("../../../src/lib/asiolink/tcp_socket.h" , 383, oss__.str().c_str()); } while (1); }}; |
| 384 | TCPEndpoint* tcp_endpoint = static_cast<TCPEndpoint*>(endpoint); |
| 385 | |
| 386 | // Write the endpoint details from the communications link. Ideally |
| 387 | // we should make IOEndpoint assignable, but this runs in to all sorts |
| 388 | // of problems concerning the management of the underlying Boost |
| 389 | // endpoint (e.g. if it is not self-managed, is the copied one |
| 390 | // self-managed?) The most pragmatic solution is to let Boost take care |
| 391 | // of everything and copy details of the underlying endpoint. |
| 392 | tcp_endpoint->getASIOEndpoint() = socket_.remote_endpoint(); |
| 393 | |
| 394 | // Ensure we can write into the buffer and if so, set the pointer to |
| 395 | // where the data will be written. |
| 396 | if (offset >= length) { |
| 397 | isc_throw(BufferOverflow, "attempt to read into area beyond end of "do { std::ostringstream oss__; oss__ << "attempt to read into area beyond end of " "TCP receive buffer"; throw BufferOverflow("../../../src/lib/asiolink/tcp_socket.h" , 398, oss__.str().c_str()); } while (1) |
| 398 | "TCP receive buffer")do { std::ostringstream oss__; oss__ << "attempt to read into area beyond end of " "TCP receive buffer"; throw BufferOverflow("../../../src/lib/asiolink/tcp_socket.h" , 398, oss__.str().c_str()); } while (1); |
| 399 | } |
| 400 | void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset); |
| 401 | |
| 402 | // ... and kick off the read. |
| 403 | socket_.async_receive(boost::asio::buffer(buffer_start, length - offset), callback); |
| 404 | |
| 405 | } else { |
| 406 | isc_throw(SocketNotOpen,do { std::ostringstream oss__; oss__ << "attempt to receive from a TCP socket that is not open" ; throw SocketNotOpen("../../../src/lib/asiolink/tcp_socket.h" , 407, oss__.str().c_str()); } while (1) |
| 407 | "attempt to receive from a TCP socket that is not open")do { std::ostringstream oss__; oss__ << "attempt to receive from a TCP socket that is not open" ; throw SocketNotOpen("../../../src/lib/asiolink/tcp_socket.h" , 407, oss__.str().c_str()); } while (1); |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | // Is the receive complete? |
| 412 | |
| 413 | template <typename C> bool |
| 414 | TCPSocket<C>::processReceivedData(const void* staging, size_t length, |
| 415 | size_t& cumulative, size_t& offset, |
| 416 | size_t& expected, |
| 417 | isc::util::OutputBufferPtr& outbuff) { |
| 418 | // Point to the data in the staging buffer and note how much there is. |
| 419 | const uint8_t* data = static_cast<const uint8_t*>(staging); |
| 420 | size_t data_length = length; |
| 421 | |
| 422 | // Is the number is "expected" valid? It won't be unless we have received |
| 423 | // at least two bytes of data in total for this set of receives. |
| 424 | if (cumulative < 2) { |
| 425 | |
| 426 | // "expected" is not valid. Did this read give us enough data to |
| 427 | // work it out? |
| 428 | cumulative += length; |
| 429 | if (cumulative < 2) { |
| 430 | |
| 431 | // Nope, still not valid. This must have been the first packet and |
| 432 | // was only one byte long. Tell the fetch code to read the next |
| 433 | // packet into the staging buffer beyond the data that is already |
| 434 | // there so that the next time we are called we have a complete |
| 435 | // TCP count. |
| 436 | offset = cumulative; |
| 437 | return (false); |
| 438 | } |
| 439 | |
| 440 | // Have enough data to interpret the packet count, so do so now. |
| 441 | expected = isc::util::readUint16(data, cumulative); |
| 442 | |
| 443 | // We have two bytes less of data to process. Point to the start of the |
| 444 | // data and adjust the packet size. Note that at this point, |
| 445 | // "cumulative" is the true amount of data in the staging buffer, not |
| 446 | // "length". |
| 447 | data += 2; |
| 448 | data_length = cumulative - 2; |
| 449 | } else { |
| 450 | |
| 451 | // Update total amount of data received. |
| 452 | cumulative += length; |
| 453 | } |
| 454 | |
| 455 | // Regardless of anything else, the next read goes into the start of the |
| 456 | // staging buffer. |
| 457 | offset = 0; |
| 458 | |
| 459 | // Work out how much data we still have to put in the output buffer. (This |
| 460 | // could be zero if we have just interpreted the TCP count and that was |
| 461 | // set to zero.) |
| 462 | if (expected >= outbuff->getLength()) { |
| 463 | |
| 464 | // Still need data in the output packet. Copy what we can from the |
| 465 | // staging buffer to the output buffer. |
| 466 | size_t copy_amount = std::min(expected - outbuff->getLength(), data_length); |
| 467 | outbuff->writeData(data, copy_amount); |
| 468 | } |
| 469 | |
| 470 | // We can now say if we have all the data. |
| 471 | return (expected == outbuff->getLength()); |
| 472 | } |
| 473 | |
| 474 | // Cancel I/O on the socket. No-op if the socket is not open. |
| 475 | |
| 476 | template <typename C> void |
| 477 | TCPSocket<C>::cancel() { |
| 478 | if (socket_.is_open()) { |
| 479 | socket_.cancel(); |
| 480 | } |
| 481 | } |
| 482 | |
| 483 | // Close the socket down. Can only do this if the socket is open and we are |
| 484 | // managing it ourself. |
| 485 | |
| 486 | template <typename C> void |
| 487 | TCPSocket<C>::close() { |
| 488 | if (socket_.is_open() && socket_ptr_) { |
| 489 | socket_.close(); |
| 490 | } |
| 491 | } |
| 492 | |
| 493 | } // namespace asiolink |
| 494 | } // namespace isc |
| 495 | |
| 496 | #endif // TCP_SOCKET_H |
| 1 | // |
| 2 | // basic_stream_socket.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 | |
| 11 | #ifndef BOOST_ASIO_BASIC_STREAM_SOCKET_HPP |
| 12 | #define BOOST_ASIO_BASIC_STREAM_SOCKET_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 | #include <cstddef> |
| 20 | #include <boost/asio/async_result.hpp> |
| 21 | #include <boost/asio/basic_socket.hpp> |
| 22 | #include <boost/asio/detail/handler_type_requirements.hpp> |
| 23 | #include <boost/asio/detail/non_const_lvalue.hpp> |
| 24 | #include <boost/asio/detail/throw_error.hpp> |
| 25 | #include <boost/asio/error.hpp> |
| 26 | |
| 27 | #include <boost/asio/detail/push_options.hpp> |
| 28 | |
| 29 | namespace boost { |
| 30 | namespace asio { |
| 31 | |
| 32 | #if !defined(BOOST_ASIO_BASIC_STREAM_SOCKET_FWD_DECL) |
| 33 | #define BOOST_ASIO_BASIC_STREAM_SOCKET_FWD_DECL |
| 34 | |
| 35 | // Forward declaration with defaulted arguments. |
| 36 | template <typename Protocol, typename Executor = any_io_executor> |
| 37 | class basic_stream_socket; |
| 38 | |
| 39 | #endif // !defined(BOOST_ASIO_BASIC_STREAM_SOCKET_FWD_DECL) |
| 40 | |
| 41 | /// Provides stream-oriented socket functionality. |
| 42 | /** |
| 43 | * The basic_stream_socket class template provides asynchronous and blocking |
| 44 | * stream-oriented socket functionality. |
| 45 | * |
| 46 | * @par Thread Safety |
| 47 | * @e Distinct @e objects: Safe.@n |
| 48 | * @e Shared @e objects: Unsafe. |
| 49 | * |
| 50 | * Synchronous @c send, @c receive, @c connect, and @c shutdown operations are |
| 51 | * thread safe with respect to each other, if the underlying operating system |
| 52 | * calls are also thread safe. This means that it is permitted to perform |
| 53 | * concurrent calls to these synchronous operations on a single socket object. |
| 54 | * Other synchronous operations, such as @c open or @c close, are not thread |
| 55 | * safe. |
| 56 | * |
| 57 | * @par Concepts: |
| 58 | * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. |
| 59 | */ |
| 60 | template <typename Protocol, typename Executor> |
| 61 | class basic_stream_socket |
| 62 | : public basic_socket<Protocol, Executor> |
| 63 | { |
| 64 | private: |
| 65 | class initiate_async_send; |
| 66 | class initiate_async_receive; |
| 67 | |
| 68 | public: |
| 69 | /// The type of the executor associated with the object. |
| 70 | typedef Executor executor_type; |
| 71 | |
| 72 | /// Rebinds the socket type to another executor. |
| 73 | template <typename Executor1> |
| 74 | struct rebind_executor |
| 75 | { |
| 76 | /// The socket type when rebound to the specified executor. |
| 77 | typedef basic_stream_socket<Protocol, Executor1> other; |
| 78 | }; |
| 79 | |
| 80 | /// The native representation of a socket. |
| 81 | #if defined(GENERATING_DOCUMENTATION) |
| 82 | typedef implementation_defined native_handle_type; |
| 83 | #else |
| 84 | typedef typename basic_socket<Protocol, |
| 85 | Executor>::native_handle_type native_handle_type; |
| 86 | #endif |
| 87 | |
| 88 | /// The protocol type. |
| 89 | typedef Protocol protocol_type; |
| 90 | |
| 91 | /// The endpoint type. |
| 92 | typedef typename Protocol::endpoint endpoint_type; |
| 93 | |
| 94 | /// Construct a basic_stream_socket without opening it. |
| 95 | /** |
| 96 | * This constructor creates a stream socket without opening it. The socket |
| 97 | * needs to be opened and then connected or accepted before data can be sent |
| 98 | * or received on it. |
| 99 | * |
| 100 | * @param ex The I/O executor that the socket will use, by default, to |
| 101 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 102 | */ |
| 103 | explicit basic_stream_socket(const executor_type& ex) |
| 104 | : basic_socket<Protocol, Executor>(ex) |
| 105 | { |
| 106 | } |
| 107 | |
| 108 | /// Construct a basic_stream_socket without opening it. |
| 109 | /** |
| 110 | * This constructor creates a stream socket without opening it. The socket |
| 111 | * needs to be opened and then connected or accepted before data can be sent |
| 112 | * or received on it. |
| 113 | * |
| 114 | * @param context An execution context which provides the I/O executor that |
| 115 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 116 | * operations performed on the socket. |
| 117 | */ |
| 118 | template <typename ExecutionContext> |
| 119 | explicit basic_stream_socket(ExecutionContext& context, |
| 120 | constraint_t< |
| 121 | is_convertible<ExecutionContext&, execution_context&>::value |
| 122 | > = 0) |
| 123 | : basic_socket<Protocol, Executor>(context) |
| 124 | { |
| 125 | } |
| 126 | |
| 127 | /// Construct and open a basic_stream_socket. |
| 128 | /** |
| 129 | * This constructor creates and opens a stream socket. The socket needs to be |
| 130 | * connected or accepted before data can be sent or received on it. |
| 131 | * |
| 132 | * @param ex The I/O executor that the socket will use, by default, to |
| 133 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 134 | * |
| 135 | * @param protocol An object specifying protocol parameters to be used. |
| 136 | * |
| 137 | * @throws boost::system::system_error Thrown on failure. |
| 138 | */ |
| 139 | basic_stream_socket(const executor_type& ex, const protocol_type& protocol) |
| 140 | : basic_socket<Protocol, Executor>(ex, protocol) |
| 141 | { |
| 142 | } |
| 143 | |
| 144 | /// Construct and open a basic_stream_socket. |
| 145 | /** |
| 146 | * This constructor creates and opens a stream socket. The socket needs to be |
| 147 | * connected or accepted before data can be sent or received on it. |
| 148 | * |
| 149 | * @param context An execution context which provides the I/O executor that |
| 150 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 151 | * operations performed on the socket. |
| 152 | * |
| 153 | * @param protocol An object specifying protocol parameters to be used. |
| 154 | * |
| 155 | * @throws boost::system::system_error Thrown on failure. |
| 156 | */ |
| 157 | template <typename ExecutionContext> |
| 158 | basic_stream_socket(ExecutionContext& context, const protocol_type& protocol, |
| 159 | constraint_t< |
| 160 | is_convertible<ExecutionContext&, execution_context&>::value, |
| 161 | defaulted_constraint |
| 162 | > = defaulted_constraint()) |
| 163 | : basic_socket<Protocol, Executor>(context, protocol) |
| 164 | { |
| 165 | } |
| 166 | |
| 167 | /// Construct a basic_stream_socket, opening it and binding it to the given |
| 168 | /// local endpoint. |
| 169 | /** |
| 170 | * This constructor creates a stream socket and automatically opens it bound |
| 171 | * to the specified endpoint on the local machine. The protocol used is the |
| 172 | * protocol associated with the given endpoint. |
| 173 | * |
| 174 | * @param ex The I/O executor that the socket will use, by default, to |
| 175 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 176 | * |
| 177 | * @param endpoint An endpoint on the local machine to which the stream |
| 178 | * socket will be bound. |
| 179 | * |
| 180 | * @throws boost::system::system_error Thrown on failure. |
| 181 | */ |
| 182 | basic_stream_socket(const executor_type& ex, const endpoint_type& endpoint) |
| 183 | : basic_socket<Protocol, Executor>(ex, endpoint) |
| 184 | { |
| 185 | } |
| 186 | |
| 187 | /// Construct a basic_stream_socket, opening it and binding it to the given |
| 188 | /// local endpoint. |
| 189 | /** |
| 190 | * This constructor creates a stream socket and automatically opens it bound |
| 191 | * to the specified endpoint on the local machine. The protocol used is the |
| 192 | * protocol associated with the given endpoint. |
| 193 | * |
| 194 | * @param context An execution context which provides the I/O executor that |
| 195 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 196 | * operations performed on the socket. |
| 197 | * |
| 198 | * @param endpoint An endpoint on the local machine to which the stream |
| 199 | * socket will be bound. |
| 200 | * |
| 201 | * @throws boost::system::system_error Thrown on failure. |
| 202 | */ |
| 203 | template <typename ExecutionContext> |
| 204 | basic_stream_socket(ExecutionContext& context, const endpoint_type& endpoint, |
| 205 | constraint_t< |
| 206 | is_convertible<ExecutionContext&, execution_context&>::value |
| 207 | > = 0) |
| 208 | : basic_socket<Protocol, Executor>(context, endpoint) |
| 209 | { |
| 210 | } |
| 211 | |
| 212 | /// Construct a basic_stream_socket on an existing native socket. |
| 213 | /** |
| 214 | * This constructor creates a stream socket object to hold an existing native |
| 215 | * socket. |
| 216 | * |
| 217 | * @param ex The I/O executor that the socket will use, by default, to |
| 218 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 219 | * |
| 220 | * @param protocol An object specifying protocol parameters to be used. |
| 221 | * |
| 222 | * @param native_socket The new underlying socket implementation. |
| 223 | * |
| 224 | * @throws boost::system::system_error Thrown on failure. |
| 225 | */ |
| 226 | basic_stream_socket(const executor_type& ex, |
| 227 | const protocol_type& protocol, const native_handle_type& native_socket) |
| 228 | : basic_socket<Protocol, Executor>(ex, protocol, native_socket) |
| 229 | { |
| 230 | } |
| 231 | |
| 232 | /// Construct a basic_stream_socket on an existing native socket. |
| 233 | /** |
| 234 | * This constructor creates a stream socket object to hold an existing native |
| 235 | * socket. |
| 236 | * |
| 237 | * @param context An execution context which provides the I/O executor that |
| 238 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 239 | * operations performed on the socket. |
| 240 | * |
| 241 | * @param protocol An object specifying protocol parameters to be used. |
| 242 | * |
| 243 | * @param native_socket The new underlying socket implementation. |
| 244 | * |
| 245 | * @throws boost::system::system_error Thrown on failure. |
| 246 | */ |
| 247 | template <typename ExecutionContext> |
| 248 | basic_stream_socket(ExecutionContext& context, |
| 249 | const protocol_type& protocol, const native_handle_type& native_socket, |
| 250 | constraint_t< |
| 251 | is_convertible<ExecutionContext&, execution_context&>::value |
| 252 | > = 0) |
| 253 | : basic_socket<Protocol, Executor>(context, protocol, native_socket) |
| 254 | { |
| 255 | } |
| 256 | |
| 257 | /// Move-construct a basic_stream_socket from another. |
| 258 | /** |
| 259 | * This constructor moves a stream socket from one object to another. |
| 260 | * |
| 261 | * @param other The other basic_stream_socket object from which the move |
| 262 | * will occur. |
| 263 | * |
| 264 | * @note Following the move, the moved-from object is in the same state as if |
| 265 | * constructed using the @c basic_stream_socket(const executor_type&) |
| 266 | * constructor. |
| 267 | */ |
| 268 | basic_stream_socket(basic_stream_socket&& other) noexcept |
| 269 | : basic_socket<Protocol, Executor>(std::move(other)) |
| 270 | { |
| 271 | } |
| 272 | |
| 273 | /// Move-assign a basic_stream_socket from another. |
| 274 | /** |
| 275 | * This assignment operator moves a stream socket from one object to another. |
| 276 | * |
| 277 | * @param other The other basic_stream_socket object from which the move |
| 278 | * will occur. |
| 279 | * |
| 280 | * @note Following the move, the moved-from object is in the same state as if |
| 281 | * constructed using the @c basic_stream_socket(const executor_type&) |
| 282 | * constructor. |
| 283 | */ |
| 284 | basic_stream_socket& operator=(basic_stream_socket&& other) |
| 285 | { |
| 286 | basic_socket<Protocol, Executor>::operator=(std::move(other)); |
| 287 | return *this; |
| 288 | } |
| 289 | |
| 290 | /// Move-construct a basic_stream_socket from a socket of another protocol |
| 291 | /// type. |
| 292 | /** |
| 293 | * This constructor moves a stream socket from one object to another. |
| 294 | * |
| 295 | * @param other The other basic_stream_socket object from which the move |
| 296 | * will occur. |
| 297 | * |
| 298 | * @note Following the move, the moved-from object is in the same state as if |
| 299 | * constructed using the @c basic_stream_socket(const executor_type&) |
| 300 | * constructor. |
| 301 | */ |
| 302 | template <typename Protocol1, typename Executor1> |
| 303 | basic_stream_socket(basic_stream_socket<Protocol1, Executor1>&& other, |
| 304 | constraint_t< |
| 305 | is_convertible<Protocol1, Protocol>::value |
| 306 | && is_convertible<Executor1, Executor>::value |
| 307 | > = 0) |
| 308 | : basic_socket<Protocol, Executor>(std::move(other)) |
| 309 | { |
| 310 | } |
| 311 | |
| 312 | /// Move-assign a basic_stream_socket from a socket of another protocol type. |
| 313 | /** |
| 314 | * This assignment operator moves a stream socket from one object to another. |
| 315 | * |
| 316 | * @param other The other basic_stream_socket object from which the move |
| 317 | * will occur. |
| 318 | * |
| 319 | * @note Following the move, the moved-from object is in the same state as if |
| 320 | * constructed using the @c basic_stream_socket(const executor_type&) |
| 321 | * constructor. |
| 322 | */ |
| 323 | template <typename Protocol1, typename Executor1> |
| 324 | constraint_t< |
| 325 | is_convertible<Protocol1, Protocol>::value |
| 326 | && is_convertible<Executor1, Executor>::value, |
| 327 | basic_stream_socket& |
| 328 | > operator=(basic_stream_socket<Protocol1, Executor1>&& other) |
| 329 | { |
| 330 | basic_socket<Protocol, Executor>::operator=(std::move(other)); |
| 331 | return *this; |
| 332 | } |
| 333 | |
| 334 | /// Destroys the socket. |
| 335 | /** |
| 336 | * This function destroys the socket, cancelling any outstanding asynchronous |
| 337 | * operations associated with the socket as if by calling @c cancel. |
| 338 | */ |
| 339 | ~basic_stream_socket() |
| 340 | { |
| 341 | } |
| 342 | |
| 343 | /// Send some data on the socket. |
| 344 | /** |
| 345 | * This function is used to send data on the stream socket. The function |
| 346 | * call will block until one or more bytes of the data has been sent |
| 347 | * successfully, or an until error occurs. |
| 348 | * |
| 349 | * @param buffers One or more data buffers to be sent on the socket. |
| 350 | * |
| 351 | * @returns The number of bytes sent. |
| 352 | * |
| 353 | * @throws boost::system::system_error Thrown on failure. |
| 354 | * |
| 355 | * @note The send operation may not transmit all of the data to the peer. |
| 356 | * Consider using the @ref write function if you need to ensure that all data |
| 357 | * is written before the blocking operation completes. |
| 358 | * |
| 359 | * @par Example |
| 360 | * To send a single data buffer use the @ref buffer function as follows: |
| 361 | * @code |
| 362 | * socket.send(boost::asio::buffer(data, size)); |
| 363 | * @endcode |
| 364 | * See the @ref buffer documentation for information on sending multiple |
| 365 | * buffers in one go, and how to use it with arrays, boost::array or |
| 366 | * std::vector. |
| 367 | */ |
| 368 | template <typename ConstBufferSequence> |
| 369 | std::size_t send(const ConstBufferSequence& buffers) |
| 370 | { |
| 371 | boost::system::error_code ec; |
| 372 | std::size_t s = this->impl_.get_service().send( |
| 373 | this->impl_.get_implementation(), buffers, 0, ec); |
| 374 | boost::asio::detail::throw_error(ec, "send"); |
| 375 | return s; |
| 376 | } |
| 377 | |
| 378 | /// Send some data on the socket. |
| 379 | /** |
| 380 | * This function is used to send data on the stream socket. The function |
| 381 | * call will block until one or more bytes of the data has been sent |
| 382 | * successfully, or an until error occurs. |
| 383 | * |
| 384 | * @param buffers One or more data buffers to be sent on the socket. |
| 385 | * |
| 386 | * @param flags Flags specifying how the send call is to be made. |
| 387 | * |
| 388 | * @returns The number of bytes sent. |
| 389 | * |
| 390 | * @throws boost::system::system_error Thrown on failure. |
| 391 | * |
| 392 | * @note The send operation may not transmit all of the data to the peer. |
| 393 | * Consider using the @ref write function if you need to ensure that all data |
| 394 | * is written before the blocking operation completes. |
| 395 | * |
| 396 | * @par Example |
| 397 | * To send a single data buffer use the @ref buffer function as follows: |
| 398 | * @code |
| 399 | * socket.send(boost::asio::buffer(data, size), 0); |
| 400 | * @endcode |
| 401 | * See the @ref buffer documentation for information on sending multiple |
| 402 | * buffers in one go, and how to use it with arrays, boost::array or |
| 403 | * std::vector. |
| 404 | */ |
| 405 | template <typename ConstBufferSequence> |
| 406 | std::size_t send(const ConstBufferSequence& buffers, |
| 407 | socket_base::message_flags flags) |
| 408 | { |
| 409 | boost::system::error_code ec; |
| 410 | std::size_t s = this->impl_.get_service().send( |
| 411 | this->impl_.get_implementation(), buffers, flags, ec); |
| 412 | boost::asio::detail::throw_error(ec, "send"); |
| 413 | return s; |
| 414 | } |
| 415 | |
| 416 | /// Send some data on the socket. |
| 417 | /** |
| 418 | * This function is used to send data on the stream socket. The function |
| 419 | * call will block until one or more bytes of the data has been sent |
| 420 | * successfully, or an until error occurs. |
| 421 | * |
| 422 | * @param buffers One or more data buffers to be sent on the socket. |
| 423 | * |
| 424 | * @param flags Flags specifying how the send call is to be made. |
| 425 | * |
| 426 | * @param ec Set to indicate what error occurred, if any. |
| 427 | * |
| 428 | * @returns The number of bytes sent. Returns 0 if an error occurred. |
| 429 | * |
| 430 | * @note The send operation may not transmit all of the data to the peer. |
| 431 | * Consider using the @ref write function if you need to ensure that all data |
| 432 | * is written before the blocking operation completes. |
| 433 | */ |
| 434 | template <typename ConstBufferSequence> |
| 435 | std::size_t send(const ConstBufferSequence& buffers, |
| 436 | socket_base::message_flags flags, boost::system::error_code& ec) |
| 437 | { |
| 438 | return this->impl_.get_service().send( |
| 439 | this->impl_.get_implementation(), buffers, flags, ec); |
| 440 | } |
| 441 | |
| 442 | /// Start an asynchronous send. |
| 443 | /** |
| 444 | * This function is used to asynchronously send data on the stream socket. |
| 445 | * It is an initiating function for an @ref asynchronous_operation, and always |
| 446 | * returns immediately. |
| 447 | * |
| 448 | * @param buffers One or more data buffers to be sent on the socket. Although |
| 449 | * the buffers object may be copied as necessary, ownership of the underlying |
| 450 | * memory blocks is retained by the caller, which must guarantee that they |
| 451 | * remain valid until the completion handler is called. |
| 452 | * |
| 453 | * @param token The @ref completion_token that will be used to produce a |
| 454 | * completion handler, which will be called when the send completes. |
| 455 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 456 | * @ref yield_context, or a function object with the correct completion |
| 457 | * signature. The function signature of the completion handler must be: |
| 458 | * @code void handler( |
| 459 | * const boost::system::error_code& error, // Result of operation. |
| 460 | * std::size_t bytes_transferred // Number of bytes sent. |
| 461 | * ); @endcode |
| 462 | * Regardless of whether the asynchronous operation completes immediately or |
| 463 | * not, the completion handler will not be invoked from within this function. |
| 464 | * On immediate completion, invocation of the handler will be performed in a |
| 465 | * manner equivalent to using boost::asio::async_immediate(). |
| 466 | * |
| 467 | * @par Completion Signature |
| 468 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 469 | * |
| 470 | * @note The send operation may not transmit all of the data to the peer. |
| 471 | * Consider using the @ref async_write function if you need to ensure that all |
| 472 | * data is written before the asynchronous operation completes. |
| 473 | * |
| 474 | * @par Example |
| 475 | * To send a single data buffer use the @ref buffer function as follows: |
| 476 | * @code |
| 477 | * socket.async_send(boost::asio::buffer(data, size), handler); |
| 478 | * @endcode |
| 479 | * See the @ref buffer documentation for information on sending multiple |
| 480 | * buffers in one go, and how to use it with arrays, boost::array or |
| 481 | * std::vector. |
| 482 | * |
| 483 | * @par Per-Operation Cancellation |
| 484 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 485 | * cancellation for the following boost::asio::cancellation_type values: |
| 486 | * |
| 487 | * @li @c cancellation_type::terminal |
| 488 | * |
| 489 | * @li @c cancellation_type::partial |
| 490 | * |
| 491 | * @li @c cancellation_type::total |
| 492 | */ |
| 493 | template <typename ConstBufferSequence, |
| 494 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 495 | std::size_t))typename WriteToken = default_completion_token_t<executor_type>> |
| 496 | auto async_send(const ConstBufferSequence& buffers, |
| 497 | WriteToken&& token = default_completion_token_t<executor_type>()) |
| 498 | -> decltype( |
| 499 | async_initiate<WriteToken, |
| 500 | void (boost::system::error_code, std::size_t)>( |
| 501 | declval<initiate_async_send>(), token, |
| 502 | buffers, socket_base::message_flags(0))) |
| 503 | { |
| 504 | return async_initiate<WriteToken, |
| 505 | void (boost::system::error_code, std::size_t)>( |
| 506 | initiate_async_send(this), token, |
| 507 | buffers, socket_base::message_flags(0)); |
| 508 | } |
| 509 | |
| 510 | /// Start an asynchronous send. |
| 511 | /** |
| 512 | * This function is used to asynchronously send data on the stream socket. |
| 513 | * It is an initiating function for an @ref asynchronous_operation, and always |
| 514 | * returns immediately. |
| 515 | * |
| 516 | * @param buffers One or more data buffers to be sent on the socket. Although |
| 517 | * the buffers object may be copied as necessary, ownership of the underlying |
| 518 | * memory blocks is retained by the caller, which must guarantee that they |
| 519 | * remain valid until the completion handler is called. |
| 520 | * |
| 521 | * @param flags Flags specifying how the send call is to be made. |
| 522 | * |
| 523 | * @param token The @ref completion_token that will be used to produce a |
| 524 | * completion handler, which will be called when the send completes. |
| 525 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 526 | * @ref yield_context, or a function object with the correct completion |
| 527 | * signature. The function signature of the completion handler must be: |
| 528 | * @code void handler( |
| 529 | * const boost::system::error_code& error, // Result of operation. |
| 530 | * std::size_t bytes_transferred // Number of bytes sent. |
| 531 | * ); @endcode |
| 532 | * Regardless of whether the asynchronous operation completes immediately or |
| 533 | * not, the completion handler will not be invoked from within this function. |
| 534 | * On immediate completion, invocation of the handler will be performed in a |
| 535 | * manner equivalent to using boost::asio::async_immediate(). |
| 536 | * |
| 537 | * @par Completion Signature |
| 538 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 539 | * |
| 540 | * @note The send operation may not transmit all of the data to the peer. |
| 541 | * Consider using the @ref async_write function if you need to ensure that all |
| 542 | * data is written before the asynchronous operation completes. |
| 543 | * |
| 544 | * @par Example |
| 545 | * To send a single data buffer use the @ref buffer function as follows: |
| 546 | * @code |
| 547 | * socket.async_send(boost::asio::buffer(data, size), 0, handler); |
| 548 | * @endcode |
| 549 | * See the @ref buffer documentation for information on sending multiple |
| 550 | * buffers in one go, and how to use it with arrays, boost::array or |
| 551 | * std::vector. |
| 552 | * |
| 553 | * @par Per-Operation Cancellation |
| 554 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 555 | * cancellation for the following boost::asio::cancellation_type values: |
| 556 | * |
| 557 | * @li @c cancellation_type::terminal |
| 558 | * |
| 559 | * @li @c cancellation_type::partial |
| 560 | * |
| 561 | * @li @c cancellation_type::total |
| 562 | */ |
| 563 | template <typename ConstBufferSequence, |
| 564 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 565 | std::size_t))typename WriteToken = default_completion_token_t<executor_type>> |
| 566 | auto async_send(const ConstBufferSequence& buffers, |
| 567 | socket_base::message_flags flags, |
| 568 | WriteToken&& token = default_completion_token_t<executor_type>()) |
| 569 | -> decltype( |
| 570 | async_initiate<WriteToken, |
| 571 | void (boost::system::error_code, std::size_t)>( |
| 572 | declval<initiate_async_send>(), token, buffers, flags)) |
| 573 | { |
| 574 | return async_initiate<WriteToken, |
| 575 | void (boost::system::error_code, std::size_t)>( |
| 576 | initiate_async_send(this), token, buffers, flags); |
| 577 | } |
| 578 | |
| 579 | /// Receive some data on the socket. |
| 580 | /** |
| 581 | * This function is used to receive data on the stream socket. The function |
| 582 | * call will block until one or more bytes of data has been received |
| 583 | * successfully, or until an error occurs. |
| 584 | * |
| 585 | * @param buffers One or more buffers into which the data will be received. |
| 586 | * |
| 587 | * @returns The number of bytes received. |
| 588 | * |
| 589 | * @throws boost::system::system_error Thrown on failure. An error code of |
| 590 | * boost::asio::error::eof indicates that the connection was closed by the |
| 591 | * peer. |
| 592 | * |
| 593 | * @note The receive operation may not receive all of the requested number of |
| 594 | * bytes. Consider using the @ref read function if you need to ensure that the |
| 595 | * requested amount of data is read before the blocking operation completes. |
| 596 | * |
| 597 | * @par Example |
| 598 | * To receive into a single data buffer use the @ref buffer function as |
| 599 | * follows: |
| 600 | * @code |
| 601 | * socket.receive(boost::asio::buffer(data, size)); |
| 602 | * @endcode |
| 603 | * See the @ref buffer documentation for information on receiving into |
| 604 | * multiple buffers in one go, and how to use it with arrays, boost::array or |
| 605 | * std::vector. |
| 606 | */ |
| 607 | template <typename MutableBufferSequence> |
| 608 | std::size_t receive(const MutableBufferSequence& buffers) |
| 609 | { |
| 610 | boost::system::error_code ec; |
| 611 | std::size_t s = this->impl_.get_service().receive( |
| 612 | this->impl_.get_implementation(), buffers, 0, ec); |
| 613 | boost::asio::detail::throw_error(ec, "receive"); |
| 614 | return s; |
| 615 | } |
| 616 | |
| 617 | /// Receive some data on the socket. |
| 618 | /** |
| 619 | * This function is used to receive data on the stream socket. The function |
| 620 | * call will block until one or more bytes of data has been received |
| 621 | * successfully, or until an error occurs. |
| 622 | * |
| 623 | * @param buffers One or more buffers into which the data will be received. |
| 624 | * |
| 625 | * @param flags Flags specifying how the receive call is to be made. |
| 626 | * |
| 627 | * @returns The number of bytes received. |
| 628 | * |
| 629 | * @throws boost::system::system_error Thrown on failure. An error code of |
| 630 | * boost::asio::error::eof indicates that the connection was closed by the |
| 631 | * peer. |
| 632 | * |
| 633 | * @note The receive operation may not receive all of the requested number of |
| 634 | * bytes. Consider using the @ref read function if you need to ensure that the |
| 635 | * requested amount of data is read before the blocking operation completes. |
| 636 | * |
| 637 | * @par Example |
| 638 | * To receive into a single data buffer use the @ref buffer function as |
| 639 | * follows: |
| 640 | * @code |
| 641 | * socket.receive(boost::asio::buffer(data, size), 0); |
| 642 | * @endcode |
| 643 | * See the @ref buffer documentation for information on receiving into |
| 644 | * multiple buffers in one go, and how to use it with arrays, boost::array or |
| 645 | * std::vector. |
| 646 | */ |
| 647 | template <typename MutableBufferSequence> |
| 648 | std::size_t receive(const MutableBufferSequence& buffers, |
| 649 | socket_base::message_flags flags) |
| 650 | { |
| 651 | boost::system::error_code ec; |
| 652 | std::size_t s = this->impl_.get_service().receive( |
| 653 | this->impl_.get_implementation(), buffers, flags, ec); |
| 654 | boost::asio::detail::throw_error(ec, "receive"); |
| 655 | return s; |
| 656 | } |
| 657 | |
| 658 | /// Receive some data on a connected socket. |
| 659 | /** |
| 660 | * This function is used to receive data on the stream socket. The function |
| 661 | * call will block until one or more bytes of data has been received |
| 662 | * successfully, or until an error occurs. |
| 663 | * |
| 664 | * @param buffers One or more buffers into which the data will be received. |
| 665 | * |
| 666 | * @param flags Flags specifying how the receive call is to be made. |
| 667 | * |
| 668 | * @param ec Set to indicate what error occurred, if any. |
| 669 | * |
| 670 | * @returns The number of bytes received. Returns 0 if an error occurred. |
| 671 | * |
| 672 | * @note The receive operation may not receive all of the requested number of |
| 673 | * bytes. Consider using the @ref read function if you need to ensure that the |
| 674 | * requested amount of data is read before the blocking operation completes. |
| 675 | */ |
| 676 | template <typename MutableBufferSequence> |
| 677 | std::size_t receive(const MutableBufferSequence& buffers, |
| 678 | socket_base::message_flags flags, boost::system::error_code& ec) |
| 679 | { |
| 680 | return this->impl_.get_service().receive( |
| 681 | this->impl_.get_implementation(), buffers, flags, ec); |
| 682 | } |
| 683 | |
| 684 | /// Start an asynchronous receive. |
| 685 | /** |
| 686 | * This function is used to asynchronously receive data from the stream |
| 687 | * socket. It is an initiating function for an @ref asynchronous_operation, |
| 688 | * and always returns immediately. |
| 689 | * |
| 690 | * @param buffers One or more buffers into which the data will be received. |
| 691 | * Although the buffers object may be copied as necessary, ownership of the |
| 692 | * underlying memory blocks is retained by the caller, which must guarantee |
| 693 | * that they remain valid until the completion handler is called. |
| 694 | * |
| 695 | * @param token The @ref completion_token that will be used to produce a |
| 696 | * completion handler, which will be called when the receive completes. |
| 697 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 698 | * @ref yield_context, or a function object with the correct completion |
| 699 | * signature. The function signature of the completion handler must be: |
| 700 | * @code void handler( |
| 701 | * const boost::system::error_code& error, // Result of operation. |
| 702 | * std::size_t bytes_transferred // Number of bytes received. |
| 703 | * ); @endcode |
| 704 | * Regardless of whether the asynchronous operation completes immediately or |
| 705 | * not, the completion handler will not be invoked from within this function. |
| 706 | * On immediate completion, invocation of the handler will be performed in a |
| 707 | * manner equivalent to using boost::asio::async_immediate(). |
| 708 | * |
| 709 | * @par Completion Signature |
| 710 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 711 | * |
| 712 | * @note The receive operation may not receive all of the requested number of |
| 713 | * bytes. Consider using the @ref async_read function if you need to ensure |
| 714 | * that the requested amount of data is received before the asynchronous |
| 715 | * operation completes. |
| 716 | * |
| 717 | * @par Example |
| 718 | * To receive into a single data buffer use the @ref buffer function as |
| 719 | * follows: |
| 720 | * @code |
| 721 | * socket.async_receive(boost::asio::buffer(data, size), handler); |
| 722 | * @endcode |
| 723 | * See the @ref buffer documentation for information on receiving into |
| 724 | * multiple buffers in one go, and how to use it with arrays, boost::array or |
| 725 | * std::vector. |
| 726 | * |
| 727 | * @par Per-Operation Cancellation |
| 728 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 729 | * cancellation for the following boost::asio::cancellation_type values: |
| 730 | * |
| 731 | * @li @c cancellation_type::terminal |
| 732 | * |
| 733 | * @li @c cancellation_type::partial |
| 734 | * |
| 735 | * @li @c cancellation_type::total |
| 736 | */ |
| 737 | template <typename MutableBufferSequence, |
| 738 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 739 | std::size_t))typename ReadToken = default_completion_token_t<executor_type>> |
| 740 | auto async_receive(const MutableBufferSequence& buffers, |
| 741 | ReadToken&& token = default_completion_token_t<executor_type>()) |
| 742 | -> decltype( |
| 743 | async_initiate<ReadToken, |
| 744 | void (boost::system::error_code, std::size_t)>( |
| 745 | declval<initiate_async_receive>(), token, |
| 746 | buffers, socket_base::message_flags(0))) |
| 747 | { |
| 748 | return async_initiate<ReadToken, |
| 749 | void (boost::system::error_code, std::size_t)>( |
| 750 | initiate_async_receive(this), token, |
| 751 | buffers, socket_base::message_flags(0)); |
| 752 | } |
| 753 | |
| 754 | /// Start an asynchronous receive. |
| 755 | /** |
| 756 | * This function is used to asynchronously receive data from the stream |
| 757 | * socket. It is an initiating function for an @ref asynchronous_operation, |
| 758 | * and always returns immediately. |
| 759 | * |
| 760 | * @param buffers One or more buffers into which the data will be received. |
| 761 | * Although the buffers object may be copied as necessary, ownership of the |
| 762 | * underlying memory blocks is retained by the caller, which must guarantee |
| 763 | * that they remain valid until the completion handler is called. |
| 764 | * |
| 765 | * @param flags Flags specifying how the receive call is to be made. |
| 766 | * |
| 767 | * @param token The @ref completion_token that will be used to produce a |
| 768 | * completion handler, which will be called when the receive completes. |
| 769 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 770 | * @ref yield_context, or a function object with the correct completion |
| 771 | * signature. The function signature of the completion handler must be: |
| 772 | * @code void handler( |
| 773 | * const boost::system::error_code& error, // Result of operation. |
| 774 | * std::size_t bytes_transferred // Number of bytes received. |
| 775 | * ); @endcode |
| 776 | * Regardless of whether the asynchronous operation completes immediately or |
| 777 | * not, the completion handler will not be invoked from within this function. |
| 778 | * On immediate completion, invocation of the handler will be performed in a |
| 779 | * manner equivalent to using boost::asio::async_immediate(). |
| 780 | * |
| 781 | * @par Completion Signature |
| 782 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 783 | * |
| 784 | * @note The receive operation may not receive all of the requested number of |
| 785 | * bytes. Consider using the @ref async_read function if you need to ensure |
| 786 | * that the requested amount of data is received before the asynchronous |
| 787 | * operation completes. |
| 788 | * |
| 789 | * @par Example |
| 790 | * To receive into a single data buffer use the @ref buffer function as |
| 791 | * follows: |
| 792 | * @code |
| 793 | * socket.async_receive(boost::asio::buffer(data, size), 0, handler); |
| 794 | * @endcode |
| 795 | * See the @ref buffer documentation for information on receiving into |
| 796 | * multiple buffers in one go, and how to use it with arrays, boost::array or |
| 797 | * std::vector. |
| 798 | * |
| 799 | * @par Per-Operation Cancellation |
| 800 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 801 | * cancellation for the following boost::asio::cancellation_type values: |
| 802 | * |
| 803 | * @li @c cancellation_type::terminal |
| 804 | * |
| 805 | * @li @c cancellation_type::partial |
| 806 | * |
| 807 | * @li @c cancellation_type::total |
| 808 | */ |
| 809 | template <typename MutableBufferSequence, |
| 810 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 811 | std::size_t))typename ReadToken = default_completion_token_t<executor_type>> |
| 812 | auto async_receive(const MutableBufferSequence& buffers, |
| 813 | socket_base::message_flags flags, |
| 814 | ReadToken&& token = default_completion_token_t<executor_type>()) |
| 815 | -> decltype( |
| 816 | async_initiate<ReadToken, |
| 817 | void (boost::system::error_code, std::size_t)>( |
| 818 | declval<initiate_async_receive>(), token, buffers, flags)) |
| 819 | { |
| 820 | return async_initiate<ReadToken, |
| 821 | void (boost::system::error_code, std::size_t)>( |
| 822 | initiate_async_receive(this), token, buffers, flags); |
| 823 | } |
| 824 | |
| 825 | /// Write some data to the socket. |
| 826 | /** |
| 827 | * This function is used to write data to the stream socket. The function call |
| 828 | * will block until one or more bytes of the data has been written |
| 829 | * successfully, or until an error occurs. |
| 830 | * |
| 831 | * @param buffers One or more data buffers to be written to the socket. |
| 832 | * |
| 833 | * @returns The number of bytes written. |
| 834 | * |
| 835 | * @throws boost::system::system_error Thrown on failure. An error code of |
| 836 | * boost::asio::error::eof indicates that the connection was closed by the |
| 837 | * peer. |
| 838 | * |
| 839 | * @note The write_some operation may not transmit all of the data to the |
| 840 | * peer. Consider using the @ref write function if you need to ensure that |
| 841 | * all data is written before the blocking operation completes. |
| 842 | * |
| 843 | * @par Example |
| 844 | * To write a single data buffer use the @ref buffer function as follows: |
| 845 | * @code |
| 846 | * socket.write_some(boost::asio::buffer(data, size)); |
| 847 | * @endcode |
| 848 | * See the @ref buffer documentation for information on writing multiple |
| 849 | * buffers in one go, and how to use it with arrays, boost::array or |
| 850 | * std::vector. |
| 851 | */ |
| 852 | template <typename ConstBufferSequence> |
| 853 | std::size_t write_some(const ConstBufferSequence& buffers) |
| 854 | { |
| 855 | boost::system::error_code ec; |
| 856 | std::size_t s = this->impl_.get_service().send( |
| 857 | this->impl_.get_implementation(), buffers, 0, ec); |
| 858 | boost::asio::detail::throw_error(ec, "write_some"); |
| 859 | return s; |
| 860 | } |
| 861 | |
| 862 | /// Write some data to the socket. |
| 863 | /** |
| 864 | * This function is used to write data to the stream socket. The function call |
| 865 | * will block until one or more bytes of the data has been written |
| 866 | * successfully, or until an error occurs. |
| 867 | * |
| 868 | * @param buffers One or more data buffers to be written to the socket. |
| 869 | * |
| 870 | * @param ec Set to indicate what error occurred, if any. |
| 871 | * |
| 872 | * @returns The number of bytes written. Returns 0 if an error occurred. |
| 873 | * |
| 874 | * @note The write_some operation may not transmit all of the data to the |
| 875 | * peer. Consider using the @ref write function if you need to ensure that |
| 876 | * all data is written before the blocking operation completes. |
| 877 | */ |
| 878 | template <typename ConstBufferSequence> |
| 879 | std::size_t write_some(const ConstBufferSequence& buffers, |
| 880 | boost::system::error_code& ec) |
| 881 | { |
| 882 | return this->impl_.get_service().send( |
| 883 | this->impl_.get_implementation(), buffers, 0, ec); |
| 884 | } |
| 885 | |
| 886 | /// Start an asynchronous write. |
| 887 | /** |
| 888 | * This function is used to asynchronously write data to the stream socket. |
| 889 | * It is an initiating function for an @ref asynchronous_operation, and always |
| 890 | * returns immediately. |
| 891 | * |
| 892 | * @param buffers One or more data buffers to be written to the socket. |
| 893 | * Although the buffers object may be copied as necessary, ownership of the |
| 894 | * underlying memory blocks is retained by the caller, which must guarantee |
| 895 | * that they remain valid until the completion handler is called. |
| 896 | * |
| 897 | * @param token The @ref completion_token that will be used to produce a |
| 898 | * completion handler, which will be called when the write completes. |
| 899 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 900 | * @ref yield_context, or a function object with the correct completion |
| 901 | * signature. The function signature of the completion handler must be: |
| 902 | * @code void handler( |
| 903 | * const boost::system::error_code& error, // Result of operation. |
| 904 | * std::size_t bytes_transferred // Number of bytes written. |
| 905 | * ); @endcode |
| 906 | * Regardless of whether the asynchronous operation completes immediately or |
| 907 | * not, the completion handler will not be invoked from within this function. |
| 908 | * On immediate completion, invocation of the handler will be performed in a |
| 909 | * manner equivalent to using boost::asio::async_immediate(). |
| 910 | * |
| 911 | * @par Completion Signature |
| 912 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 913 | * |
| 914 | * @note The write operation may not transmit all of the data to the peer. |
| 915 | * Consider using the @ref async_write function if you need to ensure that all |
| 916 | * data is written before the asynchronous operation completes. |
| 917 | * |
| 918 | * @par Example |
| 919 | * To write a single data buffer use the @ref buffer function as follows: |
| 920 | * @code |
| 921 | * socket.async_write_some(boost::asio::buffer(data, size), handler); |
| 922 | * @endcode |
| 923 | * See the @ref buffer documentation for information on writing multiple |
| 924 | * buffers in one go, and how to use it with arrays, boost::array or |
| 925 | * std::vector. |
| 926 | * |
| 927 | * @par Per-Operation Cancellation |
| 928 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 929 | * cancellation for the following boost::asio::cancellation_type values: |
| 930 | * |
| 931 | * @li @c cancellation_type::terminal |
| 932 | * |
| 933 | * @li @c cancellation_type::partial |
| 934 | * |
| 935 | * @li @c cancellation_type::total |
| 936 | */ |
| 937 | template <typename ConstBufferSequence, |
| 938 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 939 | std::size_t))typename WriteToken = default_completion_token_t<executor_type>> |
| 940 | auto async_write_some(const ConstBufferSequence& buffers, |
| 941 | WriteToken&& token = default_completion_token_t<executor_type>()) |
| 942 | -> decltype( |
| 943 | async_initiate<WriteToken, |
| 944 | void (boost::system::error_code, std::size_t)>( |
| 945 | declval<initiate_async_send>(), token, |
| 946 | buffers, socket_base::message_flags(0))) |
| 947 | { |
| 948 | return async_initiate<WriteToken, |
| 949 | void (boost::system::error_code, std::size_t)>( |
| 950 | initiate_async_send(this), token, |
| 951 | buffers, socket_base::message_flags(0)); |
| 952 | } |
| 953 | |
| 954 | /// Read some data from the socket. |
| 955 | /** |
| 956 | * This function is used to read data from the stream socket. The function |
| 957 | * call will block until one or more bytes of data has been read successfully, |
| 958 | * or until an error occurs. |
| 959 | * |
| 960 | * @param buffers One or more buffers into which the data will be read. |
| 961 | * |
| 962 | * @returns The number of bytes read. |
| 963 | * |
| 964 | * @throws boost::system::system_error Thrown on failure. An error code of |
| 965 | * boost::asio::error::eof indicates that the connection was closed by the |
| 966 | * peer. |
| 967 | * |
| 968 | * @note The read_some operation may not read all of the requested number of |
| 969 | * bytes. Consider using the @ref read function if you need to ensure that |
| 970 | * the requested amount of data is read before the blocking operation |
| 971 | * completes. |
| 972 | * |
| 973 | * @par Example |
| 974 | * To read into a single data buffer use the @ref buffer function as follows: |
| 975 | * @code |
| 976 | * socket.read_some(boost::asio::buffer(data, size)); |
| 977 | * @endcode |
| 978 | * See the @ref buffer documentation for information on reading into multiple |
| 979 | * buffers in one go, and how to use it with arrays, boost::array or |
| 980 | * std::vector. |
| 981 | */ |
| 982 | template <typename MutableBufferSequence> |
| 983 | std::size_t read_some(const MutableBufferSequence& buffers) |
| 984 | { |
| 985 | boost::system::error_code ec; |
| 986 | std::size_t s = this->impl_.get_service().receive( |
| 987 | this->impl_.get_implementation(), buffers, 0, ec); |
| 988 | boost::asio::detail::throw_error(ec, "read_some"); |
| 989 | return s; |
| 990 | } |
| 991 | |
| 992 | /// Read some data from the socket. |
| 993 | /** |
| 994 | * This function is used to read data from the stream socket. The function |
| 995 | * call will block until one or more bytes of data has been read successfully, |
| 996 | * or until an error occurs. |
| 997 | * |
| 998 | * @param buffers One or more buffers into which the data will be read. |
| 999 | * |
| 1000 | * @param ec Set to indicate what error occurred, if any. |
| 1001 | * |
| 1002 | * @returns The number of bytes read. Returns 0 if an error occurred. |
| 1003 | * |
| 1004 | * @note The read_some operation may not read all of the requested number of |
| 1005 | * bytes. Consider using the @ref read function if you need to ensure that |
| 1006 | * the requested amount of data is read before the blocking operation |
| 1007 | * completes. |
| 1008 | */ |
| 1009 | template <typename MutableBufferSequence> |
| 1010 | std::size_t read_some(const MutableBufferSequence& buffers, |
| 1011 | boost::system::error_code& ec) |
| 1012 | { |
| 1013 | return this->impl_.get_service().receive( |
| 1014 | this->impl_.get_implementation(), buffers, 0, ec); |
| 1015 | } |
| 1016 | |
| 1017 | /// Start an asynchronous read. |
| 1018 | /** |
| 1019 | * This function is used to asynchronously read data from the stream socket. |
| 1020 | * socket. It is an initiating function for an @ref asynchronous_operation, |
| 1021 | * and always returns immediately. |
| 1022 | * |
| 1023 | * @param buffers One or more buffers into which the data will be read. |
| 1024 | * Although the buffers object may be copied as necessary, ownership of the |
| 1025 | * underlying memory blocks is retained by the caller, which must guarantee |
| 1026 | * that they remain valid until the completion handler is called. |
| 1027 | * |
| 1028 | * @param token The @ref completion_token that will be used to produce a |
| 1029 | * completion handler, which will be called when the read completes. |
| 1030 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 1031 | * @ref yield_context, or a function object with the correct completion |
| 1032 | * signature. The function signature of the completion handler must be: |
| 1033 | * @code void handler( |
| 1034 | * const boost::system::error_code& error, // Result of operation. |
| 1035 | * std::size_t bytes_transferred // Number of bytes read. |
| 1036 | * ); @endcode |
| 1037 | * Regardless of whether the asynchronous operation completes immediately or |
| 1038 | * not, the completion handler will not be invoked from within this function. |
| 1039 | * On immediate completion, invocation of the handler will be performed in a |
| 1040 | * manner equivalent to using boost::asio::async_immediate(). |
| 1041 | * |
| 1042 | * @par Completion Signature |
| 1043 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 1044 | * |
| 1045 | * @note The read operation may not read all of the requested number of bytes. |
| 1046 | * Consider using the @ref async_read function if you need to ensure that the |
| 1047 | * requested amount of data is read before the asynchronous operation |
| 1048 | * completes. |
| 1049 | * |
| 1050 | * @par Example |
| 1051 | * To read into a single data buffer use the @ref buffer function as follows: |
| 1052 | * @code |
| 1053 | * socket.async_read_some(boost::asio::buffer(data, size), handler); |
| 1054 | * @endcode |
| 1055 | * See the @ref buffer documentation for information on reading into multiple |
| 1056 | * buffers in one go, and how to use it with arrays, boost::array or |
| 1057 | * std::vector. |
| 1058 | * |
| 1059 | * @par Per-Operation Cancellation |
| 1060 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 1061 | * cancellation for the following boost::asio::cancellation_type values: |
| 1062 | * |
| 1063 | * @li @c cancellation_type::terminal |
| 1064 | * |
| 1065 | * @li @c cancellation_type::partial |
| 1066 | * |
| 1067 | * @li @c cancellation_type::total |
| 1068 | */ |
| 1069 | template <typename MutableBufferSequence, |
| 1070 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 1071 | std::size_t))typename ReadToken = default_completion_token_t<executor_type>> |
| 1072 | auto async_read_some(const MutableBufferSequence& buffers, |
| 1073 | ReadToken&& token = default_completion_token_t<executor_type>()) |
| 1074 | -> decltype( |
| 1075 | async_initiate<ReadToken, |
| 1076 | void (boost::system::error_code, std::size_t)>( |
| 1077 | declval<initiate_async_receive>(), token, |
| 1078 | buffers, socket_base::message_flags(0))) |
| 1079 | { |
| 1080 | return async_initiate<ReadToken, |
| 1081 | void (boost::system::error_code, std::size_t)>( |
| 1082 | initiate_async_receive(this), token, |
| 1083 | buffers, socket_base::message_flags(0)); |
| 1084 | } |
| 1085 | |
| 1086 | private: |
| 1087 | // Disallow copying and assignment. |
| 1088 | basic_stream_socket(const basic_stream_socket&) = delete; |
| 1089 | basic_stream_socket& operator=(const basic_stream_socket&) = delete; |
| 1090 | |
| 1091 | class initiate_async_send |
| 1092 | { |
| 1093 | public: |
| 1094 | typedef Executor executor_type; |
| 1095 | |
| 1096 | explicit initiate_async_send(basic_stream_socket* self) |
| 1097 | : self_(self) |
| 1098 | { |
| 1099 | } |
| 1100 | |
| 1101 | const executor_type& get_executor() const noexcept |
| 1102 | { |
| 1103 | return self_->get_executor(); |
| 1104 | } |
| 1105 | |
| 1106 | template <typename WriteHandler, typename ConstBufferSequence> |
| 1107 | void operator()(WriteHandler&& handler, |
| 1108 | const ConstBufferSequence& buffers, |
| 1109 | socket_base::message_flags flags) const |
| 1110 | { |
| 1111 | // If you get an error on the following line it means that your handler |
| 1112 | // does not meet the documented type requirements for a WriteHandler. |
| 1113 | BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler)typedef typename ::boost::asio::async_result< typename ::boost ::asio::decay<WriteHandler>::type, void(boost::system:: error_code, std::size_t)>::completion_handler_type asio_true_handler_type ; static_assert(sizeof(boost::asio::detail::two_arg_handler_test ( boost::asio::detail::rvref< asio_true_handler_type>() , static_cast<const boost::system::error_code*>(0), static_cast <const std::size_t*>(0))) == 1, "WriteHandler type requirements not met" ); typedef boost::asio::detail::handler_type_requirements< sizeof( boost::asio::detail::argbyv( boost::asio::detail::rvref < asio_true_handler_type>())) + sizeof( boost::asio::detail ::rorlvref< asio_true_handler_type>()( boost::asio::detail ::lvref<const boost::system::error_code>(), boost::asio ::detail::lvref<const std::size_t>()), char(0))> __attribute__ ((__unused__)) type_check; |
| 1114 | |
| 1115 | detail::non_const_lvalue<WriteHandler> handler2(handler); |
| 1116 | self_->impl_.get_service().async_send( |
| 1117 | self_->impl_.get_implementation(), buffers, flags, |
| 1118 | handler2.value, self_->impl_.get_executor()); |
| 1119 | } |
| 1120 | |
| 1121 | private: |
| 1122 | basic_stream_socket* self_; |
| 1123 | }; |
| 1124 | |
| 1125 | class initiate_async_receive |
| 1126 | { |
| 1127 | public: |
| 1128 | typedef Executor executor_type; |
| 1129 | |
| 1130 | explicit initiate_async_receive(basic_stream_socket* self) |
| 1131 | : self_(self) |
| 1132 | { |
| 1133 | } |
| 1134 | |
| 1135 | const executor_type& get_executor() const noexcept |
| 1136 | { |
| 1137 | return self_->get_executor(); |
| 1138 | } |
| 1139 | |
| 1140 | template <typename ReadHandler, typename MutableBufferSequence> |
| 1141 | void operator()(ReadHandler&& handler, |
| 1142 | const MutableBufferSequence& buffers, |
| 1143 | socket_base::message_flags flags) const |
| 1144 | { |
| 1145 | // If you get an error on the following line it means that your handler |
| 1146 | // does not meet the documented type requirements for a ReadHandler. |
| 1147 | BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler)typedef typename ::boost::asio::async_result< typename ::boost ::asio::decay<ReadHandler>::type, void(boost::system::error_code , std::size_t)>::completion_handler_type asio_true_handler_type ; static_assert(sizeof(boost::asio::detail::two_arg_handler_test ( boost::asio::detail::rvref< asio_true_handler_type>() , static_cast<const boost::system::error_code*>(0), static_cast <const std::size_t*>(0))) == 1, "ReadHandler type requirements not met" ); typedef boost::asio::detail::handler_type_requirements< sizeof( boost::asio::detail::argbyv( boost::asio::detail::rvref < asio_true_handler_type>())) + sizeof( boost::asio::detail ::rorlvref< asio_true_handler_type>()( boost::asio::detail ::lvref<const boost::system::error_code>(), boost::asio ::detail::lvref<const std::size_t>()), char(0))> __attribute__ ((__unused__)) type_check; |
| 1148 | |
| 1149 | detail::non_const_lvalue<ReadHandler> handler2(handler); |
| 1150 | self_->impl_.get_service().async_receive( |
| 1151 | self_->impl_.get_implementation(), buffers, flags, |
| 1152 | handler2.value, self_->impl_.get_executor()); |
| 1153 | } |
| 1154 | |
| 1155 | private: |
| 1156 | basic_stream_socket* self_; |
| 1157 | }; |
| 1158 | }; |
| 1159 | |
| 1160 | } // namespace asio |
| 1161 | } // namespace boost |
| 1162 | |
| 1163 | #include <boost/asio/detail/pop_options.hpp> |
| 1164 | |
| 1165 | #endif // BOOST_ASIO_BASIC_STREAM_SOCKET_HPP |
| 1 | // |
| 2 | // basic_socket.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 | |
| 11 | #ifndef BOOST_ASIO_BASIC_SOCKET_HPP |
| 12 | #define BOOST_ASIO_BASIC_SOCKET_HPP |
| 13 | |
| 14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) |
| 15 | # pragma once |
| 16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
| 17 | |
| 18 | #include <utility> |
| 19 | #include <boost/asio/any_io_executor.hpp> |
| 20 | #include <boost/asio/detail/config.hpp> |
| 21 | #include <boost/asio/async_result.hpp> |
| 22 | #include <boost/asio/detail/handler_type_requirements.hpp> |
| 23 | #include <boost/asio/detail/io_object_impl.hpp> |
| 24 | #include <boost/asio/detail/non_const_lvalue.hpp> |
| 25 | #include <boost/asio/detail/throw_error.hpp> |
| 26 | #include <boost/asio/detail/type_traits.hpp> |
| 27 | #include <boost/asio/error.hpp> |
| 28 | #include <boost/asio/execution_context.hpp> |
| 29 | #include <boost/asio/post.hpp> |
| 30 | #include <boost/asio/socket_base.hpp> |
| 31 | |
| 32 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) |
| 33 | # include <boost/asio/detail/null_socket_service.hpp> |
| 34 | #elif defined(BOOST_ASIO_HAS_IOCP) |
| 35 | # include <boost/asio/detail/win_iocp_socket_service.hpp> |
| 36 | #elif defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 37 | # include <boost/asio/detail/io_uring_socket_service.hpp> |
| 38 | #else |
| 39 | # include <boost/asio/detail/reactive_socket_service.hpp> |
| 40 | #endif |
| 41 | |
| 42 | #include <boost/asio/detail/push_options.hpp> |
| 43 | |
| 44 | namespace boost { |
| 45 | namespace asio { |
| 46 | |
| 47 | #if !defined(BOOST_ASIO_BASIC_SOCKET_FWD_DECL) |
| 48 | #define BOOST_ASIO_BASIC_SOCKET_FWD_DECL |
| 49 | |
| 50 | // Forward declaration with defaulted arguments. |
| 51 | template <typename Protocol, typename Executor = any_io_executor> |
| 52 | class basic_socket; |
| 53 | |
| 54 | #endif // !defined(BOOST_ASIO_BASIC_SOCKET_FWD_DECL) |
| 55 | |
| 56 | /// Provides socket functionality. |
| 57 | /** |
| 58 | * The basic_socket class template provides functionality that is common to both |
| 59 | * stream-oriented and datagram-oriented sockets. |
| 60 | * |
| 61 | * @par Thread Safety |
| 62 | * @e Distinct @e objects: Safe.@n |
| 63 | * @e Shared @e objects: Unsafe. |
| 64 | */ |
| 65 | template <typename Protocol, typename Executor> |
| 66 | class basic_socket |
| 67 | : public socket_base |
| 68 | { |
| 69 | private: |
| 70 | class initiate_async_connect; |
| 71 | class initiate_async_wait; |
| 72 | |
| 73 | public: |
| 74 | /// The type of the executor associated with the object. |
| 75 | typedef Executor executor_type; |
| 76 | |
| 77 | /// Rebinds the socket type to another executor. |
| 78 | template <typename Executor1> |
| 79 | struct rebind_executor |
| 80 | { |
| 81 | /// The socket type when rebound to the specified executor. |
| 82 | typedef basic_socket<Protocol, Executor1> other; |
| 83 | }; |
| 84 | |
| 85 | /// The native representation of a socket. |
| 86 | #if defined(GENERATING_DOCUMENTATION) |
| 87 | typedef implementation_defined native_handle_type; |
| 88 | #elif defined(BOOST_ASIO_WINDOWS_RUNTIME) |
| 89 | typedef typename detail::null_socket_service< |
| 90 | Protocol>::native_handle_type native_handle_type; |
| 91 | #elif defined(BOOST_ASIO_HAS_IOCP) |
| 92 | typedef typename detail::win_iocp_socket_service< |
| 93 | Protocol>::native_handle_type native_handle_type; |
| 94 | #elif defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 95 | typedef typename detail::io_uring_socket_service< |
| 96 | Protocol>::native_handle_type native_handle_type; |
| 97 | #else |
| 98 | typedef typename detail::reactive_socket_service< |
| 99 | Protocol>::native_handle_type native_handle_type; |
| 100 | #endif |
| 101 | |
| 102 | /// The protocol type. |
| 103 | typedef Protocol protocol_type; |
| 104 | |
| 105 | /// The endpoint type. |
| 106 | typedef typename Protocol::endpoint endpoint_type; |
| 107 | |
| 108 | #if !defined(BOOST_ASIO_NO_EXTENSIONS) |
| 109 | /// A basic_socket is always the lowest layer. |
| 110 | typedef basic_socket<Protocol, Executor> lowest_layer_type; |
| 111 | #endif // !defined(BOOST_ASIO_NO_EXTENSIONS) |
| 112 | |
| 113 | /// Construct a basic_socket without opening it. |
| 114 | /** |
| 115 | * This constructor creates a socket without opening it. |
| 116 | * |
| 117 | * @param ex The I/O executor that the socket will use, by default, to |
| 118 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 119 | */ |
| 120 | explicit basic_socket(const executor_type& ex) |
| 121 | : impl_(0, ex) |
| 122 | { |
| 123 | } |
| 124 | |
| 125 | /// Construct a basic_socket without opening it. |
| 126 | /** |
| 127 | * This constructor creates a socket without opening it. |
| 128 | * |
| 129 | * @param context An execution context which provides the I/O executor that |
| 130 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 131 | * operations performed on the socket. |
| 132 | */ |
| 133 | template <typename ExecutionContext> |
| 134 | explicit basic_socket(ExecutionContext& context, |
| 135 | constraint_t< |
| 136 | is_convertible<ExecutionContext&, execution_context&>::value |
| 137 | > = 0) |
| 138 | : impl_(0, 0, context) |
| 139 | { |
| 140 | } |
| 141 | |
| 142 | /// Construct and open a basic_socket. |
| 143 | /** |
| 144 | * This constructor creates and opens a socket. |
| 145 | * |
| 146 | * @param ex The I/O executor that the socket will use, by default, to |
| 147 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 148 | * |
| 149 | * @param protocol An object specifying protocol parameters to be used. |
| 150 | * |
| 151 | * @throws boost::system::system_error Thrown on failure. |
| 152 | */ |
| 153 | basic_socket(const executor_type& ex, const protocol_type& protocol) |
| 154 | : impl_(0, ex) |
| 155 | { |
| 156 | boost::system::error_code ec; |
| 157 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 158 | boost::asio::detail::throw_error(ec, "open"); |
| 159 | } |
| 160 | |
| 161 | /// Construct and open a basic_socket. |
| 162 | /** |
| 163 | * This constructor creates and opens a socket. |
| 164 | * |
| 165 | * @param context An execution context which provides the I/O executor that |
| 166 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 167 | * operations performed on the socket. |
| 168 | * |
| 169 | * @param protocol An object specifying protocol parameters to be used. |
| 170 | * |
| 171 | * @throws boost::system::system_error Thrown on failure. |
| 172 | */ |
| 173 | template <typename ExecutionContext> |
| 174 | basic_socket(ExecutionContext& context, const protocol_type& protocol, |
| 175 | constraint_t< |
| 176 | is_convertible<ExecutionContext&, execution_context&>::value, |
| 177 | defaulted_constraint |
| 178 | > = defaulted_constraint()) |
| 179 | : impl_(0, 0, context) |
| 180 | { |
| 181 | boost::system::error_code ec; |
| 182 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 183 | boost::asio::detail::throw_error(ec, "open"); |
| 184 | } |
| 185 | |
| 186 | /// Construct a basic_socket, opening it and binding it to the given local |
| 187 | /// endpoint. |
| 188 | /** |
| 189 | * This constructor creates a socket and automatically opens it bound to the |
| 190 | * specified endpoint on the local machine. The protocol used is the protocol |
| 191 | * associated with the given endpoint. |
| 192 | * |
| 193 | * @param ex The I/O executor that the socket will use, by default, to |
| 194 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 195 | * |
| 196 | * @param endpoint An endpoint on the local machine to which the socket will |
| 197 | * be bound. |
| 198 | * |
| 199 | * @throws boost::system::system_error Thrown on failure. |
| 200 | */ |
| 201 | basic_socket(const executor_type& ex, const endpoint_type& endpoint) |
| 202 | : impl_(0, ex) |
| 203 | { |
| 204 | boost::system::error_code ec; |
| 205 | const protocol_type protocol = endpoint.protocol(); |
| 206 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 207 | boost::asio::detail::throw_error(ec, "open"); |
| 208 | impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); |
| 209 | boost::asio::detail::throw_error(ec, "bind"); |
| 210 | } |
| 211 | |
| 212 | /// Construct a basic_socket, opening it and binding it to the given local |
| 213 | /// endpoint. |
| 214 | /** |
| 215 | * This constructor creates a socket and automatically opens it bound to the |
| 216 | * specified endpoint on the local machine. The protocol used is the protocol |
| 217 | * associated with the given endpoint. |
| 218 | * |
| 219 | * @param context An execution context which provides the I/O executor that |
| 220 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 221 | * operations performed on the socket. |
| 222 | * |
| 223 | * @param endpoint An endpoint on the local machine to which the socket will |
| 224 | * be bound. |
| 225 | * |
| 226 | * @throws boost::system::system_error Thrown on failure. |
| 227 | */ |
| 228 | template <typename ExecutionContext> |
| 229 | basic_socket(ExecutionContext& context, const endpoint_type& endpoint, |
| 230 | constraint_t< |
| 231 | is_convertible<ExecutionContext&, execution_context&>::value |
| 232 | > = 0) |
| 233 | : impl_(0, 0, context) |
| 234 | { |
| 235 | boost::system::error_code ec; |
| 236 | const protocol_type protocol = endpoint.protocol(); |
| 237 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 238 | boost::asio::detail::throw_error(ec, "open"); |
| 239 | impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); |
| 240 | boost::asio::detail::throw_error(ec, "bind"); |
| 241 | } |
| 242 | |
| 243 | /// Construct a basic_socket on an existing native socket. |
| 244 | /** |
| 245 | * This constructor creates a socket object to hold an existing native socket. |
| 246 | * |
| 247 | * @param ex The I/O executor that the socket will use, by default, to |
| 248 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 249 | * |
| 250 | * @param protocol An object specifying protocol parameters to be used. |
| 251 | * |
| 252 | * @param native_socket A native socket. |
| 253 | * |
| 254 | * @throws boost::system::system_error Thrown on failure. |
| 255 | */ |
| 256 | basic_socket(const executor_type& ex, const protocol_type& protocol, |
| 257 | const native_handle_type& native_socket) |
| 258 | : impl_(0, ex) |
| 259 | { |
| 260 | boost::system::error_code ec; |
| 261 | impl_.get_service().assign(impl_.get_implementation(), |
| 262 | protocol, native_socket, ec); |
| 263 | boost::asio::detail::throw_error(ec, "assign"); |
| 264 | } |
| 265 | |
| 266 | /// Construct a basic_socket on an existing native socket. |
| 267 | /** |
| 268 | * This constructor creates a socket object to hold an existing native socket. |
| 269 | * |
| 270 | * @param context An execution context which provides the I/O executor that |
| 271 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 272 | * operations performed on the socket. |
| 273 | * |
| 274 | * @param protocol An object specifying protocol parameters to be used. |
| 275 | * |
| 276 | * @param native_socket A native socket. |
| 277 | * |
| 278 | * @throws boost::system::system_error Thrown on failure. |
| 279 | */ |
| 280 | template <typename ExecutionContext> |
| 281 | basic_socket(ExecutionContext& context, const protocol_type& protocol, |
| 282 | const native_handle_type& native_socket, |
| 283 | constraint_t< |
| 284 | is_convertible<ExecutionContext&, execution_context&>::value |
| 285 | > = 0) |
| 286 | : impl_(0, 0, context) |
| 287 | { |
| 288 | boost::system::error_code ec; |
| 289 | impl_.get_service().assign(impl_.get_implementation(), |
| 290 | protocol, native_socket, ec); |
| 291 | boost::asio::detail::throw_error(ec, "assign"); |
| 292 | } |
| 293 | |
| 294 | /// Move-construct a basic_socket from another. |
| 295 | /** |
| 296 | * This constructor moves a socket from one object to another. |
| 297 | * |
| 298 | * @param other The other basic_socket object from which the move will |
| 299 | * occur. |
| 300 | * |
| 301 | * @note Following the move, the moved-from object is in the same state as if |
| 302 | * constructed using the @c basic_socket(const executor_type&) constructor. |
| 303 | */ |
| 304 | basic_socket(basic_socket&& other) noexcept |
| 305 | : impl_(std::move(other.impl_)) |
| 306 | { |
| 307 | } |
| 308 | |
| 309 | /// Move-assign a basic_socket from another. |
| 310 | /** |
| 311 | * This assignment operator moves a socket from one object to another. |
| 312 | * |
| 313 | * @param other The other basic_socket object from which the move will |
| 314 | * occur. |
| 315 | * |
| 316 | * @note Following the move, the moved-from object is in the same state as if |
| 317 | * constructed using the @c basic_socket(const executor_type&) constructor. |
| 318 | */ |
| 319 | basic_socket& operator=(basic_socket&& other) |
| 320 | { |
| 321 | impl_ = std::move(other.impl_); |
| 322 | return *this; |
| 323 | } |
| 324 | |
| 325 | // All sockets have access to each other's implementations. |
| 326 | template <typename Protocol1, typename Executor1> |
| 327 | friend class basic_socket; |
| 328 | |
| 329 | /// Move-construct a basic_socket from a socket of another protocol type. |
| 330 | /** |
| 331 | * This constructor moves a socket from one object to another. |
| 332 | * |
| 333 | * @param other The other basic_socket object from which the move will |
| 334 | * occur. |
| 335 | * |
| 336 | * @note Following the move, the moved-from object is in the same state as if |
| 337 | * constructed using the @c basic_socket(const executor_type&) constructor. |
| 338 | */ |
| 339 | template <typename Protocol1, typename Executor1> |
| 340 | basic_socket(basic_socket<Protocol1, Executor1>&& other, |
| 341 | constraint_t< |
| 342 | is_convertible<Protocol1, Protocol>::value |
| 343 | && is_convertible<Executor1, Executor>::value |
| 344 | > = 0) |
| 345 | : impl_(std::move(other.impl_)) |
| 346 | { |
| 347 | } |
| 348 | |
| 349 | /// Move-assign a basic_socket from a socket of another protocol type. |
| 350 | /** |
| 351 | * This assignment operator moves a socket from one object to another. |
| 352 | * |
| 353 | * @param other The other basic_socket object from which the move will |
| 354 | * occur. |
| 355 | * |
| 356 | * @note Following the move, the moved-from object is in the same state as if |
| 357 | * constructed using the @c basic_socket(const executor_type&) constructor. |
| 358 | */ |
| 359 | template <typename Protocol1, typename Executor1> |
| 360 | constraint_t< |
| 361 | is_convertible<Protocol1, Protocol>::value |
| 362 | && is_convertible<Executor1, Executor>::value, |
| 363 | basic_socket& |
| 364 | > operator=(basic_socket<Protocol1, Executor1>&& other) |
| 365 | { |
| 366 | basic_socket tmp(std::move(other)); |
| 367 | impl_ = std::move(tmp.impl_); |
| 368 | return *this; |
| 369 | } |
| 370 | |
| 371 | /// Get the executor associated with the object. |
| 372 | const executor_type& get_executor() noexcept |
| 373 | { |
| 374 | return impl_.get_executor(); |
| 375 | } |
| 376 | |
| 377 | #if !defined(BOOST_ASIO_NO_EXTENSIONS) |
| 378 | /// Get a reference to the lowest layer. |
| 379 | /** |
| 380 | * This function returns a reference to the lowest layer in a stack of |
| 381 | * layers. Since a basic_socket cannot contain any further layers, it simply |
| 382 | * returns a reference to itself. |
| 383 | * |
| 384 | * @return A reference to the lowest layer in the stack of layers. Ownership |
| 385 | * is not transferred to the caller. |
| 386 | */ |
| 387 | lowest_layer_type& lowest_layer() |
| 388 | { |
| 389 | return *this; |
| 390 | } |
| 391 | |
| 392 | /// Get a const reference to the lowest layer. |
| 393 | /** |
| 394 | * This function returns a const reference to the lowest layer in a stack of |
| 395 | * layers. Since a basic_socket cannot contain any further layers, it simply |
| 396 | * returns a reference to itself. |
| 397 | * |
| 398 | * @return A const reference to the lowest layer in the stack of layers. |
| 399 | * Ownership is not transferred to the caller. |
| 400 | */ |
| 401 | const lowest_layer_type& lowest_layer() const |
| 402 | { |
| 403 | return *this; |
| 404 | } |
| 405 | #endif // !defined(BOOST_ASIO_NO_EXTENSIONS) |
| 406 | |
| 407 | /// Open the socket using the specified protocol. |
| 408 | /** |
| 409 | * This function opens the socket so that it will use the specified protocol. |
| 410 | * |
| 411 | * @param protocol An object specifying protocol parameters to be used. |
| 412 | * |
| 413 | * @throws boost::system::system_error Thrown on failure. |
| 414 | * |
| 415 | * @par Example |
| 416 | * @code |
| 417 | * boost::asio::ip::tcp::socket socket(my_context); |
| 418 | * socket.open(boost::asio::ip::tcp::v4()); |
| 419 | * @endcode |
| 420 | */ |
| 421 | void open(const protocol_type& protocol = protocol_type()) |
| 422 | { |
| 423 | boost::system::error_code ec; |
| 424 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 425 | boost::asio::detail::throw_error(ec, "open"); |
| 426 | } |
| 427 | |
| 428 | /// Open the socket using the specified protocol. |
| 429 | /** |
| 430 | * This function opens the socket so that it will use the specified protocol. |
| 431 | * |
| 432 | * @param protocol An object specifying which protocol is to be used. |
| 433 | * |
| 434 | * @param ec Set to indicate what error occurred, if any. |
| 435 | * |
| 436 | * @par Example |
| 437 | * @code |
| 438 | * boost::asio::ip::tcp::socket socket(my_context); |
| 439 | * boost::system::error_code ec; |
| 440 | * socket.open(boost::asio::ip::tcp::v4(), ec); |
| 441 | * if (ec) |
| 442 | * { |
| 443 | * // An error occurred. |
| 444 | * } |
| 445 | * @endcode |
| 446 | */ |
| 447 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code open(const protocol_type& protocol, |
| 448 | boost::system::error_code& ec) |
| 449 | { |
| 450 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 451 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 452 | } |
| 453 | |
| 454 | /// Assign an existing native socket to the socket. |
| 455 | /* |
| 456 | * This function opens the socket to hold an existing native socket. |
| 457 | * |
| 458 | * @param protocol An object specifying which protocol is to be used. |
| 459 | * |
| 460 | * @param native_socket A native socket. |
| 461 | * |
| 462 | * @throws boost::system::system_error Thrown on failure. |
| 463 | */ |
| 464 | void assign(const protocol_type& protocol, |
| 465 | const native_handle_type& native_socket) |
| 466 | { |
| 467 | boost::system::error_code ec; |
| 468 | impl_.get_service().assign(impl_.get_implementation(), |
| 469 | protocol, native_socket, ec); |
| 470 | boost::asio::detail::throw_error(ec, "assign"); |
| 471 | } |
| 472 | |
| 473 | /// Assign an existing native socket to the socket. |
| 474 | /* |
| 475 | * This function opens the socket to hold an existing native socket. |
| 476 | * |
| 477 | * @param protocol An object specifying which protocol is to be used. |
| 478 | * |
| 479 | * @param native_socket A native socket. |
| 480 | * |
| 481 | * @param ec Set to indicate what error occurred, if any. |
| 482 | */ |
| 483 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code assign(const protocol_type& protocol, |
| 484 | const native_handle_type& native_socket, boost::system::error_code& ec) |
| 485 | { |
| 486 | impl_.get_service().assign(impl_.get_implementation(), |
| 487 | protocol, native_socket, ec); |
| 488 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 489 | } |
| 490 | |
| 491 | /// Determine whether the socket is open. |
| 492 | bool is_open() const |
| 493 | { |
| 494 | return impl_.get_service().is_open(impl_.get_implementation()); |
| 495 | } |
| 496 | |
| 497 | /// Close the socket. |
| 498 | /** |
| 499 | * This function is used to close the socket. Any asynchronous send, receive |
| 500 | * or connect operations will be cancelled immediately, and will complete |
| 501 | * with the boost::asio::error::operation_aborted error. |
| 502 | * |
| 503 | * @throws boost::system::system_error Thrown on failure. Note that, even if |
| 504 | * the function indicates an error, the underlying descriptor is closed. |
| 505 | * |
| 506 | * @note For portable behaviour with respect to graceful closure of a |
| 507 | * connected socket, call shutdown() before closing the socket. |
| 508 | */ |
| 509 | void close() |
| 510 | { |
| 511 | boost::system::error_code ec; |
| 512 | impl_.get_service().close(impl_.get_implementation(), ec); |
| 513 | boost::asio::detail::throw_error(ec, "close"); |
| 514 | } |
| 515 | |
| 516 | /// Close the socket. |
| 517 | /** |
| 518 | * This function is used to close the socket. Any asynchronous send, receive |
| 519 | * or connect operations will be cancelled immediately, and will complete |
| 520 | * with the boost::asio::error::operation_aborted error. |
| 521 | * |
| 522 | * @param ec Set to indicate what error occurred, if any. Note that, even if |
| 523 | * the function indicates an error, the underlying descriptor is closed. |
| 524 | * |
| 525 | * @par Example |
| 526 | * @code |
| 527 | * boost::asio::ip::tcp::socket socket(my_context); |
| 528 | * ... |
| 529 | * boost::system::error_code ec; |
| 530 | * socket.close(ec); |
| 531 | * if (ec) |
| 532 | * { |
| 533 | * // An error occurred. |
| 534 | * } |
| 535 | * @endcode |
| 536 | * |
| 537 | * @note For portable behaviour with respect to graceful closure of a |
| 538 | * connected socket, call shutdown() before closing the socket. |
| 539 | */ |
| 540 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code close(boost::system::error_code& ec) |
| 541 | { |
| 542 | impl_.get_service().close(impl_.get_implementation(), ec); |
| 543 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 544 | } |
| 545 | |
| 546 | /// Release ownership of the underlying native socket. |
| 547 | /** |
| 548 | * This function causes all outstanding asynchronous connect, send and receive |
| 549 | * operations to finish immediately, and the handlers for cancelled operations |
| 550 | * will be passed the boost::asio::error::operation_aborted error. Ownership |
| 551 | * of the native socket is then transferred to the caller. |
| 552 | * |
| 553 | * @throws boost::system::system_error Thrown on failure. |
| 554 | * |
| 555 | * @note This function is unsupported on Windows versions prior to Windows |
| 556 | * 8.1, and will fail with boost::asio::error::operation_not_supported on |
| 557 | * these platforms. |
| 558 | */ |
| 559 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ |
| 560 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) |
| 561 | __declspec(deprecated("This function always fails with " |
| 562 | "operation_not_supported when used on Windows versions " |
| 563 | "prior to Windows 8.1.")) |
| 564 | #endif |
| 565 | native_handle_type release() |
| 566 | { |
| 567 | boost::system::error_code ec; |
| 568 | native_handle_type s = impl_.get_service().release( |
| 569 | impl_.get_implementation(), ec); |
| 570 | boost::asio::detail::throw_error(ec, "release"); |
| 571 | return s; |
| 572 | } |
| 573 | |
| 574 | /// Release ownership of the underlying native socket. |
| 575 | /** |
| 576 | * This function causes all outstanding asynchronous connect, send and receive |
| 577 | * operations to finish immediately, and the handlers for cancelled operations |
| 578 | * will be passed the boost::asio::error::operation_aborted error. Ownership |
| 579 | * of the native socket is then transferred to the caller. |
| 580 | * |
| 581 | * @param ec Set to indicate what error occurred, if any. |
| 582 | * |
| 583 | * @note This function is unsupported on Windows versions prior to Windows |
| 584 | * 8.1, and will fail with boost::asio::error::operation_not_supported on |
| 585 | * these platforms. |
| 586 | */ |
| 587 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ |
| 588 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) |
| 589 | __declspec(deprecated("This function always fails with " |
| 590 | "operation_not_supported when used on Windows versions " |
| 591 | "prior to Windows 8.1.")) |
| 592 | #endif |
| 593 | native_handle_type release(boost::system::error_code& ec) |
| 594 | { |
| 595 | return impl_.get_service().release(impl_.get_implementation(), ec); |
| 596 | } |
| 597 | |
| 598 | /// Get the native socket representation. |
| 599 | /** |
| 600 | * This function may be used to obtain the underlying representation of the |
| 601 | * socket. This is intended to allow access to native socket functionality |
| 602 | * that is not otherwise provided. |
| 603 | */ |
| 604 | native_handle_type native_handle() |
| 605 | { |
| 606 | return impl_.get_service().native_handle(impl_.get_implementation()); |
| 607 | } |
| 608 | |
| 609 | /// Cancel all asynchronous operations associated with the socket. |
| 610 | /** |
| 611 | * This function causes all outstanding asynchronous connect, send and receive |
| 612 | * operations to finish immediately, and the handlers for cancelled operations |
| 613 | * will be passed the boost::asio::error::operation_aborted error. |
| 614 | * |
| 615 | * @throws boost::system::system_error Thrown on failure. |
| 616 | * |
| 617 | * @note Calls to cancel() will always fail with |
| 618 | * boost::asio::error::operation_not_supported when run on Windows XP, Windows |
| 619 | * Server 2003, and earlier versions of Windows, unless |
| 620 | * BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has |
| 621 | * two issues that should be considered before enabling its use: |
| 622 | * |
| 623 | * @li It will only cancel asynchronous operations that were initiated in the |
| 624 | * current thread. |
| 625 | * |
| 626 | * @li It can appear to complete without error, but the request to cancel the |
| 627 | * unfinished operations may be silently ignored by the operating system. |
| 628 | * Whether it works or not seems to depend on the drivers that are installed. |
| 629 | * |
| 630 | * For portable cancellation, consider using one of the following |
| 631 | * alternatives: |
| 632 | * |
| 633 | * @li Disable asio's I/O completion port backend by defining |
| 634 | * BOOST_ASIO_DISABLE_IOCP. |
| 635 | * |
| 636 | * @li Use the close() function to simultaneously cancel the outstanding |
| 637 | * operations and close the socket. |
| 638 | * |
| 639 | * When running on Windows Vista, Windows Server 2008, and later, the |
| 640 | * CancelIoEx function is always used. This function does not have the |
| 641 | * problems described above. |
| 642 | */ |
| 643 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ |
| 644 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ |
| 645 | && !defined(BOOST_ASIO_ENABLE_CANCELIO) |
| 646 | __declspec(deprecated("By default, this function always fails with " |
| 647 | "operation_not_supported when used on Windows XP, Windows Server 2003, " |
| 648 | "or earlier. Consult documentation for details.")) |
| 649 | #endif |
| 650 | void cancel() |
| 651 | { |
| 652 | boost::system::error_code ec; |
| 653 | impl_.get_service().cancel(impl_.get_implementation(), ec); |
| 654 | boost::asio::detail::throw_error(ec, "cancel"); |
| 655 | } |
| 656 | |
| 657 | /// Cancel all asynchronous operations associated with the socket. |
| 658 | /** |
| 659 | * This function causes all outstanding asynchronous connect, send and receive |
| 660 | * operations to finish immediately, and the handlers for cancelled operations |
| 661 | * will be passed the boost::asio::error::operation_aborted error. |
| 662 | * |
| 663 | * @param ec Set to indicate what error occurred, if any. |
| 664 | * |
| 665 | * @note Calls to cancel() will always fail with |
| 666 | * boost::asio::error::operation_not_supported when run on Windows XP, Windows |
| 667 | * Server 2003, and earlier versions of Windows, unless |
| 668 | * BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has |
| 669 | * two issues that should be considered before enabling its use: |
| 670 | * |
| 671 | * @li It will only cancel asynchronous operations that were initiated in the |
| 672 | * current thread. |
| 673 | * |
| 674 | * @li It can appear to complete without error, but the request to cancel the |
| 675 | * unfinished operations may be silently ignored by the operating system. |
| 676 | * Whether it works or not seems to depend on the drivers that are installed. |
| 677 | * |
| 678 | * For portable cancellation, consider using one of the following |
| 679 | * alternatives: |
| 680 | * |
| 681 | * @li Disable asio's I/O completion port backend by defining |
| 682 | * BOOST_ASIO_DISABLE_IOCP. |
| 683 | * |
| 684 | * @li Use the close() function to simultaneously cancel the outstanding |
| 685 | * operations and close the socket. |
| 686 | * |
| 687 | * When running on Windows Vista, Windows Server 2008, and later, the |
| 688 | * CancelIoEx function is always used. This function does not have the |
| 689 | * problems described above. |
| 690 | */ |
| 691 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ |
| 692 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ |
| 693 | && !defined(BOOST_ASIO_ENABLE_CANCELIO) |
| 694 | __declspec(deprecated("By default, this function always fails with " |
| 695 | "operation_not_supported when used on Windows XP, Windows Server 2003, " |
| 696 | "or earlier. Consult documentation for details.")) |
| 697 | #endif |
| 698 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code cancel(boost::system::error_code& ec) |
| 699 | { |
| 700 | impl_.get_service().cancel(impl_.get_implementation(), ec); |
| 701 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 702 | } |
| 703 | |
| 704 | /// Determine whether the socket is at the out-of-band data mark. |
| 705 | /** |
| 706 | * This function is used to check whether the socket input is currently |
| 707 | * positioned at the out-of-band data mark. |
| 708 | * |
| 709 | * @return A bool indicating whether the socket is at the out-of-band data |
| 710 | * mark. |
| 711 | * |
| 712 | * @throws boost::system::system_error Thrown on failure. |
| 713 | */ |
| 714 | bool at_mark() const |
| 715 | { |
| 716 | boost::system::error_code ec; |
| 717 | bool b = impl_.get_service().at_mark(impl_.get_implementation(), ec); |
| 718 | boost::asio::detail::throw_error(ec, "at_mark"); |
| 719 | return b; |
| 720 | } |
| 721 | |
| 722 | /// Determine whether the socket is at the out-of-band data mark. |
| 723 | /** |
| 724 | * This function is used to check whether the socket input is currently |
| 725 | * positioned at the out-of-band data mark. |
| 726 | * |
| 727 | * @param ec Set to indicate what error occurred, if any. |
| 728 | * |
| 729 | * @return A bool indicating whether the socket is at the out-of-band data |
| 730 | * mark. |
| 731 | */ |
| 732 | bool at_mark(boost::system::error_code& ec) const |
| 733 | { |
| 734 | return impl_.get_service().at_mark(impl_.get_implementation(), ec); |
| 735 | } |
| 736 | |
| 737 | /// Determine the number of bytes available for reading. |
| 738 | /** |
| 739 | * This function is used to determine the number of bytes that may be read |
| 740 | * without blocking. |
| 741 | * |
| 742 | * @return The number of bytes that may be read without blocking, or 0 if an |
| 743 | * error occurs. |
| 744 | * |
| 745 | * @throws boost::system::system_error Thrown on failure. |
| 746 | */ |
| 747 | std::size_t available() const |
| 748 | { |
| 749 | boost::system::error_code ec; |
| 750 | std::size_t s = impl_.get_service().available( |
| 751 | impl_.get_implementation(), ec); |
| 752 | boost::asio::detail::throw_error(ec, "available"); |
| 753 | return s; |
| 754 | } |
| 755 | |
| 756 | /// Determine the number of bytes available for reading. |
| 757 | /** |
| 758 | * This function is used to determine the number of bytes that may be read |
| 759 | * without blocking. |
| 760 | * |
| 761 | * @param ec Set to indicate what error occurred, if any. |
| 762 | * |
| 763 | * @return The number of bytes that may be read without blocking, or 0 if an |
| 764 | * error occurs. |
| 765 | */ |
| 766 | std::size_t available(boost::system::error_code& ec) const |
| 767 | { |
| 768 | return impl_.get_service().available(impl_.get_implementation(), ec); |
| 769 | } |
| 770 | |
| 771 | /// Bind the socket to the given local endpoint. |
| 772 | /** |
| 773 | * This function binds the socket to the specified endpoint on the local |
| 774 | * machine. |
| 775 | * |
| 776 | * @param endpoint An endpoint on the local machine to which the socket will |
| 777 | * be bound. |
| 778 | * |
| 779 | * @throws boost::system::system_error Thrown on failure. |
| 780 | * |
| 781 | * @par Example |
| 782 | * @code |
| 783 | * boost::asio::ip::tcp::socket socket(my_context); |
| 784 | * socket.open(boost::asio::ip::tcp::v4()); |
| 785 | * socket.bind(boost::asio::ip::tcp::endpoint( |
| 786 | * boost::asio::ip::tcp::v4(), 12345)); |
| 787 | * @endcode |
| 788 | */ |
| 789 | void bind(const endpoint_type& endpoint) |
| 790 | { |
| 791 | boost::system::error_code ec; |
| 792 | impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); |
| 793 | boost::asio::detail::throw_error(ec, "bind"); |
| 794 | } |
| 795 | |
| 796 | /// Bind the socket to the given local endpoint. |
| 797 | /** |
| 798 | * This function binds the socket to the specified endpoint on the local |
| 799 | * machine. |
| 800 | * |
| 801 | * @param endpoint An endpoint on the local machine to which the socket will |
| 802 | * be bound. |
| 803 | * |
| 804 | * @param ec Set to indicate what error occurred, if any. |
| 805 | * |
| 806 | * @par Example |
| 807 | * @code |
| 808 | * boost::asio::ip::tcp::socket socket(my_context); |
| 809 | * socket.open(boost::asio::ip::tcp::v4()); |
| 810 | * boost::system::error_code ec; |
| 811 | * socket.bind(boost::asio::ip::tcp::endpoint( |
| 812 | * boost::asio::ip::tcp::v4(), 12345), ec); |
| 813 | * if (ec) |
| 814 | * { |
| 815 | * // An error occurred. |
| 816 | * } |
| 817 | * @endcode |
| 818 | */ |
| 819 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code bind(const endpoint_type& endpoint, |
| 820 | boost::system::error_code& ec) |
| 821 | { |
| 822 | impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); |
| 823 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 824 | } |
| 825 | |
| 826 | /// Connect the socket to the specified endpoint. |
| 827 | /** |
| 828 | * This function is used to connect a socket to the specified remote endpoint. |
| 829 | * The function call will block until the connection is successfully made or |
| 830 | * an error occurs. |
| 831 | * |
| 832 | * The socket is automatically opened if it is not already open. If the |
| 833 | * connect fails, and the socket was automatically opened, the socket is |
| 834 | * not returned to the closed state. |
| 835 | * |
| 836 | * @param peer_endpoint The remote endpoint to which the socket will be |
| 837 | * connected. |
| 838 | * |
| 839 | * @throws boost::system::system_error Thrown on failure. |
| 840 | * |
| 841 | * @par Example |
| 842 | * @code |
| 843 | * boost::asio::ip::tcp::socket socket(my_context); |
| 844 | * boost::asio::ip::tcp::endpoint endpoint( |
| 845 | * boost::asio::ip::address::from_string("1.2.3.4"), 12345); |
| 846 | * socket.connect(endpoint); |
| 847 | * @endcode |
| 848 | */ |
| 849 | void connect(const endpoint_type& peer_endpoint) |
| 850 | { |
| 851 | boost::system::error_code ec; |
| 852 | if (!is_open()) |
| 853 | { |
| 854 | impl_.get_service().open(impl_.get_implementation(), |
| 855 | peer_endpoint.protocol(), ec); |
| 856 | boost::asio::detail::throw_error(ec, "connect"); |
| 857 | } |
| 858 | impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); |
| 859 | boost::asio::detail::throw_error(ec, "connect"); |
| 860 | } |
| 861 | |
| 862 | /// Connect the socket to the specified endpoint. |
| 863 | /** |
| 864 | * This function is used to connect a socket to the specified remote endpoint. |
| 865 | * The function call will block until the connection is successfully made or |
| 866 | * an error occurs. |
| 867 | * |
| 868 | * The socket is automatically opened if it is not already open. If the |
| 869 | * connect fails, and the socket was automatically opened, the socket is |
| 870 | * not returned to the closed state. |
| 871 | * |
| 872 | * @param peer_endpoint The remote endpoint to which the socket will be |
| 873 | * connected. |
| 874 | * |
| 875 | * @param ec Set to indicate what error occurred, if any. |
| 876 | * |
| 877 | * @par Example |
| 878 | * @code |
| 879 | * boost::asio::ip::tcp::socket socket(my_context); |
| 880 | * boost::asio::ip::tcp::endpoint endpoint( |
| 881 | * boost::asio::ip::address::from_string("1.2.3.4"), 12345); |
| 882 | * boost::system::error_code ec; |
| 883 | * socket.connect(endpoint, ec); |
| 884 | * if (ec) |
| 885 | * { |
| 886 | * // An error occurred. |
| 887 | * } |
| 888 | * @endcode |
| 889 | */ |
| 890 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code connect(const endpoint_type& peer_endpoint, |
| 891 | boost::system::error_code& ec) |
| 892 | { |
| 893 | if (!is_open()) |
| 894 | { |
| 895 | impl_.get_service().open(impl_.get_implementation(), |
| 896 | peer_endpoint.protocol(), ec); |
| 897 | if (ec) |
| 898 | { |
| 899 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 900 | } |
| 901 | } |
| 902 | |
| 903 | impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); |
| 904 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 905 | } |
| 906 | |
| 907 | /// Start an asynchronous connect. |
| 908 | /** |
| 909 | * This function is used to asynchronously connect a socket to the specified |
| 910 | * remote endpoint. It is an initiating function for an @ref |
| 911 | * asynchronous_operation, and always returns immediately. |
| 912 | * |
| 913 | * The socket is automatically opened if it is not already open. If the |
| 914 | * connect fails, and the socket was automatically opened, the socket is |
| 915 | * not returned to the closed state. |
| 916 | * |
| 917 | * @param peer_endpoint The remote endpoint to which the socket will be |
| 918 | * connected. Copies will be made of the endpoint object as required. |
| 919 | * |
| 920 | * @param token The @ref completion_token that will be used to produce a |
| 921 | * completion handler, which will be called when the connect completes. |
| 922 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 923 | * @ref yield_context, or a function object with the correct completion |
| 924 | * signature. The function signature of the completion handler must be: |
| 925 | * @code void handler( |
| 926 | * const boost::system::error_code& error // Result of operation. |
| 927 | * ); @endcode |
| 928 | * Regardless of whether the asynchronous operation completes immediately or |
| 929 | * not, the completion handler will not be invoked from within this function. |
| 930 | * On immediate completion, invocation of the handler will be performed in a |
| 931 | * manner equivalent to using boost::asio::async_immediate(). |
| 932 | * |
| 933 | * @par Completion Signature |
| 934 | * @code void(boost::system::error_code) @endcode |
| 935 | * |
| 936 | * @par Example |
| 937 | * @code |
| 938 | * void connect_handler(const boost::system::error_code& error) |
| 939 | * { |
| 940 | * if (!error) |
| 941 | * { |
| 942 | * // Connect succeeded. |
| 943 | * } |
| 944 | * } |
| 945 | * |
| 946 | * ... |
| 947 | * |
| 948 | * boost::asio::ip::tcp::socket socket(my_context); |
| 949 | * boost::asio::ip::tcp::endpoint endpoint( |
| 950 | * boost::asio::ip::address::from_string("1.2.3.4"), 12345); |
| 951 | * socket.async_connect(endpoint, connect_handler); |
| 952 | * @endcode |
| 953 | * |
| 954 | * @par Per-Operation Cancellation |
| 955 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 956 | * cancellation for the following boost::asio::cancellation_type values: |
| 957 | * |
| 958 | * @li @c cancellation_type::terminal |
| 959 | * |
| 960 | * @li @c cancellation_type::partial |
| 961 | * |
| 962 | * @li @c cancellation_type::total |
| 963 | */ |
| 964 | template < |
| 965 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code))typename |
| 966 | ConnectToken = default_completion_token_t<executor_type>> |
| 967 | auto async_connect(const endpoint_type& peer_endpoint, |
| 968 | ConnectToken&& token = default_completion_token_t<executor_type>()) |
| 969 | -> decltype( |
| 970 | async_initiate<ConnectToken, void (boost::system::error_code)>( |
| 971 | declval<initiate_async_connect>(), token, |
| 972 | peer_endpoint, declval<boost::system::error_code&>())) |
| 973 | { |
| 974 | boost::system::error_code open_ec; |
| 975 | if (!is_open()) |
| 976 | { |
| 977 | const protocol_type protocol = peer_endpoint.protocol(); |
| 978 | impl_.get_service().open(impl_.get_implementation(), protocol, open_ec); |
| 979 | } |
| 980 | |
| 981 | return async_initiate<ConnectToken, void (boost::system::error_code)>( |
| 982 | initiate_async_connect(this), token, peer_endpoint, open_ec); |
| 983 | } |
| 984 | |
| 985 | /// Set an option on the socket. |
| 986 | /** |
| 987 | * This function is used to set an option on the socket. |
| 988 | * |
| 989 | * @param option The new option value to be set on the socket. |
| 990 | * |
| 991 | * @throws boost::system::system_error Thrown on failure. |
| 992 | * |
| 993 | * @sa SettableSocketOption @n |
| 994 | * boost::asio::socket_base::broadcast @n |
| 995 | * boost::asio::socket_base::do_not_route @n |
| 996 | * boost::asio::socket_base::keep_alive @n |
| 997 | * boost::asio::socket_base::linger @n |
| 998 | * boost::asio::socket_base::receive_buffer_size @n |
| 999 | * boost::asio::socket_base::receive_low_watermark @n |
| 1000 | * boost::asio::socket_base::reuse_address @n |
| 1001 | * boost::asio::socket_base::send_buffer_size @n |
| 1002 | * boost::asio::socket_base::send_low_watermark @n |
| 1003 | * boost::asio::ip::multicast::join_group @n |
| 1004 | * boost::asio::ip::multicast::leave_group @n |
| 1005 | * boost::asio::ip::multicast::enable_loopback @n |
| 1006 | * boost::asio::ip::multicast::outbound_interface @n |
| 1007 | * boost::asio::ip::multicast::hops @n |
| 1008 | * boost::asio::ip::tcp::no_delay |
| 1009 | * |
| 1010 | * @par Example |
| 1011 | * Setting the IPPROTO_TCP/TCP_NODELAY option: |
| 1012 | * @code |
| 1013 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1014 | * ... |
| 1015 | * boost::asio::ip::tcp::no_delay option(true); |
| 1016 | * socket.set_option(option); |
| 1017 | * @endcode |
| 1018 | */ |
| 1019 | template <typename SettableSocketOption> |
| 1020 | void set_option(const SettableSocketOption& option) |
| 1021 | { |
| 1022 | boost::system::error_code ec; |
| 1023 | impl_.get_service().set_option(impl_.get_implementation(), option, ec); |
| 1024 | boost::asio::detail::throw_error(ec, "set_option"); |
| 1025 | } |
| 1026 | |
| 1027 | /// Set an option on the socket. |
| 1028 | /** |
| 1029 | * This function is used to set an option on the socket. |
| 1030 | * |
| 1031 | * @param option The new option value to be set on the socket. |
| 1032 | * |
| 1033 | * @param ec Set to indicate what error occurred, if any. |
| 1034 | * |
| 1035 | * @sa SettableSocketOption @n |
| 1036 | * boost::asio::socket_base::broadcast @n |
| 1037 | * boost::asio::socket_base::do_not_route @n |
| 1038 | * boost::asio::socket_base::keep_alive @n |
| 1039 | * boost::asio::socket_base::linger @n |
| 1040 | * boost::asio::socket_base::receive_buffer_size @n |
| 1041 | * boost::asio::socket_base::receive_low_watermark @n |
| 1042 | * boost::asio::socket_base::reuse_address @n |
| 1043 | * boost::asio::socket_base::send_buffer_size @n |
| 1044 | * boost::asio::socket_base::send_low_watermark @n |
| 1045 | * boost::asio::ip::multicast::join_group @n |
| 1046 | * boost::asio::ip::multicast::leave_group @n |
| 1047 | * boost::asio::ip::multicast::enable_loopback @n |
| 1048 | * boost::asio::ip::multicast::outbound_interface @n |
| 1049 | * boost::asio::ip::multicast::hops @n |
| 1050 | * boost::asio::ip::tcp::no_delay |
| 1051 | * |
| 1052 | * @par Example |
| 1053 | * Setting the IPPROTO_TCP/TCP_NODELAY option: |
| 1054 | * @code |
| 1055 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1056 | * ... |
| 1057 | * boost::asio::ip::tcp::no_delay option(true); |
| 1058 | * boost::system::error_code ec; |
| 1059 | * socket.set_option(option, ec); |
| 1060 | * if (ec) |
| 1061 | * { |
| 1062 | * // An error occurred. |
| 1063 | * } |
| 1064 | * @endcode |
| 1065 | */ |
| 1066 | template <typename SettableSocketOption> |
| 1067 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code set_option(const SettableSocketOption& option, |
| 1068 | boost::system::error_code& ec) |
| 1069 | { |
| 1070 | impl_.get_service().set_option(impl_.get_implementation(), option, ec); |
| 1071 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1072 | } |
| 1073 | |
| 1074 | /// Get an option from the socket. |
| 1075 | /** |
| 1076 | * This function is used to get the current value of an option on the socket. |
| 1077 | * |
| 1078 | * @param option The option value to be obtained from the socket. |
| 1079 | * |
| 1080 | * @throws boost::system::system_error Thrown on failure. |
| 1081 | * |
| 1082 | * @sa GettableSocketOption @n |
| 1083 | * boost::asio::socket_base::broadcast @n |
| 1084 | * boost::asio::socket_base::do_not_route @n |
| 1085 | * boost::asio::socket_base::keep_alive @n |
| 1086 | * boost::asio::socket_base::linger @n |
| 1087 | * boost::asio::socket_base::receive_buffer_size @n |
| 1088 | * boost::asio::socket_base::receive_low_watermark @n |
| 1089 | * boost::asio::socket_base::reuse_address @n |
| 1090 | * boost::asio::socket_base::send_buffer_size @n |
| 1091 | * boost::asio::socket_base::send_low_watermark @n |
| 1092 | * boost::asio::ip::multicast::join_group @n |
| 1093 | * boost::asio::ip::multicast::leave_group @n |
| 1094 | * boost::asio::ip::multicast::enable_loopback @n |
| 1095 | * boost::asio::ip::multicast::outbound_interface @n |
| 1096 | * boost::asio::ip::multicast::hops @n |
| 1097 | * boost::asio::ip::tcp::no_delay |
| 1098 | * |
| 1099 | * @par Example |
| 1100 | * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: |
| 1101 | * @code |
| 1102 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1103 | * ... |
| 1104 | * boost::asio::ip::tcp::socket::keep_alive option; |
| 1105 | * socket.get_option(option); |
| 1106 | * bool is_set = option.value(); |
| 1107 | * @endcode |
| 1108 | */ |
| 1109 | template <typename GettableSocketOption> |
| 1110 | void get_option(GettableSocketOption& option) const |
| 1111 | { |
| 1112 | boost::system::error_code ec; |
| 1113 | impl_.get_service().get_option(impl_.get_implementation(), option, ec); |
| 1114 | boost::asio::detail::throw_error(ec, "get_option"); |
| 1115 | } |
| 1116 | |
| 1117 | /// Get an option from the socket. |
| 1118 | /** |
| 1119 | * This function is used to get the current value of an option on the socket. |
| 1120 | * |
| 1121 | * @param option The option value to be obtained from the socket. |
| 1122 | * |
| 1123 | * @param ec Set to indicate what error occurred, if any. |
| 1124 | * |
| 1125 | * @sa GettableSocketOption @n |
| 1126 | * boost::asio::socket_base::broadcast @n |
| 1127 | * boost::asio::socket_base::do_not_route @n |
| 1128 | * boost::asio::socket_base::keep_alive @n |
| 1129 | * boost::asio::socket_base::linger @n |
| 1130 | * boost::asio::socket_base::receive_buffer_size @n |
| 1131 | * boost::asio::socket_base::receive_low_watermark @n |
| 1132 | * boost::asio::socket_base::reuse_address @n |
| 1133 | * boost::asio::socket_base::send_buffer_size @n |
| 1134 | * boost::asio::socket_base::send_low_watermark @n |
| 1135 | * boost::asio::ip::multicast::join_group @n |
| 1136 | * boost::asio::ip::multicast::leave_group @n |
| 1137 | * boost::asio::ip::multicast::enable_loopback @n |
| 1138 | * boost::asio::ip::multicast::outbound_interface @n |
| 1139 | * boost::asio::ip::multicast::hops @n |
| 1140 | * boost::asio::ip::tcp::no_delay |
| 1141 | * |
| 1142 | * @par Example |
| 1143 | * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: |
| 1144 | * @code |
| 1145 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1146 | * ... |
| 1147 | * boost::asio::ip::tcp::socket::keep_alive option; |
| 1148 | * boost::system::error_code ec; |
| 1149 | * socket.get_option(option, ec); |
| 1150 | * if (ec) |
| 1151 | * { |
| 1152 | * // An error occurred. |
| 1153 | * } |
| 1154 | * bool is_set = option.value(); |
| 1155 | * @endcode |
| 1156 | */ |
| 1157 | template <typename GettableSocketOption> |
| 1158 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code get_option(GettableSocketOption& option, |
| 1159 | boost::system::error_code& ec) const |
| 1160 | { |
| 1161 | impl_.get_service().get_option(impl_.get_implementation(), option, ec); |
| 1162 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1163 | } |
| 1164 | |
| 1165 | /// Perform an IO control command on the socket. |
| 1166 | /** |
| 1167 | * This function is used to execute an IO control command on the socket. |
| 1168 | * |
| 1169 | * @param command The IO control command to be performed on the socket. |
| 1170 | * |
| 1171 | * @throws boost::system::system_error Thrown on failure. |
| 1172 | * |
| 1173 | * @sa IoControlCommand @n |
| 1174 | * boost::asio::socket_base::bytes_readable @n |
| 1175 | * boost::asio::socket_base::non_blocking_io |
| 1176 | * |
| 1177 | * @par Example |
| 1178 | * Getting the number of bytes ready to read: |
| 1179 | * @code |
| 1180 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1181 | * ... |
| 1182 | * boost::asio::ip::tcp::socket::bytes_readable command; |
| 1183 | * socket.io_control(command); |
| 1184 | * std::size_t bytes_readable = command.get(); |
| 1185 | * @endcode |
| 1186 | */ |
| 1187 | template <typename IoControlCommand> |
| 1188 | void io_control(IoControlCommand& command) |
| 1189 | { |
| 1190 | boost::system::error_code ec; |
| 1191 | impl_.get_service().io_control(impl_.get_implementation(), command, ec); |
| 1192 | boost::asio::detail::throw_error(ec, "io_control"); |
| 1193 | } |
| 1194 | |
| 1195 | /// Perform an IO control command on the socket. |
| 1196 | /** |
| 1197 | * This function is used to execute an IO control command on the socket. |
| 1198 | * |
| 1199 | * @param command The IO control command to be performed on the socket. |
| 1200 | * |
| 1201 | * @param ec Set to indicate what error occurred, if any. |
| 1202 | * |
| 1203 | * @sa IoControlCommand @n |
| 1204 | * boost::asio::socket_base::bytes_readable @n |
| 1205 | * boost::asio::socket_base::non_blocking_io |
| 1206 | * |
| 1207 | * @par Example |
| 1208 | * Getting the number of bytes ready to read: |
| 1209 | * @code |
| 1210 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1211 | * ... |
| 1212 | * boost::asio::ip::tcp::socket::bytes_readable command; |
| 1213 | * boost::system::error_code ec; |
| 1214 | * socket.io_control(command, ec); |
| 1215 | * if (ec) |
| 1216 | * { |
| 1217 | * // An error occurred. |
| 1218 | * } |
| 1219 | * std::size_t bytes_readable = command.get(); |
| 1220 | * @endcode |
| 1221 | */ |
| 1222 | template <typename IoControlCommand> |
| 1223 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code io_control(IoControlCommand& command, |
| 1224 | boost::system::error_code& ec) |
| 1225 | { |
| 1226 | impl_.get_service().io_control(impl_.get_implementation(), command, ec); |
| 1227 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1228 | } |
| 1229 | |
| 1230 | /// Gets the non-blocking mode of the socket. |
| 1231 | /** |
| 1232 | * @returns @c true if the socket's synchronous operations will fail with |
| 1233 | * boost::asio::error::would_block if they are unable to perform the requested |
| 1234 | * operation immediately. If @c false, synchronous operations will block |
| 1235 | * until complete. |
| 1236 | * |
| 1237 | * @note The non-blocking mode has no effect on the behaviour of asynchronous |
| 1238 | * operations. Asynchronous operations will never fail with the error |
| 1239 | * boost::asio::error::would_block. |
| 1240 | */ |
| 1241 | bool non_blocking() const |
| 1242 | { |
| 1243 | return impl_.get_service().non_blocking(impl_.get_implementation()); |
| 1244 | } |
| 1245 | |
| 1246 | /// Sets the non-blocking mode of the socket. |
| 1247 | /** |
| 1248 | * @param mode If @c true, the socket's synchronous operations will fail with |
| 1249 | * boost::asio::error::would_block if they are unable to perform the requested |
| 1250 | * operation immediately. If @c false, synchronous operations will block |
| 1251 | * until complete. |
| 1252 | * |
| 1253 | * @throws boost::system::system_error Thrown on failure. |
| 1254 | * |
| 1255 | * @note The non-blocking mode has no effect on the behaviour of asynchronous |
| 1256 | * operations. Asynchronous operations will never fail with the error |
| 1257 | * boost::asio::error::would_block. |
| 1258 | */ |
| 1259 | void non_blocking(bool mode) |
| 1260 | { |
| 1261 | boost::system::error_code ec; |
| 1262 | impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); |
| 1263 | boost::asio::detail::throw_error(ec, "non_blocking"); |
| 1264 | } |
| 1265 | |
| 1266 | /// Sets the non-blocking mode of the socket. |
| 1267 | /** |
| 1268 | * @param mode If @c true, the socket's synchronous operations will fail with |
| 1269 | * boost::asio::error::would_block if they are unable to perform the requested |
| 1270 | * operation immediately. If @c false, synchronous operations will block |
| 1271 | * until complete. |
| 1272 | * |
| 1273 | * @param ec Set to indicate what error occurred, if any. |
| 1274 | * |
| 1275 | * @note The non-blocking mode has no effect on the behaviour of asynchronous |
| 1276 | * operations. Asynchronous operations will never fail with the error |
| 1277 | * boost::asio::error::would_block. |
| 1278 | */ |
| 1279 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code non_blocking( |
| 1280 | bool mode, boost::system::error_code& ec) |
| 1281 | { |
| 1282 | impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); |
| 1283 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1284 | } |
| 1285 | |
| 1286 | /// Gets the non-blocking mode of the native socket implementation. |
| 1287 | /** |
| 1288 | * This function is used to retrieve the non-blocking mode of the underlying |
| 1289 | * native socket. This mode has no effect on the behaviour of the socket |
| 1290 | * object's synchronous operations. |
| 1291 | * |
| 1292 | * @returns @c true if the underlying socket is in non-blocking mode and |
| 1293 | * direct system calls may fail with boost::asio::error::would_block (or the |
| 1294 | * equivalent system error). |
| 1295 | * |
| 1296 | * @note The current non-blocking mode is cached by the socket object. |
| 1297 | * Consequently, the return value may be incorrect if the non-blocking mode |
| 1298 | * was set directly on the native socket. |
| 1299 | * |
| 1300 | * @par Example |
| 1301 | * This function is intended to allow the encapsulation of arbitrary |
| 1302 | * non-blocking system calls as asynchronous operations, in a way that is |
| 1303 | * transparent to the user of the socket object. The following example |
| 1304 | * illustrates how Linux's @c sendfile system call might be encapsulated: |
| 1305 | * @code template <typename Handler> |
| 1306 | * struct sendfile_op |
| 1307 | * { |
| 1308 | * tcp::socket& sock_; |
| 1309 | * int fd_; |
| 1310 | * Handler handler_; |
| 1311 | * off_t offset_; |
| 1312 | * std::size_t total_bytes_transferred_; |
| 1313 | * |
| 1314 | * // Function call operator meeting WriteHandler requirements. |
| 1315 | * // Used as the handler for the async_write_some operation. |
| 1316 | * void operator()(boost::system::error_code ec, std::size_t) |
| 1317 | * { |
| 1318 | * // Put the underlying socket into non-blocking mode. |
| 1319 | * if (!ec) |
| 1320 | * if (!sock_.native_non_blocking()) |
| 1321 | * sock_.native_non_blocking(true, ec); |
| 1322 | * |
| 1323 | * if (!ec) |
| 1324 | * { |
| 1325 | * for (;;) |
| 1326 | * { |
| 1327 | * // Try the system call. |
| 1328 | * errno = 0; |
| 1329 | * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); |
| 1330 | * ec = boost::system::error_code(n < 0 ? errno : 0, |
| 1331 | * boost::asio::error::get_system_category()); |
| 1332 | * total_bytes_transferred_ += ec ? 0 : n; |
| 1333 | * |
| 1334 | * // Retry operation immediately if interrupted by signal. |
| 1335 | * if (ec == boost::asio::error::interrupted) |
| 1336 | * continue; |
| 1337 | * |
| 1338 | * // Check if we need to run the operation again. |
| 1339 | * if (ec == boost::asio::error::would_block |
| 1340 | * || ec == boost::asio::error::try_again) |
| 1341 | * { |
| 1342 | * // We have to wait for the socket to become ready again. |
| 1343 | * sock_.async_wait(tcp::socket::wait_write, *this); |
| 1344 | * return; |
| 1345 | * } |
| 1346 | * |
| 1347 | * if (ec || n == 0) |
| 1348 | * { |
| 1349 | * // An error occurred, or we have reached the end of the file. |
| 1350 | * // Either way we must exit the loop so we can call the handler. |
| 1351 | * break; |
| 1352 | * } |
| 1353 | * |
| 1354 | * // Loop around to try calling sendfile again. |
| 1355 | * } |
| 1356 | * } |
| 1357 | * |
| 1358 | * // Pass result back to user's handler. |
| 1359 | * handler_(ec, total_bytes_transferred_); |
| 1360 | * } |
| 1361 | * }; |
| 1362 | * |
| 1363 | * template <typename Handler> |
| 1364 | * void async_sendfile(tcp::socket& sock, int fd, Handler h) |
| 1365 | * { |
| 1366 | * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; |
| 1367 | * sock.async_wait(tcp::socket::wait_write, op); |
| 1368 | * } @endcode |
| 1369 | */ |
| 1370 | bool native_non_blocking() const |
| 1371 | { |
| 1372 | return impl_.get_service().native_non_blocking(impl_.get_implementation()); |
| 1373 | } |
| 1374 | |
| 1375 | /// Sets the non-blocking mode of the native socket implementation. |
| 1376 | /** |
| 1377 | * This function is used to modify the non-blocking mode of the underlying |
| 1378 | * native socket. It has no effect on the behaviour of the socket object's |
| 1379 | * synchronous operations. |
| 1380 | * |
| 1381 | * @param mode If @c true, the underlying socket is put into non-blocking |
| 1382 | * mode and direct system calls may fail with boost::asio::error::would_block |
| 1383 | * (or the equivalent system error). |
| 1384 | * |
| 1385 | * @throws boost::system::system_error Thrown on failure. If the @c mode is |
| 1386 | * @c false, but the current value of @c non_blocking() is @c true, this |
| 1387 | * function fails with boost::asio::error::invalid_argument, as the |
| 1388 | * combination does not make sense. |
| 1389 | * |
| 1390 | * @par Example |
| 1391 | * This function is intended to allow the encapsulation of arbitrary |
| 1392 | * non-blocking system calls as asynchronous operations, in a way that is |
| 1393 | * transparent to the user of the socket object. The following example |
| 1394 | * illustrates how Linux's @c sendfile system call might be encapsulated: |
| 1395 | * @code template <typename Handler> |
| 1396 | * struct sendfile_op |
| 1397 | * { |
| 1398 | * tcp::socket& sock_; |
| 1399 | * int fd_; |
| 1400 | * Handler handler_; |
| 1401 | * off_t offset_; |
| 1402 | * std::size_t total_bytes_transferred_; |
| 1403 | * |
| 1404 | * // Function call operator meeting WriteHandler requirements. |
| 1405 | * // Used as the handler for the async_write_some operation. |
| 1406 | * void operator()(boost::system::error_code ec, std::size_t) |
| 1407 | * { |
| 1408 | * // Put the underlying socket into non-blocking mode. |
| 1409 | * if (!ec) |
| 1410 | * if (!sock_.native_non_blocking()) |
| 1411 | * sock_.native_non_blocking(true, ec); |
| 1412 | * |
| 1413 | * if (!ec) |
| 1414 | * { |
| 1415 | * for (;;) |
| 1416 | * { |
| 1417 | * // Try the system call. |
| 1418 | * errno = 0; |
| 1419 | * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); |
| 1420 | * ec = boost::system::error_code(n < 0 ? errno : 0, |
| 1421 | * boost::asio::error::get_system_category()); |
| 1422 | * total_bytes_transferred_ += ec ? 0 : n; |
| 1423 | * |
| 1424 | * // Retry operation immediately if interrupted by signal. |
| 1425 | * if (ec == boost::asio::error::interrupted) |
| 1426 | * continue; |
| 1427 | * |
| 1428 | * // Check if we need to run the operation again. |
| 1429 | * if (ec == boost::asio::error::would_block |
| 1430 | * || ec == boost::asio::error::try_again) |
| 1431 | * { |
| 1432 | * // We have to wait for the socket to become ready again. |
| 1433 | * sock_.async_wait(tcp::socket::wait_write, *this); |
| 1434 | * return; |
| 1435 | * } |
| 1436 | * |
| 1437 | * if (ec || n == 0) |
| 1438 | * { |
| 1439 | * // An error occurred, or we have reached the end of the file. |
| 1440 | * // Either way we must exit the loop so we can call the handler. |
| 1441 | * break; |
| 1442 | * } |
| 1443 | * |
| 1444 | * // Loop around to try calling sendfile again. |
| 1445 | * } |
| 1446 | * } |
| 1447 | * |
| 1448 | * // Pass result back to user's handler. |
| 1449 | * handler_(ec, total_bytes_transferred_); |
| 1450 | * } |
| 1451 | * }; |
| 1452 | * |
| 1453 | * template <typename Handler> |
| 1454 | * void async_sendfile(tcp::socket& sock, int fd, Handler h) |
| 1455 | * { |
| 1456 | * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; |
| 1457 | * sock.async_wait(tcp::socket::wait_write, op); |
| 1458 | * } @endcode |
| 1459 | */ |
| 1460 | void native_non_blocking(bool mode) |
| 1461 | { |
| 1462 | boost::system::error_code ec; |
| 1463 | impl_.get_service().native_non_blocking( |
| 1464 | impl_.get_implementation(), mode, ec); |
| 1465 | boost::asio::detail::throw_error(ec, "native_non_blocking"); |
| 1466 | } |
| 1467 | |
| 1468 | /// Sets the non-blocking mode of the native socket implementation. |
| 1469 | /** |
| 1470 | * This function is used to modify the non-blocking mode of the underlying |
| 1471 | * native socket. It has no effect on the behaviour of the socket object's |
| 1472 | * synchronous operations. |
| 1473 | * |
| 1474 | * @param mode If @c true, the underlying socket is put into non-blocking |
| 1475 | * mode and direct system calls may fail with boost::asio::error::would_block |
| 1476 | * (or the equivalent system error). |
| 1477 | * |
| 1478 | * @param ec Set to indicate what error occurred, if any. If the @c mode is |
| 1479 | * @c false, but the current value of @c non_blocking() is @c true, this |
| 1480 | * function fails with boost::asio::error::invalid_argument, as the |
| 1481 | * combination does not make sense. |
| 1482 | * |
| 1483 | * @par Example |
| 1484 | * This function is intended to allow the encapsulation of arbitrary |
| 1485 | * non-blocking system calls as asynchronous operations, in a way that is |
| 1486 | * transparent to the user of the socket object. The following example |
| 1487 | * illustrates how Linux's @c sendfile system call might be encapsulated: |
| 1488 | * @code template <typename Handler> |
| 1489 | * struct sendfile_op |
| 1490 | * { |
| 1491 | * tcp::socket& sock_; |
| 1492 | * int fd_; |
| 1493 | * Handler handler_; |
| 1494 | * off_t offset_; |
| 1495 | * std::size_t total_bytes_transferred_; |
| 1496 | * |
| 1497 | * // Function call operator meeting WriteHandler requirements. |
| 1498 | * // Used as the handler for the async_write_some operation. |
| 1499 | * void operator()(boost::system::error_code ec, std::size_t) |
| 1500 | * { |
| 1501 | * // Put the underlying socket into non-blocking mode. |
| 1502 | * if (!ec) |
| 1503 | * if (!sock_.native_non_blocking()) |
| 1504 | * sock_.native_non_blocking(true, ec); |
| 1505 | * |
| 1506 | * if (!ec) |
| 1507 | * { |
| 1508 | * for (;;) |
| 1509 | * { |
| 1510 | * // Try the system call. |
| 1511 | * errno = 0; |
| 1512 | * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); |
| 1513 | * ec = boost::system::error_code(n < 0 ? errno : 0, |
| 1514 | * boost::asio::error::get_system_category()); |
| 1515 | * total_bytes_transferred_ += ec ? 0 : n; |
| 1516 | * |
| 1517 | * // Retry operation immediately if interrupted by signal. |
| 1518 | * if (ec == boost::asio::error::interrupted) |
| 1519 | * continue; |
| 1520 | * |
| 1521 | * // Check if we need to run the operation again. |
| 1522 | * if (ec == boost::asio::error::would_block |
| 1523 | * || ec == boost::asio::error::try_again) |
| 1524 | * { |
| 1525 | * // We have to wait for the socket to become ready again. |
| 1526 | * sock_.async_wait(tcp::socket::wait_write, *this); |
| 1527 | * return; |
| 1528 | * } |
| 1529 | * |
| 1530 | * if (ec || n == 0) |
| 1531 | * { |
| 1532 | * // An error occurred, or we have reached the end of the file. |
| 1533 | * // Either way we must exit the loop so we can call the handler. |
| 1534 | * break; |
| 1535 | * } |
| 1536 | * |
| 1537 | * // Loop around to try calling sendfile again. |
| 1538 | * } |
| 1539 | * } |
| 1540 | * |
| 1541 | * // Pass result back to user's handler. |
| 1542 | * handler_(ec, total_bytes_transferred_); |
| 1543 | * } |
| 1544 | * }; |
| 1545 | * |
| 1546 | * template <typename Handler> |
| 1547 | * void async_sendfile(tcp::socket& sock, int fd, Handler h) |
| 1548 | * { |
| 1549 | * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; |
| 1550 | * sock.async_wait(tcp::socket::wait_write, op); |
| 1551 | * } @endcode |
| 1552 | */ |
| 1553 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code native_non_blocking( |
| 1554 | bool mode, boost::system::error_code& ec) |
| 1555 | { |
| 1556 | impl_.get_service().native_non_blocking( |
| 1557 | impl_.get_implementation(), mode, ec); |
| 1558 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1559 | } |
| 1560 | |
| 1561 | /// Get the local endpoint of the socket. |
| 1562 | /** |
| 1563 | * This function is used to obtain the locally bound endpoint of the socket. |
| 1564 | * |
| 1565 | * @returns An object that represents the local endpoint of the socket. |
| 1566 | * |
| 1567 | * @throws boost::system::system_error Thrown on failure. |
| 1568 | * |
| 1569 | * @par Example |
| 1570 | * @code |
| 1571 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1572 | * ... |
| 1573 | * boost::asio::ip::tcp::endpoint endpoint = socket.local_endpoint(); |
| 1574 | * @endcode |
| 1575 | */ |
| 1576 | endpoint_type local_endpoint() const |
| 1577 | { |
| 1578 | boost::system::error_code ec; |
| 1579 | endpoint_type ep = impl_.get_service().local_endpoint( |
| 1580 | impl_.get_implementation(), ec); |
| 1581 | boost::asio::detail::throw_error(ec, "local_endpoint"); |
| 1582 | return ep; |
| 1583 | } |
| 1584 | |
| 1585 | /// Get the local endpoint of the socket. |
| 1586 | /** |
| 1587 | * This function is used to obtain the locally bound endpoint of the socket. |
| 1588 | * |
| 1589 | * @param ec Set to indicate what error occurred, if any. |
| 1590 | * |
| 1591 | * @returns An object that represents the local endpoint of the socket. |
| 1592 | * Returns a default-constructed endpoint object if an error occurred. |
| 1593 | * |
| 1594 | * @par Example |
| 1595 | * @code |
| 1596 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1597 | * ... |
| 1598 | * boost::system::error_code ec; |
| 1599 | * boost::asio::ip::tcp::endpoint endpoint = socket.local_endpoint(ec); |
| 1600 | * if (ec) |
| 1601 | * { |
| 1602 | * // An error occurred. |
| 1603 | * } |
| 1604 | * @endcode |
| 1605 | */ |
| 1606 | endpoint_type local_endpoint(boost::system::error_code& ec) const |
| 1607 | { |
| 1608 | return impl_.get_service().local_endpoint(impl_.get_implementation(), ec); |
| 1609 | } |
| 1610 | |
| 1611 | /// Get the remote endpoint of the socket. |
| 1612 | /** |
| 1613 | * This function is used to obtain the remote endpoint of the socket. |
| 1614 | * |
| 1615 | * @returns An object that represents the remote endpoint of the socket. |
| 1616 | * |
| 1617 | * @throws boost::system::system_error Thrown on failure. |
| 1618 | * |
| 1619 | * @par Example |
| 1620 | * @code |
| 1621 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1622 | * ... |
| 1623 | * boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(); |
| 1624 | * @endcode |
| 1625 | */ |
| 1626 | endpoint_type remote_endpoint() const |
| 1627 | { |
| 1628 | boost::system::error_code ec; |
| 1629 | endpoint_type ep = impl_.get_service().remote_endpoint( |
| 1630 | impl_.get_implementation(), ec); |
| 1631 | boost::asio::detail::throw_error(ec, "remote_endpoint"); |
| 1632 | return ep; |
| 1633 | } |
| 1634 | |
| 1635 | /// Get the remote endpoint of the socket. |
| 1636 | /** |
| 1637 | * This function is used to obtain the remote endpoint of the socket. |
| 1638 | * |
| 1639 | * @param ec Set to indicate what error occurred, if any. |
| 1640 | * |
| 1641 | * @returns An object that represents the remote endpoint of the socket. |
| 1642 | * Returns a default-constructed endpoint object if an error occurred. |
| 1643 | * |
| 1644 | * @par Example |
| 1645 | * @code |
| 1646 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1647 | * ... |
| 1648 | * boost::system::error_code ec; |
| 1649 | * boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(ec); |
| 1650 | * if (ec) |
| 1651 | * { |
| 1652 | * // An error occurred. |
| 1653 | * } |
| 1654 | * @endcode |
| 1655 | */ |
| 1656 | endpoint_type remote_endpoint(boost::system::error_code& ec) const |
| 1657 | { |
| 1658 | return impl_.get_service().remote_endpoint(impl_.get_implementation(), ec); |
| 1659 | } |
| 1660 | |
| 1661 | /// Disable sends or receives on the socket. |
| 1662 | /** |
| 1663 | * This function is used to disable send operations, receive operations, or |
| 1664 | * both. |
| 1665 | * |
| 1666 | * @param what Determines what types of operation will no longer be allowed. |
| 1667 | * |
| 1668 | * @throws boost::system::system_error Thrown on failure. |
| 1669 | * |
| 1670 | * @par Example |
| 1671 | * Shutting down the send side of the socket: |
| 1672 | * @code |
| 1673 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1674 | * ... |
| 1675 | * socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send); |
| 1676 | * @endcode |
| 1677 | */ |
| 1678 | void shutdown(shutdown_type what) |
| 1679 | { |
| 1680 | boost::system::error_code ec; |
| 1681 | impl_.get_service().shutdown(impl_.get_implementation(), what, ec); |
| 1682 | boost::asio::detail::throw_error(ec, "shutdown"); |
| 1683 | } |
| 1684 | |
| 1685 | /// Disable sends or receives on the socket. |
| 1686 | /** |
| 1687 | * This function is used to disable send operations, receive operations, or |
| 1688 | * both. |
| 1689 | * |
| 1690 | * @param what Determines what types of operation will no longer be allowed. |
| 1691 | * |
| 1692 | * @param ec Set to indicate what error occurred, if any. |
| 1693 | * |
| 1694 | * @par Example |
| 1695 | * Shutting down the send side of the socket: |
| 1696 | * @code |
| 1697 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1698 | * ... |
| 1699 | * boost::system::error_code ec; |
| 1700 | * socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); |
| 1701 | * if (ec) |
| 1702 | * { |
| 1703 | * // An error occurred. |
| 1704 | * } |
| 1705 | * @endcode |
| 1706 | */ |
| 1707 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code shutdown(shutdown_type what, |
| 1708 | boost::system::error_code& ec) |
| 1709 | { |
| 1710 | impl_.get_service().shutdown(impl_.get_implementation(), what, ec); |
| 1711 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1712 | } |
| 1713 | |
| 1714 | /// Wait for the socket to become ready to read, ready to write, or to have |
| 1715 | /// pending error conditions. |
| 1716 | /** |
| 1717 | * This function is used to perform a blocking wait for a socket to enter |
| 1718 | * a ready to read, write or error condition state. |
| 1719 | * |
| 1720 | * @param w Specifies the desired socket state. |
| 1721 | * |
| 1722 | * @par Example |
| 1723 | * Waiting for a socket to become readable. |
| 1724 | * @code |
| 1725 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1726 | * ... |
| 1727 | * socket.wait(boost::asio::ip::tcp::socket::wait_read); |
| 1728 | * @endcode |
| 1729 | */ |
| 1730 | void wait(wait_type w) |
| 1731 | { |
| 1732 | boost::system::error_code ec; |
| 1733 | impl_.get_service().wait(impl_.get_implementation(), w, ec); |
| 1734 | boost::asio::detail::throw_error(ec, "wait"); |
| 1735 | } |
| 1736 | |
| 1737 | /// Wait for the socket to become ready to read, ready to write, or to have |
| 1738 | /// pending error conditions. |
| 1739 | /** |
| 1740 | * This function is used to perform a blocking wait for a socket to enter |
| 1741 | * a ready to read, write or error condition state. |
| 1742 | * |
| 1743 | * @param w Specifies the desired socket state. |
| 1744 | * |
| 1745 | * @param ec Set to indicate what error occurred, if any. |
| 1746 | * |
| 1747 | * @par Example |
| 1748 | * Waiting for a socket to become readable. |
| 1749 | * @code |
| 1750 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1751 | * ... |
| 1752 | * boost::system::error_code ec; |
| 1753 | * socket.wait(boost::asio::ip::tcp::socket::wait_read, ec); |
| 1754 | * @endcode |
| 1755 | */ |
| 1756 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code wait(wait_type w, boost::system::error_code& ec) |
| 1757 | { |
| 1758 | impl_.get_service().wait(impl_.get_implementation(), w, ec); |
| 1759 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1760 | } |
| 1761 | |
| 1762 | /// Asynchronously wait for the socket to become ready to read, ready to |
| 1763 | /// write, or to have pending error conditions. |
| 1764 | /** |
| 1765 | * This function is used to perform an asynchronous wait for a socket to enter |
| 1766 | * a ready to read, write or error condition state. It is an initiating |
| 1767 | * function for an @ref asynchronous_operation, and always returns |
| 1768 | * immediately. |
| 1769 | * |
| 1770 | * @param w Specifies the desired socket state. |
| 1771 | * |
| 1772 | * @param token The @ref completion_token that will be used to produce a |
| 1773 | * completion handler, which will be called when the wait completes. Potential |
| 1774 | * completion tokens include @ref use_future, @ref use_awaitable, @ref |
| 1775 | * yield_context, or a function object with the correct completion signature. |
| 1776 | * The function signature of the completion handler must be: |
| 1777 | * @code void handler( |
| 1778 | * const boost::system::error_code& error // Result of operation. |
| 1779 | * ); @endcode |
| 1780 | * Regardless of whether the asynchronous operation completes immediately or |
| 1781 | * not, the completion handler will not be invoked from within this function. |
| 1782 | * On immediate completion, invocation of the handler will be performed in a |
| 1783 | * manner equivalent to using boost::asio::async_immediate(). |
| 1784 | * |
| 1785 | * @par Completion Signature |
| 1786 | * @code void(boost::system::error_code) @endcode |
| 1787 | * |
| 1788 | * @par Example |
| 1789 | * @code |
| 1790 | * void wait_handler(const boost::system::error_code& error) |
| 1791 | * { |
| 1792 | * if (!error) |
| 1793 | * { |
| 1794 | * // Wait succeeded. |
| 1795 | * } |
| 1796 | * } |
| 1797 | * |
| 1798 | * ... |
| 1799 | * |
| 1800 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1801 | * ... |
| 1802 | * socket.async_wait(boost::asio::ip::tcp::socket::wait_read, wait_handler); |
| 1803 | * @endcode |
| 1804 | * |
| 1805 | * @par Per-Operation Cancellation |
| 1806 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 1807 | * cancellation for the following boost::asio::cancellation_type values: |
| 1808 | * |
| 1809 | * @li @c cancellation_type::terminal |
| 1810 | * |
| 1811 | * @li @c cancellation_type::partial |
| 1812 | * |
| 1813 | * @li @c cancellation_type::total |
| 1814 | */ |
| 1815 | template < |
| 1816 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code))typename |
| 1817 | WaitToken = default_completion_token_t<executor_type>> |
| 1818 | auto async_wait(wait_type w, |
| 1819 | WaitToken&& token = default_completion_token_t<executor_type>()) |
| 1820 | -> decltype( |
| 1821 | async_initiate<WaitToken, void (boost::system::error_code)>( |
| 1822 | declval<initiate_async_wait>(), token, w)) |
| 1823 | { |
| 1824 | return async_initiate<WaitToken, void (boost::system::error_code)>( |
| 1825 | initiate_async_wait(this), token, w); |
| 1826 | } |
| 1827 | |
| 1828 | protected: |
| 1829 | /// Protected destructor to prevent deletion through this type. |
| 1830 | /** |
| 1831 | * This function destroys the socket, cancelling any outstanding asynchronous |
| 1832 | * operations associated with the socket as if by calling @c cancel. |
| 1833 | */ |
| 1834 | ~basic_socket() |
| 1835 | { |
| 1836 | } |
| 1837 | |
| 1838 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) |
| 1839 | detail::io_object_impl< |
| 1840 | detail::null_socket_service<Protocol>, Executor> impl_; |
| 1841 | #elif defined(BOOST_ASIO_HAS_IOCP) |
| 1842 | detail::io_object_impl< |
| 1843 | detail::win_iocp_socket_service<Protocol>, Executor> impl_; |
| 1844 | #elif defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 1845 | detail::io_object_impl< |
| 1846 | detail::io_uring_socket_service<Protocol>, Executor> impl_; |
| 1847 | #else |
| 1848 | detail::io_object_impl< |
| 1849 | detail::reactive_socket_service<Protocol>, Executor> impl_; |
| 1850 | #endif |
| 1851 | |
| 1852 | private: |
| 1853 | // Disallow copying and assignment. |
| 1854 | basic_socket(const basic_socket&) = delete; |
| 1855 | basic_socket& operator=(const basic_socket&) = delete; |
| 1856 | |
| 1857 | class initiate_async_connect |
| 1858 | { |
| 1859 | public: |
| 1860 | typedef Executor executor_type; |
| 1861 | |
| 1862 | explicit initiate_async_connect(basic_socket* self) |
| 1863 | : self_(self) |
| 1864 | { |
| 1865 | } |
| 1866 | |
| 1867 | const executor_type& get_executor() const noexcept |
| 1868 | { |
| 1869 | return self_->get_executor(); |
| 1870 | } |
| 1871 | |
| 1872 | template <typename ConnectHandler> |
| 1873 | void operator()(ConnectHandler&& handler, |
| 1874 | const endpoint_type& peer_endpoint, |
| 1875 | const boost::system::error_code& open_ec) const |
| 1876 | { |
| 1877 | // If you get an error on the following line it means that your handler |
| 1878 | // does not meet the documented type requirements for a ConnectHandler. |
| 1879 | BOOST_ASIO_CONNECT_HANDLER_CHECK(ConnectHandler, handler)typedef typename ::boost::asio::async_result< typename ::boost ::asio::decay<ConnectHandler>::type, void(boost::system ::error_code)>::completion_handler_type asio_true_handler_type ; static_assert(sizeof(boost::asio::detail::one_arg_handler_test ( boost::asio::detail::rvref< asio_true_handler_type>() , static_cast<const boost::system::error_code*>(0))) == 1, "ConnectHandler type requirements not met"); typedef boost ::asio::detail::handler_type_requirements< sizeof( boost:: asio::detail::argbyv( boost::asio::detail::rvref< asio_true_handler_type >())) + sizeof( boost::asio::detail::rorlvref< asio_true_handler_type >()( boost::asio::detail::lvref<const boost::system::error_code >()), char(0))> __attribute__((__unused__)) type_check; |
| 1880 | |
| 1881 | if (open_ec) |
| 1882 | { |
| 1883 | boost::asio::post(self_->impl_.get_executor(), |
| 1884 | boost::asio::detail::bind_handler( |
| 1885 | static_cast<ConnectHandler&&>(handler), open_ec)); |
| 1886 | } |
| 1887 | else |
| 1888 | { |
| 1889 | detail::non_const_lvalue<ConnectHandler> handler2(handler); |
| 1890 | self_->impl_.get_service().async_connect( |
| 1891 | self_->impl_.get_implementation(), peer_endpoint, |
| 1892 | handler2.value, self_->impl_.get_executor()); |
| 1893 | } |
| 1894 | } |
| 1895 | |
| 1896 | private: |
| 1897 | basic_socket* self_; |
| 1898 | }; |
| 1899 | |
| 1900 | class initiate_async_wait |
| 1901 | { |
| 1902 | public: |
| 1903 | typedef Executor executor_type; |
| 1904 | |
| 1905 | explicit initiate_async_wait(basic_socket* self) |
| 1906 | : self_(self) |
| 1907 | { |
| 1908 | } |
| 1909 | |
| 1910 | const executor_type& get_executor() const noexcept |
| 1911 | { |
| 1912 | return self_->get_executor(); |
| 1913 | } |
| 1914 | |
| 1915 | template <typename WaitHandler> |
| 1916 | void operator()(WaitHandler&& handler, wait_type w) const |
| 1917 | { |
| 1918 | // If you get an error on the following line it means that your handler |
| 1919 | // does not meet the documented type requirements for a WaitHandler. |
| 1920 | BOOST_ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler)typedef typename ::boost::asio::async_result< typename ::boost ::asio::decay<WaitHandler>::type, void(boost::system::error_code )>::completion_handler_type asio_true_handler_type; static_assert (sizeof(boost::asio::detail::one_arg_handler_test( boost::asio ::detail::rvref< asio_true_handler_type>(), static_cast <const boost::system::error_code*>(0))) == 1, "WaitHandler type requirements not met" ); typedef boost::asio::detail::handler_type_requirements< sizeof( boost::asio::detail::argbyv( boost::asio::detail::rvref < asio_true_handler_type>())) + sizeof( boost::asio::detail ::rorlvref< asio_true_handler_type>()( boost::asio::detail ::lvref<const boost::system::error_code>()), char(0))> __attribute__((__unused__)) type_check; |
| 1921 | |
| 1922 | detail::non_const_lvalue<WaitHandler> handler2(handler); |
| 1923 | self_->impl_.get_service().async_wait( |
| 1924 | self_->impl_.get_implementation(), w, |
| 1925 | handler2.value, self_->impl_.get_executor()); |
| 1926 | } |
| 1927 | |
| 1928 | private: |
| 1929 | basic_socket* self_; |
| 1930 | }; |
| 1931 | }; |
| 1932 | |
| 1933 | } // namespace asio |
| 1934 | } // namespace boost |
| 1935 | |
| 1936 | #include <boost/asio/detail/pop_options.hpp> |
| 1937 | |
| 1938 | #endif // BOOST_ASIO_BASIC_SOCKET_HPP |
| 1 | // |
| 2 | // detail/io_object_impl.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 | |
| 11 | #ifndef BOOST_ASIO_DETAIL_IO_OBJECT_IMPL_HPP |
| 12 | #define BOOST_ASIO_DETAIL_IO_OBJECT_IMPL_HPP |
| 13 | |
| 14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) |
| 15 | # pragma once |
| 16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
| 17 | |
| 18 | #include <new> |
| 19 | #include <boost/asio/detail/config.hpp> |
| 20 | #include <boost/asio/detail/type_traits.hpp> |
| 21 | #include <boost/asio/execution/executor.hpp> |
| 22 | #include <boost/asio/execution/context.hpp> |
| 23 | #include <boost/asio/io_context.hpp> |
| 24 | #include <boost/asio/query.hpp> |
| 25 | |
| 26 | #include <boost/asio/detail/push_options.hpp> |
| 27 | |
| 28 | namespace boost { |
| 29 | namespace asio { |
| 30 | namespace detail { |
| 31 | |
| 32 | template <typename IoObjectService, |
| 33 | typename Executor = io_context::executor_type> |
| 34 | class io_object_impl |
| 35 | { |
| 36 | public: |
| 37 | // The type of the service that will be used to provide I/O operations. |
| 38 | typedef IoObjectService service_type; |
| 39 | |
| 40 | // The underlying implementation type of I/O object. |
| 41 | typedef typename service_type::implementation_type implementation_type; |
| 42 | |
| 43 | // The type of the executor associated with the object. |
| 44 | typedef Executor executor_type; |
| 45 | |
| 46 | // Construct an I/O object using an executor. |
| 47 | explicit io_object_impl(int, const executor_type& ex) |
| 48 | : service_(&boost::asio::use_service<IoObjectService>( |
| 49 | io_object_impl::get_context(ex))), |
| 50 | executor_(ex) |
| 51 | { |
| 52 | service_->construct(implementation_); |
| 53 | } |
| 54 | |
| 55 | // Construct an I/O object using an execution context. |
| 56 | template <typename ExecutionContext> |
| 57 | explicit io_object_impl(int, int, ExecutionContext& context) |
| 58 | : service_(&boost::asio::use_service<IoObjectService>(context)), |
| 59 | executor_(context.get_executor()) |
| 60 | { |
| 61 | service_->construct(implementation_); |
| 62 | } |
| 63 | |
| 64 | // Move-construct an I/O object. |
| 65 | io_object_impl(io_object_impl&& other) |
| 66 | : service_(&other.get_service()), |
| 67 | executor_(other.get_executor()) |
| 68 | { |
| 69 | service_->move_construct(implementation_, other.implementation_); |
| 70 | } |
| 71 | |
| 72 | // Perform converting move-construction of an I/O object on the same service. |
| 73 | template <typename Executor1> |
| 74 | io_object_impl(io_object_impl<IoObjectService, Executor1>&& other) |
| 75 | : service_(&other.get_service()), |
| 76 | executor_(other.get_executor()) |
| 77 | { |
| 78 | service_->move_construct(implementation_, other.get_implementation()); |
| 79 | } |
| 80 | |
| 81 | // Perform converting move-construction of an I/O object on another service. |
| 82 | template <typename IoObjectService1, typename Executor1> |
| 83 | io_object_impl(io_object_impl<IoObjectService1, Executor1>&& other) |
| 84 | : service_(&boost::asio::use_service<IoObjectService>( |
| 85 | io_object_impl::get_context(other.get_executor()))), |
| 86 | executor_(other.get_executor()) |
| 87 | { |
| 88 | service_->converting_move_construct(implementation_, |
| 89 | other.get_service(), other.get_implementation()); |
| 90 | } |
| 91 | |
| 92 | // Destructor. |
| 93 | ~io_object_impl() |
| 94 | { |
| 95 | service_->destroy(implementation_); |
| 96 | } |
| 97 | |
| 98 | // Move-assign an I/O object. |
| 99 | io_object_impl& operator=(io_object_impl&& other) |
| 100 | { |
| 101 | if (this != &other) |
| 102 | { |
| 103 | service_->move_assign(implementation_, |
| 104 | *other.service_, other.implementation_); |
| 105 | executor_.~executor_type(); |
| 106 | new (&executor_) executor_type(other.executor_); |
| 107 | service_ = other.service_; |
| 108 | } |
| 109 | return *this; |
| 110 | } |
| 111 | |
| 112 | // Get the executor associated with the object. |
| 113 | const executor_type& get_executor() noexcept |
| 114 | { |
| 115 | return executor_; |
| 116 | } |
| 117 | |
| 118 | // Get the service associated with the I/O object. |
| 119 | service_type& get_service() |
| 120 | { |
| 121 | return *service_; |
| 122 | } |
| 123 | |
| 124 | // Get the service associated with the I/O object. |
| 125 | const service_type& get_service() const |
| 126 | { |
| 127 | return *service_; |
| 128 | } |
| 129 | |
| 130 | // Get the underlying implementation of the I/O object. |
| 131 | implementation_type& get_implementation() |
| 132 | { |
| 133 | return implementation_; |
| 134 | } |
| 135 | |
| 136 | // Get the underlying implementation of the I/O object. |
| 137 | const implementation_type& get_implementation() const |
| 138 | { |
| 139 | return implementation_; |
| 140 | } |
| 141 | |
| 142 | private: |
| 143 | // Helper function to get an executor's context. |
| 144 | template <typename T> |
| 145 | static execution_context& get_context(const T& t, |
| 146 | enable_if_t<execution::is_executor<T>::value>* = 0) |
| 147 | { |
| 148 | return boost::asio::query(t, execution::context); |
| 149 | } |
| 150 | |
| 151 | // Helper function to get an executor's context. |
| 152 | template <typename T> |
| 153 | static execution_context& get_context(const T& t, |
| 154 | enable_if_t<!execution::is_executor<T>::value>* = 0) |
| 155 | { |
| 156 | return t.context(); |
| 157 | } |
| 158 | |
| 159 | // Disallow copying and copy assignment. |
| 160 | io_object_impl(const io_object_impl&); |
| 161 | io_object_impl& operator=(const io_object_impl&); |
| 162 | |
| 163 | // The service associated with the I/O object. |
| 164 | service_type* service_; |
| 165 | |
| 166 | // The underlying implementation of the I/O object. |
| 167 | implementation_type implementation_; |
| 168 | |
| 169 | // The associated executor. |
| 170 | executor_type executor_; |
| 171 | }; |
| 172 | |
| 173 | } // namespace detail |
| 174 | } // namespace asio |
| 175 | } // namespace boost |
| 176 | |
| 177 | #include <boost/asio/detail/pop_options.hpp> |
| 178 | |
| 179 | #endif // BOOST_ASIO_DETAIL_IO_OBJECT_IMPL_HPP |
| 1 | // |
| 2 | // impl/io_context.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 | |
| 11 | #ifndef BOOST_ASIO_IMPL_IO_CONTEXT_HPP |
| 12 | #define BOOST_ASIO_IMPL_IO_CONTEXT_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/config.hpp> |
| 19 | #include <boost/asio/detail/completion_handler.hpp> |
| 20 | #include <boost/asio/detail/executor_op.hpp> |
| 21 | #include <boost/asio/detail/fenced_block.hpp> |
| 22 | #include <boost/asio/detail/handler_type_requirements.hpp> |
| 23 | #include <boost/asio/detail/non_const_lvalue.hpp> |
| 24 | #include <boost/asio/detail/service_registry.hpp> |
| 25 | #include <boost/asio/detail/throw_error.hpp> |
| 26 | #include <boost/asio/detail/type_traits.hpp> |
| 27 | |
| 28 | #include <boost/asio/detail/push_options.hpp> |
| 29 | |
| 30 | namespace boost { |
| 31 | namespace asio { |
| 32 | |
| 33 | template <typename Allocator> |
| 34 | io_context::io_context(allocator_arg_t, const Allocator& a) |
| 35 | : execution_context(std::allocator_arg, a, config_from_concurrency_hint()), |
| 36 | impl_(boost::asio::make_service<impl_type>(*this, false)) |
| 37 | { |
| 38 | } |
| 39 | |
| 40 | template <typename Allocator> |
| 41 | io_context::io_context(allocator_arg_t, |
| 42 | const Allocator& a, int concurrency_hint) |
| 43 | : execution_context(std::allocator_arg, a, |
| 44 | config_from_concurrency_hint(concurrency_hint)), |
| 45 | impl_(boost::asio::make_service<impl_type>(*this, false)) |
| 46 | { |
| 47 | } |
| 48 | |
| 49 | template <typename Allocator> |
| 50 | io_context::io_context(allocator_arg_t, const Allocator& a, |
| 51 | const execution_context::service_maker& initial_services) |
| 52 | : execution_context(std::allocator_arg, a, initial_services), |
| 53 | impl_(boost::asio::make_service<impl_type>(*this, false)) |
| 54 | { |
| 55 | } |
| 56 | |
| 57 | #if !defined(GENERATING_DOCUMENTATION) |
| 58 | |
| 59 | template <typename Service> |
| 60 | inline Service& use_service(io_context& ioc) |
| 61 | { |
| 62 | // Check that Service meets the necessary type requirements. |
| 63 | (void)static_cast<execution_context::service*>(static_cast<Service*>(0)); |
| 64 | (void)static_cast<const execution_context::id*>(&Service::id); |
| 65 | |
| 66 | return ioc.service_registry_->template use_service<Service>(ioc); |
| 67 | } |
| 68 | |
| 69 | template <> |
| 70 | inline detail::io_context_impl& use_service<detail::io_context_impl>( |
| 71 | io_context& ioc) |
| 72 | { |
| 73 | return ioc.impl_; |
| 74 | } |
| 75 | |
| 76 | #endif // !defined(GENERATING_DOCUMENTATION) |
| 77 | |
| 78 | inline io_context::executor_type |
| 79 | io_context::get_executor() noexcept |
| 80 | { |
| 81 | return executor_type(*this); |
| 82 | } |
| 83 | |
| 84 | template <typename Rep, typename Period> |
| 85 | std::size_t io_context::run_for( |
| 86 | const chrono::duration<Rep, Period>& rel_time) |
| 87 | { |
| 88 | return this->run_until(chrono::steady_clock::now() + rel_time); |
| 89 | } |
| 90 | |
| 91 | template <typename Clock, typename Duration> |
| 92 | std::size_t io_context::run_until( |
| 93 | const chrono::time_point<Clock, Duration>& abs_time) |
| 94 | { |
| 95 | std::size_t n = 0; |
| 96 | while (this->run_one_until(abs_time)) |
| 97 | if (n != (std::numeric_limits<std::size_t>::max)()) |
| 98 | ++n; |
| 99 | return n; |
| 100 | } |
| 101 | |
| 102 | template <typename Rep, typename Period> |
| 103 | std::size_t io_context::run_one_for( |
| 104 | const chrono::duration<Rep, Period>& rel_time) |
| 105 | { |
| 106 | return this->run_one_until(chrono::steady_clock::now() + rel_time); |
| 107 | } |
| 108 | |
| 109 | template <typename Clock, typename Duration> |
| 110 | std::size_t io_context::run_one_until( |
| 111 | const chrono::time_point<Clock, Duration>& abs_time) |
| 112 | { |
| 113 | typename Clock::time_point now = Clock::now(); |
| 114 | while (now < abs_time) |
| 115 | { |
| 116 | typename Clock::duration rel_time = abs_time - now; |
| 117 | if (rel_time > chrono::seconds(1)) |
| 118 | rel_time = chrono::seconds(1); |
| 119 | |
| 120 | boost::system::error_code ec; |
| 121 | std::size_t s = impl_.wait_one( |
| 122 | static_cast<long>(chrono::duration_cast< |
| 123 | chrono::microseconds>(rel_time).count()), ec); |
| 124 | boost::asio::detail::throw_error(ec); |
| 125 | |
| 126 | if (s || impl_.stopped()) |
| 127 | return s; |
| 128 | |
| 129 | now = Clock::now(); |
| 130 | } |
| 131 | |
| 132 | return 0; |
| 133 | } |
| 134 | |
| 135 | #if !defined(BOOST_ASIO_NO_DEPRECATED) |
| 136 | |
| 137 | template <typename Handler> |
| 138 | #if defined(GENERATING_DOCUMENTATION) |
| 139 | unspecified |
| 140 | #else |
| 141 | inline detail::wrapped_handler<io_context&, Handler> |
| 142 | #endif |
| 143 | io_context::wrap(Handler handler) |
| 144 | { |
| 145 | return detail::wrapped_handler<io_context&, Handler>(*this, handler); |
| 146 | } |
| 147 | |
| 148 | #endif // !defined(BOOST_ASIO_NO_DEPRECATED) |
| 149 | |
| 150 | template <typename Allocator, uintptr_t Bits> |
| 151 | io_context::basic_executor_type<Allocator, Bits>& |
| 152 | io_context::basic_executor_type<Allocator, Bits>::operator=( |
| 153 | const basic_executor_type& other) noexcept |
| 154 | { |
| 155 | if (this != &other) |
| 156 | { |
| 157 | static_cast<Allocator&>(*this) = static_cast<const Allocator&>(other); |
| 158 | io_context* old_io_context = context_ptr(); |
| 159 | target_ = other.target_; |
| 160 | if (Bits & outstanding_work_tracked) |
| 161 | { |
| 162 | if (context_ptr()) |
| 163 | context_ptr()->impl_.work_started(); |
| 164 | if (old_io_context) |
| 165 | old_io_context->impl_.work_finished(); |
| 166 | } |
| 167 | } |
| 168 | return *this; |
| 169 | } |
| 170 | |
| 171 | template <typename Allocator, uintptr_t Bits> |
| 172 | io_context::basic_executor_type<Allocator, Bits>& |
| 173 | io_context::basic_executor_type<Allocator, Bits>::operator=( |
| 174 | basic_executor_type&& other) noexcept |
| 175 | { |
| 176 | if (this != &other) |
| 177 | { |
| 178 | static_cast<Allocator&>(*this) = static_cast<Allocator&&>(other); |
| 179 | io_context* old_io_context = context_ptr(); |
| 180 | target_ = other.target_; |
| 181 | if (Bits & outstanding_work_tracked) |
| 182 | { |
| 183 | other.target_ = 0; |
| 184 | if (old_io_context) |
| 185 | old_io_context->impl_.work_finished(); |
| 186 | } |
| 187 | } |
| 188 | return *this; |
| 189 | } |
| 190 | |
| 191 | template <typename Allocator, uintptr_t Bits> |
| 192 | inline bool io_context::basic_executor_type<Allocator, |
| 193 | Bits>::running_in_this_thread() const noexcept |
| 194 | { |
| 195 | return context_ptr()->impl_.can_dispatch(); |
| 196 | } |
| 197 | |
| 198 | template <typename Allocator, uintptr_t Bits> |
| 199 | template <typename Function> |
| 200 | void io_context::basic_executor_type<Allocator, Bits>::execute( |
| 201 | Function&& f) const |
| 202 | { |
| 203 | typedef decay_t<Function> function_type; |
| 204 | |
| 205 | // Invoke immediately if the blocking.possibly property is enabled and we are |
| 206 | // already inside the thread pool. |
| 207 | if ((bits() & blocking_never) == 0 && context_ptr()->impl_.can_dispatch()) |
| 208 | { |
| 209 | // Make a local, non-const copy of the function. |
| 210 | function_type tmp(static_cast<Function&&>(f)); |
| 211 | |
| 212 | #if !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 213 | try |
| 214 | { |
| 215 | #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 216 | detail::fenced_block b(detail::fenced_block::full); |
| 217 | static_cast<function_type&&>(tmp)(); |
| 218 | return; |
| 219 | #if !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 220 | } |
| 221 | catch (...) |
| 222 | { |
| 223 | context_ptr()->impl_.capture_current_exception(); |
| 224 | return; |
| 225 | } |
| 226 | #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 227 | } |
| 228 | |
| 229 | // Allocate and construct an operation to wrap the function. |
| 230 | typedef detail::executor_op<function_type, Allocator, detail::operation> op; |
| 231 | typename op::ptr p = { |
| 232 | detail::addressof(static_cast<const Allocator&>(*this)), |
| 233 | op::ptr::allocate(static_cast<const Allocator&>(*this)), 0 }; |
| 234 | p.p = new (p.v) op(static_cast<Function&&>(f), |
| 235 | static_cast<const Allocator&>(*this)); |
| 236 | |
| 237 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p,(void)0 |
| 238 | "io_context", context_ptr(), 0, "execute"))(void)0; |
| 239 | |
| 240 | context_ptr()->impl_.post_immediate_completion(p.p, |
| 241 | (bits() & relationship_continuation) != 0); |
| 242 | p.v = p.p = 0; |
| 243 | } |
| 244 | |
| 245 | #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
| 246 | template <typename Allocator, uintptr_t Bits> |
| 247 | inline io_context& io_context::basic_executor_type< |
| 248 | Allocator, Bits>::context() const noexcept |
| 249 | { |
| 250 | return *context_ptr(); |
| 251 | } |
| 252 | |
| 253 | template <typename Allocator, uintptr_t Bits> |
| 254 | inline void io_context::basic_executor_type<Allocator, |
| 255 | Bits>::on_work_started() const noexcept |
| 256 | { |
| 257 | context_ptr()->impl_.work_started(); |
| 258 | } |
| 259 | |
| 260 | template <typename Allocator, uintptr_t Bits> |
| 261 | inline void io_context::basic_executor_type<Allocator, |
| 262 | Bits>::on_work_finished() const noexcept |
| 263 | { |
| 264 | context_ptr()->impl_.work_finished(); |
| 265 | } |
| 266 | |
| 267 | template <typename Allocator, uintptr_t Bits> |
| 268 | template <typename Function, typename OtherAllocator> |
| 269 | void io_context::basic_executor_type<Allocator, Bits>::dispatch( |
| 270 | Function&& f, const OtherAllocator& a) const |
| 271 | { |
| 272 | typedef decay_t<Function> function_type; |
| 273 | |
| 274 | // Invoke immediately if we are already inside the thread pool. |
| 275 | if (context_ptr()->impl_.can_dispatch()) |
| 276 | { |
| 277 | // Make a local, non-const copy of the function. |
| 278 | function_type tmp(static_cast<Function&&>(f)); |
| 279 | |
| 280 | detail::fenced_block b(detail::fenced_block::full); |
| 281 | static_cast<function_type&&>(tmp)(); |
| 282 | return; |
| 283 | } |
| 284 | |
| 285 | // Allocate and construct an operation to wrap the function. |
| 286 | typedef detail::executor_op<function_type, |
| 287 | OtherAllocator, detail::operation> op; |
| 288 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
| 289 | p.p = new (p.v) op(static_cast<Function&&>(f), a); |
| 290 | |
| 291 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p,(void)0 |
| 292 | "io_context", context_ptr(), 0, "dispatch"))(void)0; |
| 293 | |
| 294 | context_ptr()->impl_.post_immediate_completion(p.p, false); |
| 295 | p.v = p.p = 0; |
| 296 | } |
| 297 | |
| 298 | template <typename Allocator, uintptr_t Bits> |
| 299 | template <typename Function, typename OtherAllocator> |
| 300 | void io_context::basic_executor_type<Allocator, Bits>::post( |
| 301 | Function&& f, const OtherAllocator& a) const |
| 302 | { |
| 303 | // Allocate and construct an operation to wrap the function. |
| 304 | typedef detail::executor_op<decay_t<Function>, |
| 305 | OtherAllocator, detail::operation> op; |
| 306 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
| 307 | p.p = new (p.v) op(static_cast<Function&&>(f), a); |
| 308 | |
| 309 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p,(void)0 |
| 310 | "io_context", context_ptr(), 0, "post"))(void)0; |
| 311 | |
| 312 | context_ptr()->impl_.post_immediate_completion(p.p, false); |
| 313 | p.v = p.p = 0; |
| 314 | } |
| 315 | |
| 316 | template <typename Allocator, uintptr_t Bits> |
| 317 | template <typename Function, typename OtherAllocator> |
| 318 | void io_context::basic_executor_type<Allocator, Bits>::defer( |
| 319 | Function&& f, const OtherAllocator& a) const |
| 320 | { |
| 321 | // Allocate and construct an operation to wrap the function. |
| 322 | typedef detail::executor_op<decay_t<Function>, |
| 323 | OtherAllocator, detail::operation> op; |
| 324 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
| 325 | p.p = new (p.v) op(static_cast<Function&&>(f), a); |
| 326 | |
| 327 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p,(void)0 |
| 328 | "io_context", context_ptr(), 0, "defer"))(void)0; |
| 329 | |
| 330 | context_ptr()->impl_.post_immediate_completion(p.p, true); |
| 331 | p.v = p.p = 0; |
| 332 | } |
| 333 | #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
| 334 | |
| 335 | inline boost::asio::io_context& io_context::service::get_io_context() |
| 336 | { |
| 337 | return static_cast<boost::asio::io_context&>(context()); |
| 338 | } |
| 339 | |
| 340 | } // namespace asio |
| 341 | } // namespace boost |
| 342 | |
| 343 | #include <boost/asio/detail/pop_options.hpp> |
| 344 | |
| 345 | #endif // BOOST_ASIO_IMPL_IO_CONTEXT_HPP |
| 1 | // |
| 2 | // detail/impl/service_registry.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 | |
| 11 | #ifndef BOOST_ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP |
| 12 | #define BOOST_ASIO_DETAIL_IMPL_SERVICE_REGISTRY_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/push_options.hpp> |
| 19 | |
| 20 | namespace boost { |
| 21 | namespace asio { |
| 22 | namespace detail { |
| 23 | |
| 24 | template <typename Service> |
| 25 | Service& service_registry::use_service() |
| 26 | { |
| 27 | execution_context::service::key key; |
| 28 | init_key<Service>(key, 0); |
| 29 | factory_type factory = &service_registry::create<Service, execution_context>; |
| 30 | return *static_cast<Service*>(do_use_service(key, factory, &owner_)); |
| 31 | } |
| 32 | |
| 33 | template <typename Service> |
| 34 | Service& service_registry::use_service(io_context& owner) |
| 35 | { |
| 36 | execution_context::service::key key; |
| 37 | init_key<Service>(key, 0); |
| 38 | factory_type factory = &service_registry::create<Service, io_context>; |
| 39 | return *static_cast<Service*>(do_use_service(key, factory, &owner)); |
| 40 | } |
| 41 | |
| 42 | template <typename Service, typename... Args> |
| 43 | Service& service_registry::make_service(Args&&... args) |
| 44 | { |
| 45 | auto_service_ptr new_service = |
| 46 | { create<Service, execution_context>(owner_, |
| 47 | &owner_, static_cast<Args&&>(args)...) }; |
| 48 | add_service(static_cast<Service*>(new_service.ptr_)); |
| 49 | Service& result = *static_cast<Service*>(new_service.ptr_); |
| 50 | new_service.ptr_ = 0; |
| 51 | return result; |
| 52 | } |
| 53 | |
| 54 | template <typename Service> |
| 55 | void service_registry::add_service(Service* new_service) |
| 56 | { |
| 57 | execution_context::service::key key; |
| 58 | init_key<Service>(key, 0); |
| 59 | return do_add_service(key, new_service); |
| 60 | } |
| 61 | |
| 62 | template <typename Service> |
| 63 | bool service_registry::has_service() const |
| 64 | { |
| 65 | execution_context::service::key key; |
| 66 | init_key<Service>(key, 0); |
| 67 | return do_has_service(key); |
| 68 | } |
| 69 | |
| 70 | template <typename Service> |
| 71 | inline void service_registry::init_key( |
| 72 | execution_context::service::key& key, ...) |
| 73 | { |
| 74 | init_key_from_id(key, Service::id); |
| 75 | } |
| 76 | |
| 77 | #if !defined(BOOST_ASIO_NO_TYPEID) |
| 78 | template <typename Service> |
| 79 | void service_registry::init_key(execution_context::service::key& key, |
| 80 | enable_if_t<is_base_of<typename Service::key_type, Service>::value>*) |
| 81 | { |
| 82 | key.type_info_ = &typeid(typeid_wrapper<Service>); |
| 83 | key.id_ = 0; |
| 84 | } |
| 85 | |
| 86 | template <typename Service> |
| 87 | void service_registry::init_key_from_id(execution_context::service::key& key, |
| 88 | const service_id<Service>& /*id*/) |
| 89 | { |
| 90 | key.type_info_ = &typeid(typeid_wrapper<Service>); |
| 91 | key.id_ = 0; |
| 92 | } |
| 93 | #endif // !defined(BOOST_ASIO_NO_TYPEID) |
| 94 | |
| 95 | template <typename Service, typename Owner, typename... Args> |
| 96 | execution_context::service* service_registry::create( |
| 97 | execution_context& context, void* owner, Args&&... args) |
| 98 | { |
| 99 | Service* svc = allocate_object<Service>( |
| 100 | execution_context::allocator<void>(context), |
| 101 | *static_cast<Owner*>(owner), static_cast<Args&&>(args)...); |
| 102 | svc->destroy_ = &service_registry::destroy_allocated<Service>; |
| 103 | return svc; |
| 104 | } |
| 105 | |
| 106 | template <typename Service> |
| 107 | void service_registry::destroy_allocated(execution_context::service* service) |
| 108 | { |
| 109 | deallocate_object(execution_context::allocator<void>(service->owner_), |
| 110 | static_cast<Service*>(service)); |
| 111 | } |
| 112 | |
| 113 | } // namespace detail |
| 114 | } // namespace asio |
| 115 | } // namespace boost |
| 116 | |
| 117 | #include <boost/asio/detail/pop_options.hpp> |
| 118 | |
| 119 | #endif // BOOST_ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP |
| 1 | // |
| 2 | // detail/impl/service_registry.ipp |
| 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 | |
| 11 | #ifndef BOOST_ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP |
| 12 | #define BOOST_ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP |
| 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 | #include <vector> |
| 20 | #include <boost/asio/detail/service_registry.hpp> |
| 21 | #include <boost/asio/detail/throw_exception.hpp> |
| 22 | |
| 23 | #include <boost/asio/detail/push_options.hpp> |
| 24 | |
| 25 | namespace boost { |
| 26 | namespace asio { |
| 27 | namespace detail { |
| 28 | |
| 29 | service_registry::service_registry(execution_context& owner) |
| 30 | : owner_(owner), |
| 31 | first_service_(0) |
| 32 | { |
| 33 | } |
| 34 | |
| 35 | service_registry::~service_registry() |
| 36 | { |
| 37 | } |
| 38 | |
| 39 | void service_registry::shutdown_services() |
| 40 | { |
| 41 | execution_context::service* service = first_service_; |
| 42 | while (service) |
| 43 | { |
| 44 | service->shutdown(); |
| 45 | service = service->next_; |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | void service_registry::destroy_services() |
| 50 | { |
| 51 | while (first_service_) |
| 52 | { |
| 53 | execution_context::service* next_service = first_service_->next_; |
| 54 | first_service_->destroy_(first_service_); |
| 55 | first_service_ = next_service; |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | void service_registry::notify_fork(execution_context::fork_event fork_ev) |
| 60 | { |
| 61 | // Make a copy of all of the services while holding the lock. We don't want |
| 62 | // to hold the lock while calling into each service, as it may try to call |
| 63 | // back into this class. |
| 64 | std::vector<execution_context::service*> services; |
| 65 | { |
| 66 | boost::asio::detail::mutex::scoped_lock lock(mutex_); |
| 67 | execution_context::service* service = first_service_; |
| 68 | while (service) |
| 69 | { |
| 70 | services.push_back(service); |
| 71 | service = service->next_; |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | // If processing the fork_prepare event, we want to go in reverse order of |
| 76 | // service registration, which happens to be the existing order of the |
| 77 | // services in the vector. For the other events we want to go in the other |
| 78 | // direction. |
| 79 | std::size_t num_services = services.size(); |
| 80 | if (fork_ev == execution_context::fork_prepare) |
| 81 | for (std::size_t i = 0; i < num_services; ++i) |
| 82 | services[i]->notify_fork(fork_ev); |
| 83 | else |
| 84 | for (std::size_t i = num_services; i > 0; --i) |
| 85 | services[i - 1]->notify_fork(fork_ev); |
| 86 | } |
| 87 | |
| 88 | void service_registry::init_key_from_id(execution_context::service::key& key, |
| 89 | const execution_context::id& id) |
| 90 | { |
| 91 | key.type_info_ = 0; |
| 92 | key.id_ = &id; |
| 93 | } |
| 94 | |
| 95 | bool service_registry::keys_match( |
| 96 | const execution_context::service::key& key1, |
| 97 | const execution_context::service::key& key2) |
| 98 | { |
| 99 | if (key1.id_ && key2.id_) |
| 100 | if (key1.id_ == key2.id_) |
| 101 | return true; |
| 102 | if (key1.type_info_ && key2.type_info_) |
| 103 | if (*key1.type_info_ == *key2.type_info_) |
| 104 | return true; |
| 105 | return false; |
| 106 | } |
| 107 | |
| 108 | void service_registry::destroy_added(execution_context::service* service) |
| 109 | { |
| 110 | delete service; |
| 111 | } |
| 112 | |
| 113 | service_registry::auto_service_ptr::~auto_service_ptr() |
| 114 | { |
| 115 | if (ptr_) |
| 116 | ptr_->destroy_(ptr_); |
| 117 | } |
| 118 | |
| 119 | execution_context::service* service_registry::do_use_service( |
| 120 | const execution_context::service::key& key, |
| 121 | factory_type factory, void* owner) |
| 122 | { |
| 123 | boost::asio::detail::mutex::scoped_lock lock(mutex_); |
| 124 | |
| 125 | // First see if there is an existing service object with the given key. |
| 126 | execution_context::service* service = first_service_; |
| 127 | while (service) |
| 128 | { |
| 129 | if (keys_match(service->key_, key)) |
| 130 | return service; |
| 131 | service = service->next_; |
| 132 | } |
| 133 | |
| 134 | // Create a new service object. The service registry's mutex is not locked |
| 135 | // at this time to allow for nested calls into this function from the new |
| 136 | // service's constructor. |
| 137 | lock.unlock(); |
| 138 | auto_service_ptr new_service = { factory(owner_, owner) }; |
| 139 | new_service.ptr_->key_ = key; |
| 140 | lock.lock(); |
| 141 | |
| 142 | // Check that nobody else created another service object of the same type |
| 143 | // while the lock was released. |
| 144 | service = first_service_; |
| 145 | while (service) |
| 146 | { |
| 147 | if (keys_match(service->key_, key)) |
| 148 | return service; |
| 149 | service = service->next_; |
| 150 | } |
| 151 | |
| 152 | // Service was successfully initialised, pass ownership to registry. |
| 153 | new_service.ptr_->next_ = first_service_; |
| 154 | first_service_ = new_service.ptr_; |
| 155 | new_service.ptr_ = 0; |
| 156 | return first_service_; |
| 157 | } |
| 158 | |
| 159 | void service_registry::do_add_service( |
| 160 | const execution_context::service::key& key, |
| 161 | execution_context::service* new_service) |
| 162 | { |
| 163 | if (&owner_ != &new_service->context()) |
| 164 | boost::asio::detail::throw_exception(invalid_service_owner()); |
| 165 | |
| 166 | boost::asio::detail::mutex::scoped_lock lock(mutex_); |
| 167 | |
| 168 | // Check if there is an existing service object with the given key. |
| 169 | execution_context::service* service = first_service_; |
| 170 | while (service) |
| 171 | { |
| 172 | if (keys_match(service->key_, key)) |
| 173 | boost::asio::detail::throw_exception(service_already_exists()); |
| 174 | service = service->next_; |
| 175 | } |
| 176 | |
| 177 | // Take ownership of the service object. |
| 178 | if (!new_service->destroy_) |
| 179 | new_service->destroy_ = &service_registry::destroy_added; |
| 180 | new_service->key_ = key; |
| 181 | new_service->next_ = first_service_; |
| 182 | first_service_ = new_service; |
| 183 | } |
| 184 | |
| 185 | bool service_registry::do_has_service( |
| 186 | const execution_context::service::key& key) const |
| 187 | { |
| 188 | boost::asio::detail::mutex::scoped_lock lock(mutex_); |
| 189 | |
| 190 | execution_context::service* service = first_service_; |
| 191 | while (service) |
| 192 | { |
| 193 | if (keys_match(service->key_, key)) |
| 194 | return true; |
| 195 | service = service->next_; |
| 196 | } |
| 197 | |
| 198 | return false; |
| 199 | } |
| 200 | |
| 201 | } // namespace detail |
| 202 | } // namespace asio |
| 203 | } // namespace boost |
| 204 | |
| 205 | #include <boost/asio/detail/pop_options.hpp> |
| 206 | |
| 207 | #endif // BOOST_ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP |
| 1 | // |
| 2 | // detail/memory.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 | |
| 11 | #ifndef BOOST_ASIO_DETAIL_MEMORY_HPP |
| 12 | #define BOOST_ASIO_DETAIL_MEMORY_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 | #include <cstddef> |
| 20 | #include <cstdlib> |
| 21 | #include <memory> |
| 22 | #include <new> |
| 23 | #include <boost/asio/detail/cstdint.hpp> |
| 24 | #include <boost/asio/detail/throw_exception.hpp> |
| 25 | |
| 26 | #if !defined(BOOST_ASIO_HAS_STD_ALIGNED_ALLOC1) \ |
| 27 | && defined(BOOST_ASIO_HAS_BOOST_ALIGN1) |
| 28 | # include <boost/align/aligned_alloc.hpp> |
| 29 | #endif // !defined(BOOST_ASIO_HAS_STD_ALIGNED_ALLOC) |
| 30 | // && defined(BOOST_ASIO_HAS_BOOST_ALIGN) |
| 31 | |
| 32 | namespace boost { |
| 33 | namespace asio { |
| 34 | namespace detail { |
| 35 | |
| 36 | using std::allocate_shared; |
| 37 | using std::make_shared; |
| 38 | using std::shared_ptr; |
| 39 | using std::weak_ptr; |
| 40 | using std::addressof; |
| 41 | |
| 42 | #if defined(BOOST_ASIO_HAS_STD_TO_ADDRESS) |
| 43 | using std::to_address; |
| 44 | #else // defined(BOOST_ASIO_HAS_STD_TO_ADDRESS) |
| 45 | template <typename T> |
| 46 | inline T* to_address(T* p) { return p; } |
| 47 | template <typename T> |
| 48 | inline const T* to_address(const T* p) { return p; } |
| 49 | template <typename T> |
| 50 | inline volatile T* to_address(volatile T* p) { return p; } |
| 51 | template <typename T> |
| 52 | inline const volatile T* to_address(const volatile T* p) { return p; } |
| 53 | #endif // defined(BOOST_ASIO_HAS_STD_TO_ADDRESS) |
| 54 | |
| 55 | inline void* align(std::size_t alignment, |
| 56 | std::size_t size, void*& ptr, std::size_t& space) |
| 57 | { |
| 58 | return std::align(alignment, size, ptr, space); |
| 59 | } |
| 60 | |
| 61 | template <typename T, typename Allocator, typename... Args> |
| 62 | T* allocate_object(const Allocator& a, Args&&... args) |
| 63 | { |
| 64 | typename std::allocator_traits<Allocator>::template rebind_alloc<T> alloc(a); |
| 65 | T* raw = std::allocator_traits<decltype(alloc)>::allocate(alloc, 1); |
| 66 | #if !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 67 | try |
| 68 | #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 69 | { |
| 70 | return new (raw) T(static_cast<Args&&>(args)...); |
| 71 | } |
| 72 | #if !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 73 | catch (...) |
| 74 | { |
| 75 | std::allocator_traits<decltype(alloc)>::deallocate(alloc, raw, 1); |
| 76 | throw; |
| 77 | } |
| 78 | #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 79 | } |
| 80 | |
| 81 | template <typename Allocator, typename T> |
| 82 | void deallocate_object(const Allocator& a, T* ptr) |
| 83 | { |
| 84 | typename std::allocator_traits<Allocator>::template rebind_alloc<T> alloc(a); |
| 85 | std::allocator_traits<decltype(alloc)>::destroy(alloc, ptr); |
| 86 | std::allocator_traits<decltype(alloc)>::deallocate(alloc, ptr, 1); |
| 87 | } |
| 88 | |
| 89 | } // namespace detail |
| 90 | |
| 91 | using std::allocator_arg_t; |
| 92 | # define BOOST_ASIO_USES_ALLOCATOR(t)namespace std { template <typename Allocator> struct uses_allocator <t, Allocator> : true_type {}; } \ |
| 93 | namespace std { \ |
| 94 | template <typename Allocator> \ |
| 95 | struct uses_allocator<t, Allocator> : true_type {}; \ |
| 96 | } \ |
| 97 | /**/ |
| 98 | # define BOOST_ASIO_REBIND_ALLOC(alloc, t)typename std::allocator_traits<alloc>::template rebind_alloc <t> \ |
| 99 | typename std::allocator_traits<alloc>::template rebind_alloc<t> |
| 100 | /**/ |
| 101 | |
| 102 | inline void* aligned_new(std::size_t align, std::size_t size) |
| 103 | { |
| 104 | #if defined(BOOST_ASIO_HAS_STD_ALIGNED_ALLOC1) |
| 105 | align = (align < BOOST_ASIO_DEFAULT_ALIGN16UL) ? BOOST_ASIO_DEFAULT_ALIGN16UL : align; |
| 106 | size = (size % align == 0) ? size : size + (align - size % align); |
| 107 | void* ptr = std::aligned_alloc(align, size); |
| 108 | if (!ptr) |
| 109 | { |
| 110 | std::bad_alloc ex; |
| 111 | boost::asio::detail::throw_exception(ex); |
| 112 | } |
| 113 | return ptr; |
| 114 | #elif defined(BOOST_ASIO_HAS_BOOST_ALIGN1) |
| 115 | align = (align < BOOST_ASIO_DEFAULT_ALIGN16UL) ? BOOST_ASIO_DEFAULT_ALIGN16UL : align; |
| 116 | size = (size % align == 0) ? size : size + (align - size % align); |
| 117 | void* ptr = boost::alignment::aligned_alloc(align, size); |
| 118 | if (!ptr) |
| 119 | { |
| 120 | std::bad_alloc ex; |
| 121 | boost::asio::detail::throw_exception(ex); |
| 122 | } |
| 123 | return ptr; |
| 124 | #elif defined(BOOST_ASIO_MSVC) |
| 125 | align = (align < BOOST_ASIO_DEFAULT_ALIGN16UL) ? BOOST_ASIO_DEFAULT_ALIGN16UL : align; |
| 126 | size = (size % align == 0) ? size : size + (align - size % align); |
| 127 | void* ptr = _aligned_malloc(size, align); |
| 128 | if (!ptr) |
| 129 | { |
| 130 | std::bad_alloc ex; |
| 131 | boost::asio::detail::throw_exception(ex); |
| 132 | } |
| 133 | return ptr; |
| 134 | #else // defined(BOOST_ASIO_MSVC) |
| 135 | (void)align; |
| 136 | return ::operator new(size); |
| 137 | #endif // defined(BOOST_ASIO_MSVC) |
| 138 | } |
| 139 | |
| 140 | inline void aligned_delete(void* ptr) |
| 141 | { |
| 142 | #if defined(BOOST_ASIO_HAS_STD_ALIGNED_ALLOC1) |
| 143 | std::free(ptr); |
| 144 | #elif defined(BOOST_ASIO_HAS_BOOST_ALIGN1) |
| 145 | boost::alignment::aligned_free(ptr); |
| 146 | #elif defined(BOOST_ASIO_MSVC) |
| 147 | _aligned_free(ptr); |
| 148 | #else // defined(BOOST_ASIO_MSVC) |
| 149 | ::operator delete(ptr); |
| 150 | #endif // defined(BOOST_ASIO_MSVC) |
| 151 | } |
| 152 | |
| 153 | } // namespace asio |
| 154 | } // namespace boost |
| 155 | |
| 156 | #endif // BOOST_ASIO_DETAIL_MEMORY_HPP |
| 1 | // |
| 2 | // detail/reactive_socket_service.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 | |
| 11 | #ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP |
| 12 | #define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_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_HAS_IOCP) \ |
| 21 | && !defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 22 | |
| 23 | #include <boost/asio/buffer.hpp> |
| 24 | #include <boost/asio/error.hpp> |
| 25 | #include <boost/asio/execution_context.hpp> |
| 26 | #include <boost/asio/socket_base.hpp> |
| 27 | #include <boost/asio/detail/buffer_sequence_adapter.hpp> |
| 28 | #include <boost/asio/detail/memory.hpp> |
| 29 | #include <boost/asio/detail/noncopyable.hpp> |
| 30 | #include <boost/asio/detail/reactive_null_buffers_op.hpp> |
| 31 | #include <boost/asio/detail/reactive_socket_accept_op.hpp> |
| 32 | #include <boost/asio/detail/reactive_socket_connect_op.hpp> |
| 33 | #include <boost/asio/detail/reactive_socket_recvfrom_op.hpp> |
| 34 | #include <boost/asio/detail/reactive_socket_sendto_op.hpp> |
| 35 | #include <boost/asio/detail/reactive_socket_service_base.hpp> |
| 36 | #include <boost/asio/detail/reactor.hpp> |
| 37 | #include <boost/asio/detail/reactor_op.hpp> |
| 38 | #include <boost/asio/detail/socket_holder.hpp> |
| 39 | #include <boost/asio/detail/socket_ops.hpp> |
| 40 | #include <boost/asio/detail/socket_types.hpp> |
| 41 | |
| 42 | #include <boost/asio/detail/push_options.hpp> |
| 43 | |
| 44 | namespace boost { |
| 45 | namespace asio { |
| 46 | namespace detail { |
| 47 | |
| 48 | template <typename Protocol> |
| 49 | class reactive_socket_service : |
| 50 | public execution_context_service_base<reactive_socket_service<Protocol>>, |
| 51 | public reactive_socket_service_base |
| 52 | { |
| 53 | public: |
| 54 | // The protocol type. |
| 55 | typedef Protocol protocol_type; |
| 56 | |
| 57 | // The endpoint type. |
| 58 | typedef typename Protocol::endpoint endpoint_type; |
| 59 | |
| 60 | // The native type of a socket. |
| 61 | typedef socket_type native_handle_type; |
| 62 | |
| 63 | // The implementation type of the socket. |
| 64 | struct implementation_type : |
| 65 | reactive_socket_service_base::base_implementation_type |
| 66 | { |
| 67 | // Default constructor. |
| 68 | implementation_type() |
| 69 | : protocol_(endpoint_type().protocol()) |
| 70 | { |
| 71 | } |
| 72 | |
| 73 | // The protocol associated with the socket. |
| 74 | protocol_type protocol_; |
| 75 | }; |
| 76 | |
| 77 | // Constructor. |
| 78 | reactive_socket_service(execution_context& context) |
| 79 | : execution_context_service_base< |
| 80 | reactive_socket_service<Protocol>>(context), |
| 81 | reactive_socket_service_base(context) |
| 82 | { |
| 83 | } |
| 84 | |
| 85 | // Destroy all user-defined handler objects owned by the service. |
| 86 | void shutdown() |
| 87 | { |
| 88 | this->base_shutdown(); |
| 89 | } |
| 90 | |
| 91 | // Move-construct a new socket implementation. |
| 92 | void move_construct(implementation_type& impl, |
| 93 | implementation_type& other_impl) noexcept |
| 94 | { |
| 95 | this->base_move_construct(impl, other_impl); |
| 96 | |
| 97 | impl.protocol_ = other_impl.protocol_; |
| 98 | other_impl.protocol_ = endpoint_type().protocol(); |
| 99 | } |
| 100 | |
| 101 | // Move-assign from another socket implementation. |
| 102 | void move_assign(implementation_type& impl, |
| 103 | reactive_socket_service_base& other_service, |
| 104 | implementation_type& other_impl) |
| 105 | { |
| 106 | this->base_move_assign(impl, other_service, other_impl); |
| 107 | |
| 108 | impl.protocol_ = other_impl.protocol_; |
| 109 | other_impl.protocol_ = endpoint_type().protocol(); |
| 110 | } |
| 111 | |
| 112 | // Move-construct a new socket implementation from another protocol type. |
| 113 | template <typename Protocol1> |
| 114 | void converting_move_construct(implementation_type& impl, |
| 115 | reactive_socket_service<Protocol1>&, |
| 116 | typename reactive_socket_service< |
| 117 | Protocol1>::implementation_type& other_impl) |
| 118 | { |
| 119 | this->base_move_construct(impl, other_impl); |
| 120 | |
| 121 | impl.protocol_ = protocol_type(other_impl.protocol_); |
| 122 | other_impl.protocol_ = typename Protocol1::endpoint().protocol(); |
| 123 | } |
| 124 | |
| 125 | // Open a new socket implementation. |
| 126 | boost::system::error_code open(implementation_type& impl, |
| 127 | const protocol_type& protocol, boost::system::error_code& ec) |
| 128 | { |
| 129 | if (!do_open(impl, protocol.family(), |
| 130 | protocol.type(), protocol.protocol(), ec)) |
| 131 | impl.protocol_ = protocol; |
| 132 | |
| 133 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 134 | return ec; |
| 135 | } |
| 136 | |
| 137 | // Assign a native socket to a socket implementation. |
| 138 | boost::system::error_code assign(implementation_type& impl, |
| 139 | const protocol_type& protocol, const native_handle_type& native_socket, |
| 140 | boost::system::error_code& ec) |
| 141 | { |
| 142 | if (!do_assign(impl, protocol.type(), native_socket, ec)) |
| 143 | impl.protocol_ = protocol; |
| 144 | |
| 145 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 146 | return ec; |
| 147 | } |
| 148 | |
| 149 | // Get the native socket representation. |
| 150 | native_handle_type native_handle(implementation_type& impl) |
| 151 | { |
| 152 | return impl.socket_; |
| 153 | } |
| 154 | |
| 155 | // Bind the socket to the specified local endpoint. |
| 156 | boost::system::error_code bind(implementation_type& impl, |
| 157 | const endpoint_type& endpoint, boost::system::error_code& ec) |
| 158 | { |
| 159 | socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); |
| 160 | |
| 161 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 162 | return ec; |
| 163 | } |
| 164 | |
| 165 | // Set a socket option. |
| 166 | template <typename Option> |
| 167 | boost::system::error_code set_option(implementation_type& impl, |
| 168 | const Option& option, boost::system::error_code& ec) |
| 169 | { |
| 170 | socket_ops::setsockopt(impl.socket_, impl.state_, |
| 171 | option.level(impl.protocol_), option.name(impl.protocol_), |
| 172 | option.data(impl.protocol_), option.size(impl.protocol_), ec); |
| 173 | |
| 174 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 175 | return ec; |
| 176 | } |
| 177 | |
| 178 | // Set a socket option. |
| 179 | template <typename Option> |
| 180 | boost::system::error_code get_option(const implementation_type& impl, |
| 181 | Option& option, boost::system::error_code& ec) const |
| 182 | { |
| 183 | std::size_t size = option.size(impl.protocol_); |
| 184 | socket_ops::getsockopt(impl.socket_, impl.state_, |
| 185 | option.level(impl.protocol_), option.name(impl.protocol_), |
| 186 | option.data(impl.protocol_), &size, ec); |
| 187 | if (!ec) |
| 188 | option.resize(impl.protocol_, size); |
| 189 | |
| 190 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 191 | return ec; |
| 192 | } |
| 193 | |
| 194 | // Get the local endpoint. |
| 195 | endpoint_type local_endpoint(const implementation_type& impl, |
| 196 | boost::system::error_code& ec) const |
| 197 | { |
| 198 | endpoint_type endpoint; |
| 199 | std::size_t addr_len = endpoint.capacity(); |
| 200 | if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) |
| 201 | { |
| 202 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 203 | return endpoint_type(); |
| 204 | } |
| 205 | endpoint.resize(addr_len); |
| 206 | return endpoint; |
| 207 | } |
| 208 | |
| 209 | // Get the remote endpoint. |
| 210 | endpoint_type remote_endpoint(const implementation_type& impl, |
| 211 | boost::system::error_code& ec) const |
| 212 | { |
| 213 | endpoint_type endpoint; |
| 214 | std::size_t addr_len = endpoint.capacity(); |
| 215 | if (socket_ops::getpeername(impl.socket_, |
| 216 | endpoint.data(), &addr_len, false, ec)) |
| 217 | { |
| 218 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 219 | return endpoint_type(); |
| 220 | } |
| 221 | endpoint.resize(addr_len); |
| 222 | return endpoint; |
| 223 | } |
| 224 | |
| 225 | // Disable sends or receives on the socket. |
| 226 | boost::system::error_code shutdown(base_implementation_type& impl, |
| 227 | socket_base::shutdown_type what, boost::system::error_code& ec) |
| 228 | { |
| 229 | socket_ops::shutdown(impl.socket_, what, ec); |
| 230 | |
| 231 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 232 | return ec; |
| 233 | } |
| 234 | |
| 235 | // Send a datagram to the specified endpoint. Returns the number of bytes |
| 236 | // sent. |
| 237 | template <typename ConstBufferSequence> |
| 238 | size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, |
| 239 | const endpoint_type& destination, socket_base::message_flags flags, |
| 240 | boost::system::error_code& ec) |
| 241 | { |
| 242 | typedef buffer_sequence_adapter<boost::asio::const_buffer, |
| 243 | ConstBufferSequence> bufs_type; |
| 244 | |
| 245 | size_t n; |
| 246 | if (bufs_type::is_single_buffer) |
| 247 | { |
| 248 | n = socket_ops::sync_sendto1(impl.socket_, impl.state_, |
| 249 | bufs_type::first(buffers).data(), |
| 250 | bufs_type::first(buffers).size(), flags, |
| 251 | destination.data(), destination.size(), ec); |
| 252 | } |
| 253 | else |
| 254 | { |
| 255 | bufs_type bufs(buffers); |
| 256 | n = socket_ops::sync_sendto(impl.socket_, impl.state_, |
| 257 | bufs.buffers(), bufs.count(), flags, |
| 258 | destination.data(), destination.size(), ec); |
| 259 | } |
| 260 | |
| 261 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 262 | return n; |
| 263 | } |
| 264 | |
| 265 | // Wait until data can be sent without blocking. |
| 266 | size_t send_to(implementation_type& impl, const null_buffers&, |
| 267 | const endpoint_type&, socket_base::message_flags, |
| 268 | boost::system::error_code& ec) |
| 269 | { |
| 270 | // Wait for socket to become ready. |
| 271 | socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); |
| 272 | |
| 273 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 274 | return 0; |
| 275 | } |
| 276 | |
| 277 | // Start an asynchronous send. The data being sent must be valid for the |
| 278 | // lifetime of the asynchronous operation. |
| 279 | template <typename ConstBufferSequence, typename Handler, typename IoExecutor> |
| 280 | void async_send_to(implementation_type& impl, |
| 281 | const ConstBufferSequence& buffers, |
| 282 | const endpoint_type& destination, socket_base::message_flags flags, |
| 283 | Handler& handler, const IoExecutor& io_ex) |
| 284 | { |
| 285 | bool is_continuation = |
| 286 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 287 | |
| 288 | associated_cancellation_slot_t<Handler> slot |
| 289 | = boost::asio::get_associated_cancellation_slot(handler); |
| 290 | |
| 291 | // Allocate and construct an operation to wrap the handler. |
| 292 | typedef reactive_socket_sendto_op<ConstBufferSequence, |
| 293 | endpoint_type, Handler, IoExecutor> op; |
| 294 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 295 | op::ptr::allocate(handler), 0 }; |
| 296 | p.p = new (p.v) op(success_ec_, impl.socket_, |
| 297 | buffers, destination, flags, handler, io_ex); |
| 298 | |
| 299 | // Optionally register for per-operation cancellation. |
| 300 | if (slot.is_connected()) |
| 301 | { |
| 302 | p.p->cancellation_key_ = |
| 303 | &slot.template emplace<reactor_op_cancellation>( |
| 304 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::write_op); |
| 305 | } |
| 306 | |
| 307 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 308 | &impl, impl.socket_, "async_send_to"))(void)0; |
| 309 | |
| 310 | start_op(impl, reactor::write_op, p.p, |
| 311 | is_continuation, true, false, true, &io_ex, 0); |
| 312 | p.v = p.p = 0; |
| 313 | } |
| 314 | |
| 315 | // Start an asynchronous wait until data can be sent without blocking. |
| 316 | template <typename Handler, typename IoExecutor> |
| 317 | void async_send_to(implementation_type& impl, const null_buffers&, |
| 318 | const endpoint_type&, socket_base::message_flags, |
| 319 | Handler& handler, const IoExecutor& io_ex) |
| 320 | { |
| 321 | bool is_continuation = |
| 322 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 323 | |
| 324 | associated_cancellation_slot_t<Handler> slot |
| 325 | = boost::asio::get_associated_cancellation_slot(handler); |
| 326 | |
| 327 | // Allocate and construct an operation to wrap the handler. |
| 328 | typedef reactive_null_buffers_op<Handler, IoExecutor> op; |
| 329 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 330 | op::ptr::allocate(handler), 0 }; |
| 331 | p.p = new (p.v) op(success_ec_, handler, io_ex); |
| 332 | |
| 333 | // Optionally register for per-operation cancellation. |
| 334 | if (slot.is_connected()) |
| 335 | { |
| 336 | p.p->cancellation_key_ = |
| 337 | &slot.template emplace<reactor_op_cancellation>( |
| 338 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::write_op); |
| 339 | } |
| 340 | |
| 341 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 342 | &impl, impl.socket_, "async_send_to(null_buffers)"))(void)0; |
| 343 | |
| 344 | start_op(impl, reactor::write_op, p.p, |
| 345 | is_continuation, false, false, false, &io_ex, 0); |
| 346 | p.v = p.p = 0; |
| 347 | } |
| 348 | |
| 349 | // Receive a datagram with the endpoint of the sender. Returns the number of |
| 350 | // bytes received. |
| 351 | template <typename MutableBufferSequence> |
| 352 | size_t receive_from(implementation_type& impl, |
| 353 | const MutableBufferSequence& buffers, |
| 354 | endpoint_type& sender_endpoint, socket_base::message_flags flags, |
| 355 | boost::system::error_code& ec) |
| 356 | { |
| 357 | typedef buffer_sequence_adapter<boost::asio::mutable_buffer, |
| 358 | MutableBufferSequence> bufs_type; |
| 359 | |
| 360 | std::size_t addr_len = sender_endpoint.capacity(); |
| 361 | std::size_t n; |
| 362 | if (bufs_type::is_single_buffer) |
| 363 | { |
| 364 | n = socket_ops::sync_recvfrom1(impl.socket_, impl.state_, |
| 365 | bufs_type::first(buffers).data(), bufs_type::first(buffers).size(), |
| 366 | flags, sender_endpoint.data(), &addr_len, ec); |
| 367 | } |
| 368 | else |
| 369 | { |
| 370 | bufs_type bufs(buffers); |
| 371 | n = socket_ops::sync_recvfrom(impl.socket_, impl.state_, bufs.buffers(), |
| 372 | bufs.count(), flags, sender_endpoint.data(), &addr_len, ec); |
| 373 | } |
| 374 | |
| 375 | if (!ec) |
| 376 | sender_endpoint.resize(addr_len); |
| 377 | |
| 378 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 379 | return n; |
| 380 | } |
| 381 | |
| 382 | // Wait until data can be received without blocking. |
| 383 | size_t receive_from(implementation_type& impl, const null_buffers&, |
| 384 | endpoint_type& sender_endpoint, socket_base::message_flags, |
| 385 | boost::system::error_code& ec) |
| 386 | { |
| 387 | // Wait for socket to become ready. |
| 388 | socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); |
| 389 | |
| 390 | // Reset endpoint since it can be given no sensible value at this time. |
| 391 | sender_endpoint = endpoint_type(); |
| 392 | |
| 393 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 394 | return 0; |
| 395 | } |
| 396 | |
| 397 | // Start an asynchronous receive. The buffer for the data being received and |
| 398 | // the sender_endpoint object must both be valid for the lifetime of the |
| 399 | // asynchronous operation. |
| 400 | template <typename MutableBufferSequence, |
| 401 | typename Handler, typename IoExecutor> |
| 402 | void async_receive_from(implementation_type& impl, |
| 403 | const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, |
| 404 | socket_base::message_flags flags, Handler& handler, |
| 405 | const IoExecutor& io_ex) |
| 406 | { |
| 407 | bool is_continuation = |
| 408 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 409 | |
| 410 | associated_cancellation_slot_t<Handler> slot |
| 411 | = boost::asio::get_associated_cancellation_slot(handler); |
| 412 | |
| 413 | // Allocate and construct an operation to wrap the handler. |
| 414 | typedef reactive_socket_recvfrom_op<MutableBufferSequence, |
| 415 | endpoint_type, Handler, IoExecutor> op; |
| 416 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 417 | op::ptr::allocate(handler), 0 }; |
| 418 | int protocol = impl.protocol_.type(); |
| 419 | p.p = new (p.v) op(success_ec_, impl.socket_, protocol, |
| 420 | buffers, sender_endpoint, flags, handler, io_ex); |
| 421 | |
| 422 | // Optionally register for per-operation cancellation. |
| 423 | if (slot.is_connected()) |
| 424 | { |
| 425 | p.p->cancellation_key_ = |
| 426 | &slot.template emplace<reactor_op_cancellation>( |
| 427 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 428 | } |
| 429 | |
| 430 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 431 | &impl, impl.socket_, "async_receive_from"))(void)0; |
| 432 | |
| 433 | start_op(impl, |
| 434 | (flags & socket_base::message_out_of_band) |
| 435 | ? reactor::except_op : reactor::read_op, |
| 436 | p.p, is_continuation, true, false, true, &io_ex, 0); |
| 437 | p.v = p.p = 0; |
| 438 | } |
| 439 | |
| 440 | // Wait until data can be received without blocking. |
| 441 | template <typename Handler, typename IoExecutor> |
| 442 | void async_receive_from(implementation_type& impl, const null_buffers&, |
| 443 | endpoint_type& sender_endpoint, socket_base::message_flags flags, |
| 444 | Handler& handler, const IoExecutor& io_ex) |
| 445 | { |
| 446 | bool is_continuation = |
| 447 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 448 | |
| 449 | associated_cancellation_slot_t<Handler> slot |
| 450 | = boost::asio::get_associated_cancellation_slot(handler); |
| 451 | |
| 452 | // Allocate and construct an operation to wrap the handler. |
| 453 | typedef reactive_null_buffers_op<Handler, IoExecutor> op; |
| 454 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 455 | op::ptr::allocate(handler), 0 }; |
| 456 | p.p = new (p.v) op(success_ec_, handler, io_ex); |
| 457 | |
| 458 | // Optionally register for per-operation cancellation. |
| 459 | if (slot.is_connected()) |
| 460 | { |
| 461 | p.p->cancellation_key_ = |
| 462 | &slot.template emplace<reactor_op_cancellation>( |
| 463 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 464 | } |
| 465 | |
| 466 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 467 | &impl, impl.socket_, "async_receive_from(null_buffers)"))(void)0; |
| 468 | |
| 469 | // Reset endpoint since it can be given no sensible value at this time. |
| 470 | sender_endpoint = endpoint_type(); |
| 471 | |
| 472 | start_op(impl, |
| 473 | (flags & socket_base::message_out_of_band) |
| 474 | ? reactor::except_op : reactor::read_op, |
| 475 | p.p, is_continuation, false, false, false, &io_ex, 0); |
| 476 | p.v = p.p = 0; |
| 477 | } |
| 478 | |
| 479 | // Accept a new connection. |
| 480 | template <typename Socket> |
| 481 | boost::system::error_code accept(implementation_type& impl, |
| 482 | Socket& peer, endpoint_type* peer_endpoint, boost::system::error_code& ec) |
| 483 | { |
| 484 | // We cannot accept a socket that is already open. |
| 485 | if (peer.is_open()) |
| 486 | { |
| 487 | ec = boost::asio::error::already_open; |
| 488 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 489 | return ec; |
| 490 | } |
| 491 | |
| 492 | std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; |
| 493 | socket_holder new_socket(socket_ops::sync_accept(impl.socket_, |
| 494 | impl.state_, peer_endpoint ? peer_endpoint->data() : 0, |
| 495 | peer_endpoint ? &addr_len : 0, ec)); |
| 496 | |
| 497 | // On success, assign new connection to peer socket object. |
| 498 | if (new_socket.get() != invalid_socket) |
| 499 | { |
| 500 | if (peer_endpoint) |
| 501 | peer_endpoint->resize(addr_len); |
| 502 | peer.assign(impl.protocol_, new_socket.get(), ec); |
| 503 | if (!ec) |
| 504 | new_socket.release(); |
| 505 | } |
| 506 | |
| 507 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 508 | return ec; |
| 509 | } |
| 510 | |
| 511 | // Start an asynchronous accept. The peer and peer_endpoint objects must be |
| 512 | // valid until the accept's handler is invoked. |
| 513 | template <typename Socket, typename Handler, typename IoExecutor> |
| 514 | void async_accept(implementation_type& impl, Socket& peer, |
| 515 | endpoint_type* peer_endpoint, Handler& handler, const IoExecutor& io_ex) |
| 516 | { |
| 517 | bool is_continuation = |
| 518 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 519 | |
| 520 | associated_cancellation_slot_t<Handler> slot |
| 521 | = boost::asio::get_associated_cancellation_slot(handler); |
| 522 | |
| 523 | // Allocate and construct an operation to wrap the handler. |
| 524 | typedef reactive_socket_accept_op<Socket, Protocol, Handler, IoExecutor> op; |
| 525 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 526 | op::ptr::allocate(handler), 0 }; |
| 527 | p.p = new (p.v) op(success_ec_, impl.socket_, impl.state_, |
| 528 | peer, impl.protocol_, peer_endpoint, handler, io_ex); |
| 529 | |
| 530 | // Optionally register for per-operation cancellation. |
| 531 | if (slot.is_connected() && !peer.is_open()) |
| 532 | { |
| 533 | p.p->cancellation_key_ = |
| 534 | &slot.template emplace<reactor_op_cancellation>( |
| 535 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 536 | } |
| 537 | |
| 538 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 539 | &impl, impl.socket_, "async_accept"))(void)0; |
| 540 | |
| 541 | start_accept_op(impl, p.p, is_continuation, peer.is_open(), &io_ex, 0); |
| 542 | p.v = p.p = 0; |
| 543 | } |
| 544 | |
| 545 | // Start an asynchronous accept. The peer_endpoint object must be valid until |
| 546 | // the accept's handler is invoked. |
| 547 | template <typename PeerIoExecutor, typename Handler, typename IoExecutor> |
| 548 | void async_move_accept(implementation_type& impl, |
| 549 | const PeerIoExecutor& peer_io_ex, endpoint_type* peer_endpoint, |
| 550 | Handler& handler, const IoExecutor& io_ex) |
| 551 | { |
| 552 | bool is_continuation = |
| 553 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 554 | |
| 555 | associated_cancellation_slot_t<Handler> slot |
| 556 | = boost::asio::get_associated_cancellation_slot(handler); |
| 557 | |
| 558 | // Allocate and construct an operation to wrap the handler. |
| 559 | typedef reactive_socket_move_accept_op<Protocol, |
| 560 | PeerIoExecutor, Handler, IoExecutor> op; |
| 561 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 562 | op::ptr::allocate(handler), 0 }; |
| 563 | p.p = new (p.v) op(success_ec_, peer_io_ex, impl.socket_, |
| 564 | impl.state_, impl.protocol_, peer_endpoint, handler, io_ex); |
| 565 | |
| 566 | // Optionally register for per-operation cancellation. |
| 567 | if (slot.is_connected()) |
| 568 | { |
| 569 | p.p->cancellation_key_ = |
| 570 | &slot.template emplace<reactor_op_cancellation>( |
| 571 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 572 | } |
| 573 | |
| 574 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 575 | &impl, impl.socket_, "async_accept"))(void)0; |
| 576 | |
| 577 | start_accept_op(impl, p.p, is_continuation, false, &io_ex, 0); |
| 578 | p.v = p.p = 0; |
| 579 | } |
| 580 | |
| 581 | // Connect the socket to the specified endpoint. |
| 582 | boost::system::error_code connect(implementation_type& impl, |
| 583 | const endpoint_type& peer_endpoint, boost::system::error_code& ec) |
| 584 | { |
| 585 | socket_ops::sync_connect(impl.socket_, |
| 586 | peer_endpoint.data(), peer_endpoint.size(), ec); |
| 587 | BOOST_ASIO_ERROR_LOCATION(ec)do { static constexpr boost::source_location loc = ::boost::source_location (__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN ()); (ec).assign((ec), &loc); } while (false); |
| 588 | return ec; |
| 589 | } |
| 590 | |
| 591 | // Start an asynchronous connect. |
| 592 | template <typename Handler, typename IoExecutor> |
| 593 | void async_connect(implementation_type& impl, |
| 594 | const endpoint_type& peer_endpoint, |
| 595 | Handler& handler, const IoExecutor& io_ex) |
| 596 | { |
| 597 | bool is_continuation = |
| 598 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 599 | |
| 600 | associated_cancellation_slot_t<Handler> slot |
| 601 | = boost::asio::get_associated_cancellation_slot(handler); |
| 602 | |
| 603 | // Allocate and construct an operation to wrap the handler. |
| 604 | typedef reactive_socket_connect_op<Handler, IoExecutor> op; |
| 605 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 606 | op::ptr::allocate(handler), 0 }; |
| 607 | p.p = new (p.v) op(success_ec_, impl.socket_, handler, io_ex); |
| 608 | |
| 609 | // Optionally register for per-operation cancellation. |
| 610 | if (slot.is_connected()) |
| 611 | { |
| 612 | p.p->cancellation_key_ = |
| 613 | &slot.template emplace<reactor_op_cancellation>( |
| 614 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::connect_op); |
| 615 | } |
| 616 | |
| 617 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 618 | &impl, impl.socket_, "async_connect"))(void)0; |
| 619 | |
| 620 | start_connect_op(impl, p.p, is_continuation, |
| 621 | peer_endpoint.data(), peer_endpoint.size(), &io_ex, 0); |
| 622 | p.v = p.p = 0; |
| 623 | } |
| 624 | }; |
| 625 | |
| 626 | } // namespace detail |
| 627 | } // namespace asio |
| 628 | } // namespace boost |
| 629 | |
| 630 | #include <boost/asio/detail/pop_options.hpp> |
| 631 | |
| 632 | #endif // !defined(BOOST_ASIO_HAS_IOCP) |
| 633 | // && !defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 634 | |
| 635 | #endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP |
| 1 | // |
| 2 | // detail/reactive_socket_service_base.ipp |
| 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 | |
| 11 | #ifndef BOOST_ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP |
| 12 | #define BOOST_ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP |
| 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_HAS_IOCP) \ |
| 21 | && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \ |
| 22 | && !defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 23 | |
| 24 | #include <boost/asio/config.hpp> |
| 25 | #include <boost/asio/detail/reactive_socket_service_base.hpp> |
| 26 | |
| 27 | #include <boost/asio/detail/push_options.hpp> |
| 28 | |
| 29 | namespace boost { |
| 30 | namespace asio { |
| 31 | namespace detail { |
| 32 | |
| 33 | reactive_socket_service_base::reactive_socket_service_base( |
| 34 | execution_context& context) |
| 35 | : reactor_(use_service<reactor>(context)), |
| 36 | extra_state_( |
| 37 | boost::asio::config(context).get( |
| 38 | "reactor", "reset_edge_on_partial_read", 0) |
| 39 | ? socket_ops::reset_edge_on_partial_read : 0) |
| 40 | { |
| 41 | reactor_.init_task(); |
| 42 | } |
| 43 | |
| 44 | void reactive_socket_service_base::base_shutdown() |
| 45 | { |
| 46 | } |
| 47 | |
| 48 | void reactive_socket_service_base::construct( |
| 49 | reactive_socket_service_base::base_implementation_type& impl) |
| 50 | { |
| 51 | impl.socket_ = invalid_socket; |
| 52 | impl.state_ = 0; |
| 53 | impl.reactor_data_ = reactor::per_descriptor_data(); |
| 54 | } |
| 55 | |
| 56 | void reactive_socket_service_base::base_move_construct( |
| 57 | reactive_socket_service_base::base_implementation_type& impl, |
| 58 | reactive_socket_service_base::base_implementation_type& other_impl) |
| 59 | noexcept |
| 60 | { |
| 61 | impl.socket_ = other_impl.socket_; |
| 62 | other_impl.socket_ = invalid_socket; |
| 63 | |
| 64 | impl.state_ = other_impl.state_; |
| 65 | other_impl.state_ = 0; |
| 66 | |
| 67 | reactor_.move_descriptor(impl.socket_, |
| 68 | impl.reactor_data_, other_impl.reactor_data_); |
| 69 | } |
| 70 | |
| 71 | void reactive_socket_service_base::base_move_assign( |
| 72 | reactive_socket_service_base::base_implementation_type& impl, |
| 73 | reactive_socket_service_base& other_service, |
| 74 | reactive_socket_service_base::base_implementation_type& other_impl) |
| 75 | { |
| 76 | destroy(impl); |
| 77 | |
| 78 | impl.socket_ = other_impl.socket_; |
| 79 | other_impl.socket_ = invalid_socket; |
| 80 | |
| 81 | impl.state_ = other_impl.state_; |
| 82 | other_impl.state_ = 0; |
| 83 | |
| 84 | other_service.reactor_.move_descriptor(impl.socket_, |
| 85 | impl.reactor_data_, other_impl.reactor_data_); |
| 86 | } |
| 87 | |
| 88 | void reactive_socket_service_base::destroy( |
| 89 | reactive_socket_service_base::base_implementation_type& impl) |
| 90 | { |
| 91 | if (impl.socket_ != invalid_socket) |
| 92 | { |
| 93 | BOOST_ASIO_HANDLER_OPERATION((reactor_.context(),(void)0 |
| 94 | "socket", &impl, impl.socket_, "close"))(void)0; |
| 95 | |
| 96 | reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, |
| 97 | (impl.state_ & socket_ops::possible_dup) == 0); |
| 98 | |
| 99 | boost::system::error_code ignored_ec; |
| 100 | socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); |
| 101 | |
| 102 | reactor_.cleanup_descriptor_data(impl.reactor_data_); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | boost::system::error_code reactive_socket_service_base::close( |
| 107 | reactive_socket_service_base::base_implementation_type& impl, |
| 108 | boost::system::error_code& ec) |
| 109 | { |
| 110 | if (is_open(impl)) |
| 111 | { |
| 112 | BOOST_ASIO_HANDLER_OPERATION((reactor_.context(),(void)0 |
| 113 | "socket", &impl, impl.socket_, "close"))(void)0; |
| 114 | |
| 115 | reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, |
| 116 | (impl.state_ & socket_ops::possible_dup) == 0); |
| 117 | |
| 118 | socket_ops::close(impl.socket_, impl.state_, false, ec); |
| 119 | |
| 120 | reactor_.cleanup_descriptor_data(impl.reactor_data_); |
| 121 | } |
| 122 | else |
| 123 | { |
| 124 | ec = boost::system::error_code(); |
| 125 | } |
| 126 | |
| 127 | // The descriptor is closed by the OS even if close() returns an error. |
| 128 | // |
| 129 | // (Actually, POSIX says the state of the descriptor is unspecified. On |
| 130 | // Linux the descriptor is apparently closed anyway; e.g. see |
| 131 | // http://lkml.org/lkml/2005/9/10/129 |
| 132 | // We'll just have to assume that other OSes follow the same behaviour. The |
| 133 | // known exception is when Windows's closesocket() function fails with |
| 134 | // WSAEWOULDBLOCK, but this case is handled inside socket_ops::close(). |
| 135 | construct(impl); |
| 136 | |
| 137 | return ec; |
| 138 | } |
| 139 | |
| 140 | socket_type reactive_socket_service_base::release( |
| 141 | reactive_socket_service_base::base_implementation_type& impl, |
| 142 | boost::system::error_code& ec) |
| 143 | { |
| 144 | if (!is_open(impl)) |
| 145 | { |
| 146 | ec = boost::asio::error::bad_descriptor; |
| 147 | return invalid_socket; |
| 148 | } |
| 149 | |
| 150 | BOOST_ASIO_HANDLER_OPERATION((reactor_.context(),(void)0 |
| 151 | "socket", &impl, impl.socket_, "release"))(void)0; |
| 152 | |
| 153 | reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, false); |
| 154 | reactor_.cleanup_descriptor_data(impl.reactor_data_); |
| 155 | socket_type sock = impl.socket_; |
| 156 | construct(impl); |
| 157 | ec = boost::system::error_code(); |
| 158 | return sock; |
| 159 | } |
| 160 | |
| 161 | boost::system::error_code reactive_socket_service_base::cancel( |
| 162 | reactive_socket_service_base::base_implementation_type& impl, |
| 163 | boost::system::error_code& ec) |
| 164 | { |
| 165 | if (!is_open(impl)) |
| 166 | { |
| 167 | ec = boost::asio::error::bad_descriptor; |
| 168 | return ec; |
| 169 | } |
| 170 | |
| 171 | BOOST_ASIO_HANDLER_OPERATION((reactor_.context(),(void)0 |
| 172 | "socket", &impl, impl.socket_, "cancel"))(void)0; |
| 173 | |
| 174 | reactor_.cancel_ops(impl.socket_, impl.reactor_data_); |
| 175 | ec = boost::system::error_code(); |
| 176 | return ec; |
| 177 | } |
| 178 | |
| 179 | boost::system::error_code reactive_socket_service_base::do_open( |
| 180 | reactive_socket_service_base::base_implementation_type& impl, |
| 181 | int af, int type, int protocol, boost::system::error_code& ec) |
| 182 | { |
| 183 | if (is_open(impl)) |
| 184 | { |
| 185 | ec = boost::asio::error::already_open; |
| 186 | return ec; |
| 187 | } |
| 188 | |
| 189 | socket_holder sock(socket_ops::socket(af, type, protocol, ec)); |
| 190 | if (sock.get() == invalid_socket) |
| 191 | return ec; |
| 192 | |
| 193 | if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_)) |
| 194 | { |
| 195 | ec = boost::system::error_code(err, |
| 196 | boost::asio::error::get_system_category()); |
| 197 | return ec; |
| 198 | } |
| 199 | |
| 200 | impl.socket_ = sock.release(); |
| 201 | switch (type) |
| 202 | { |
| 203 | case SOCK_STREAMSOCK_STREAM: |
| 204 | impl.state_ = socket_ops::stream_oriented | extra_state_; |
| 205 | break; |
| 206 | case SOCK_DGRAMSOCK_DGRAM: |
| 207 | impl.state_ = socket_ops::datagram_oriented | extra_state_; |
| 208 | break; |
| 209 | default: |
| 210 | impl.state_ = 0; |
| 211 | break; |
| 212 | } |
| 213 | ec = boost::system::error_code(); |
| 214 | return ec; |
| 215 | } |
| 216 | |
| 217 | boost::system::error_code reactive_socket_service_base::do_assign( |
| 218 | reactive_socket_service_base::base_implementation_type& impl, int type, |
| 219 | const reactive_socket_service_base::native_handle_type& native_socket, |
| 220 | boost::system::error_code& ec) |
| 221 | { |
| 222 | if (is_open(impl)) |
| 223 | { |
| 224 | ec = boost::asio::error::already_open; |
| 225 | return ec; |
| 226 | } |
| 227 | |
| 228 | if (int err = reactor_.register_descriptor( |
| 229 | native_socket, impl.reactor_data_)) |
| 230 | { |
| 231 | ec = boost::system::error_code(err, |
| 232 | boost::asio::error::get_system_category()); |
| 233 | return ec; |
| 234 | } |
| 235 | |
| 236 | impl.socket_ = native_socket; |
| 237 | switch (type) |
| 238 | { |
| 239 | case SOCK_STREAMSOCK_STREAM: |
| 240 | impl.state_ = socket_ops::stream_oriented | extra_state_; |
| 241 | break; |
| 242 | case SOCK_DGRAMSOCK_DGRAM: |
| 243 | impl.state_ = socket_ops::datagram_oriented | extra_state_; |
| 244 | break; |
| 245 | default: |
| 246 | impl.state_ = 0; |
| 247 | break; |
| 248 | } |
| 249 | impl.state_ |= socket_ops::possible_dup; |
| 250 | ec = boost::system::error_code(); |
| 251 | return ec; |
| 252 | } |
| 253 | |
| 254 | void reactive_socket_service_base::do_start_op( |
| 255 | reactive_socket_service_base::base_implementation_type& impl, |
| 256 | int op_type, reactor_op* op, bool is_continuation, |
| 257 | bool allow_speculative, bool noop, bool needs_non_blocking, |
| 258 | void (*on_immediate)(operation* op, bool, const void*), |
| 259 | const void* immediate_arg) |
| 260 | { |
| 261 | if (!noop) |
| 262 | { |
| 263 | if ((impl.state_ & socket_ops::non_blocking) |
| 264 | || !needs_non_blocking |
| 265 | || socket_ops::set_internal_non_blocking( |
| 266 | impl.socket_, impl.state_, true, op->ec_)) |
| 267 | { |
| 268 | reactor_.start_op(op_type, impl.socket_, impl.reactor_data_, op, |
| 269 | is_continuation, allow_speculative, on_immediate, immediate_arg); |
| 270 | return; |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | on_immediate(op, is_continuation, immediate_arg); |
| 275 | } |
| 276 | |
| 277 | void reactive_socket_service_base::do_start_accept_op( |
| 278 | reactive_socket_service_base::base_implementation_type& impl, |
| 279 | reactor_op* op, bool is_continuation, bool peer_is_open, |
| 280 | void (*on_immediate)(operation* op, bool, const void*), |
| 281 | const void* immediate_arg) |
| 282 | { |
| 283 | if (!peer_is_open) |
| 284 | { |
| 285 | do_start_op(impl, reactor::read_op, op, is_continuation, |
| 286 | true, false, true, on_immediate, immediate_arg); |
| 287 | } |
| 288 | else |
| 289 | { |
| 290 | op->ec_ = boost::asio::error::already_open; |
| 291 | on_immediate(op, is_continuation, immediate_arg); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | void reactive_socket_service_base::do_start_connect_op( |
| 296 | reactive_socket_service_base::base_implementation_type& impl, |
| 297 | reactor_op* op, bool is_continuation, const void* addr, size_t addrlen, |
| 298 | void (*on_immediate)(operation* op, bool, const void*), |
| 299 | const void* immediate_arg) |
| 300 | { |
| 301 | if ((impl.state_ & socket_ops::non_blocking) |
| 302 | || socket_ops::set_internal_non_blocking( |
| 303 | impl.socket_, impl.state_, true, op->ec_)) |
| 304 | { |
| 305 | if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) |
| 306 | { |
| 307 | if (op->ec_ == boost::asio::error::in_progress |
| 308 | || op->ec_ == boost::asio::error::would_block) |
| 309 | { |
| 310 | op->ec_ = boost::system::error_code(); |
| 311 | reactor_.start_op(reactor::connect_op, impl.socket_, impl.reactor_data_, |
| 312 | op, is_continuation, false, on_immediate, immediate_arg); |
| 313 | return; |
| 314 | } |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | on_immediate(op, is_continuation, immediate_arg); |
| 319 | } |
| 320 | |
| 321 | } // namespace detail |
| 322 | } // namespace asio |
| 323 | } // namespace boost |
| 324 | |
| 325 | #include <boost/asio/detail/pop_options.hpp> |
| 326 | |
| 327 | #endif // !defined(BOOST_ASIO_HAS_IOCP) |
| 328 | // && !defined(BOOST_ASIO_WINDOWS_RUNTIME) |
| 329 | // && !defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 330 | |
| 331 | #endif // BOOST_ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP |
| 1 | // |
| 2 | // impl/execution_context.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 | |
| 11 | #ifndef BOOST_ASIO_IMPL_EXECUTION_CONTEXT_HPP |
| 12 | #define BOOST_ASIO_IMPL_EXECUTION_CONTEXT_HPP |
| 13 | |
| 14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) |
| 15 | # pragma once |
| 16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
| 17 | |
| 18 | #include <cstring> |
| 19 | #include <boost/asio/detail/handler_type_requirements.hpp> |
| 20 | #include <boost/asio/detail/memory.hpp> |
| 21 | #include <boost/asio/detail/service_registry.hpp> |
| 22 | #include <boost/asio/detail/throw_exception.hpp> |
| 23 | |
| 24 | #include <boost/asio/detail/push_options.hpp> |
| 25 | |
| 26 | namespace boost { |
| 27 | namespace asio { |
| 28 | |
| 29 | template <typename Allocator> |
| 30 | execution_context::execution_context(allocator_arg_t, const Allocator& a) |
| 31 | : execution_context(detail::allocate_object<allocator_impl<Allocator>>(a, a)) |
| 32 | { |
| 33 | } |
| 34 | |
| 35 | template <typename Allocator> |
| 36 | execution_context::execution_context(allocator_arg_t, const Allocator& a, |
| 37 | const service_maker& initial_services) |
| 38 | : execution_context(detail::allocate_object<allocator_impl<Allocator>>(a, a), |
| 39 | initial_services) |
| 40 | { |
| 41 | } |
| 42 | |
| 43 | inline execution_context::auto_allocator_ptr::~auto_allocator_ptr() |
| 44 | { |
| 45 | ptr_->destroy(); |
| 46 | } |
| 47 | |
| 48 | template <typename Allocator> |
| 49 | void execution_context::allocator_impl<Allocator>::destroy() |
| 50 | { |
| 51 | detail::deallocate_object(allocator_, this); |
| 52 | } |
| 53 | |
| 54 | template <typename Allocator> |
| 55 | void* execution_context::allocator_impl<Allocator>::allocate( |
| 56 | std::size_t size, std::size_t align) |
| 57 | { |
| 58 | typename std::allocator_traits<Allocator>::template |
| 59 | rebind_alloc<unsigned char> alloc(allocator_); |
| 60 | |
| 61 | std::size_t space = size + align - 1; |
| 62 | unsigned char* base = std::allocator_traits<decltype(alloc)>::allocate( |
| 63 | alloc, space + sizeof(std::ptrdiff_t)); |
| 64 | |
| 65 | void* p = base; |
| 66 | if (detail::align(align, size, p, space)) |
| 67 | { |
| 68 | std::ptrdiff_t off = static_cast<unsigned char*>(p) - base; |
| 69 | std::memcpy(static_cast<unsigned char*>(p) + size, &off, sizeof(off)); |
| 70 | return p; |
| 71 | } |
| 72 | |
| 73 | std::bad_alloc ex; |
| 74 | boost::asio::detail::throw_exception(ex); |
| 75 | return 0; |
| 76 | } |
| 77 | |
| 78 | template <typename Allocator> |
| 79 | void execution_context::allocator_impl<Allocator>::deallocate( |
| 80 | void* ptr, std::size_t size, std::size_t align) |
| 81 | { |
| 82 | if (ptr) |
| 83 | { |
| 84 | typename std::allocator_traits<Allocator>::template |
| 85 | rebind_alloc<unsigned char> alloc(allocator_); |
| 86 | |
| 87 | std::ptrdiff_t off; |
| 88 | std::memcpy(&off, static_cast<unsigned char*>(ptr) + size, sizeof(off)); |
| 89 | unsigned char* base = static_cast<unsigned char*>(ptr) - off; |
| 90 | |
| 91 | std::allocator_traits<decltype(alloc)>::deallocate( |
| 92 | alloc, base, size + align - 1 + sizeof(std::ptrdiff_t)); |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | #if !defined(GENERATING_DOCUMENTATION) |
| 97 | |
| 98 | template <typename Service> |
| 99 | inline Service& use_service(execution_context& e) |
| 100 | { |
| 101 | // Check that Service meets the necessary type requirements. |
| 102 | (void)static_cast<execution_context::service*>(static_cast<Service*>(0)); |
| 103 | |
| 104 | return e.service_registry_->template use_service<Service>(); |
| 105 | } |
| 106 | |
| 107 | template <typename Service, typename... Args> |
| 108 | Service& make_service(execution_context& e, Args&&... args) |
| 109 | { |
| 110 | // Check that Service meets the necessary type requirements. |
| 111 | (void)static_cast<execution_context::service*>(static_cast<Service*>(0)); |
| 112 | |
| 113 | return e.service_registry_->template make_service<Service>( |
| 114 | static_cast<Args&&>(args)...); |
| 115 | } |
| 116 | |
| 117 | template <typename Service> |
| 118 | BOOST_ASIO_DEPRECATED_MSG("Use make_service()")[[deprecated("Use make_service()")]] |
| 119 | inline void add_service(execution_context& e, Service* svc) |
| 120 | { |
| 121 | // Check that Service meets the necessary type requirements. |
| 122 | (void)static_cast<execution_context::service*>(static_cast<Service*>(0)); |
| 123 | |
| 124 | e.service_registry_->template add_service<Service>(svc); |
| 125 | } |
| 126 | |
| 127 | template <typename Service> |
| 128 | inline bool has_service(execution_context& e) |
| 129 | { |
| 130 | // Check that Service meets the necessary type requirements. |
| 131 | (void)static_cast<execution_context::service*>(static_cast<Service*>(0)); |
| 132 | |
| 133 | return e.service_registry_->template has_service<Service>(); |
| 134 | } |
| 135 | |
| 136 | #endif // !defined(GENERATING_DOCUMENTATION) |
| 137 | |
| 138 | inline execution_context& execution_context::service::context() |
| 139 | { |
| 140 | return owner_; |
| 141 | } |
| 142 | |
| 143 | } // namespace asio |
| 144 | } // namespace boost |
| 145 | |
| 146 | #include <boost/asio/detail/pop_options.hpp> |
| 147 | |
| 148 | #endif // BOOST_ASIO_IMPL_EXECUTION_CONTEXT_HPP |
| 1 | // |
| 2 | // detail/scoped_lock.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 | |
| 11 | #ifndef BOOST_ASIO_DETAIL_SCOPED_LOCK_HPP |
| 12 | #define BOOST_ASIO_DETAIL_SCOPED_LOCK_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/noncopyable.hpp> |
| 19 | |
| 20 | #include <boost/asio/detail/push_options.hpp> |
| 21 | |
| 22 | namespace boost { |
| 23 | namespace asio { |
| 24 | namespace detail { |
| 25 | |
| 26 | // Helper class to lock and unlock a mutex automatically. |
| 27 | template <typename Mutex> |
| 28 | class scoped_lock |
| 29 | : private noncopyable |
| 30 | { |
| 31 | public: |
| 32 | // Tag type used to distinguish constructors. |
| 33 | enum adopt_lock_t { adopt_lock }; |
| 34 | |
| 35 | // Constructor adopts a lock that is already held. |
| 36 | scoped_lock(Mutex& m, adopt_lock_t) |
| 37 | : mutex_(m), |
| 38 | locked_(true) |
| 39 | { |
| 40 | } |
| 41 | |
| 42 | // Constructor acquires the lock. |
| 43 | explicit scoped_lock(Mutex& m) |
| 44 | : mutex_(m) |
| 45 | { |
| 46 | mutex_.lock(); |
| 47 | locked_ = true; |
| 48 | } |
| 49 | |
| 50 | // Destructor releases the lock. |
| 51 | ~scoped_lock() |
| 52 | { |
| 53 | if (locked_) |
| 54 | mutex_.unlock(); |
| 55 | } |
| 56 | |
| 57 | // Explicitly acquire the lock. |
| 58 | void lock() |
| 59 | { |
| 60 | if (!locked_) |
| 61 | { |
| 62 | mutex_.lock(); |
| 63 | locked_ = true; |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | // Explicitly release the lock. |
| 68 | void unlock() |
| 69 | { |
| 70 | if (locked_) |
| 71 | { |
| 72 | mutex_.unlock(); |
| 73 | locked_ = false; |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | // Test whether the lock is held. |
| 78 | bool locked() const |
| 79 | { |
| 80 | return locked_; |
| 81 | } |
| 82 | |
| 83 | // Get the underlying mutex. |
| 84 | Mutex& mutex() |
| 85 | { |
| 86 | return mutex_; |
| 87 | } |
| 88 | |
| 89 | private: |
| 90 | // The underlying mutex. |
| 91 | Mutex& mutex_; |
| 92 | |
| 93 | // Whether the mutex is currently locked or unlocked. |
| 94 | bool locked_; |
| 95 | }; |
| 96 | |
| 97 | } // namespace detail |
| 98 | } // namespace asio |
| 99 | } // namespace boost |
| 100 | |
| 101 | #include <boost/asio/detail/pop_options.hpp> |
| 102 | |
| 103 | #endif // BOOST_ASIO_DETAIL_SCOPED_LOCK_HPP |
| 1 | // |
| 2 | // detail/posix_mutex.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 | |
| 11 | #ifndef BOOST_ASIO_DETAIL_POSIX_MUTEX_HPP |
| 12 | #define BOOST_ASIO_DETAIL_POSIX_MUTEX_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_HAS_PTHREADS1) |
| 21 | |
| 22 | #include <pthread.h> |
| 23 | #include <boost/asio/detail/noncopyable.hpp> |
| 24 | #include <boost/asio/detail/scoped_lock.hpp> |
| 25 | |
| 26 | #include <boost/asio/detail/push_options.hpp> |
| 27 | |
| 28 | namespace boost { |
| 29 | namespace asio { |
| 30 | namespace detail { |
| 31 | |
| 32 | class posix_event; |
| 33 | |
| 34 | class posix_mutex |
| 35 | : private noncopyable |
| 36 | { |
| 37 | public: |
| 38 | typedef boost::asio::detail::scoped_lock<posix_mutex> scoped_lock; |
| 39 | |
| 40 | // Constructor. |
| 41 | BOOST_ASIO_DECLinline posix_mutex(); |
| 42 | |
| 43 | // Destructor. |
| 44 | ~posix_mutex() |
| 45 | { |
| 46 | ::pthread_mutex_destroy(&mutex_); // Ignore EBUSY. |
| 47 | } |
| 48 | |
| 49 | // Try to lock the mutex. |
| 50 | bool try_lock() |
| 51 | { |
| 52 | return ::pthread_mutex_trylock(&mutex_) == 0; // Ignore EINVAL. |
| 53 | } |
| 54 | |
| 55 | // Lock the mutex. |
| 56 | void lock() |
| 57 | { |
| 58 | (void)::pthread_mutex_lock(&mutex_); // Ignore EINVAL. |
| 59 | } |
| 60 | |
| 61 | // Unlock the mutex. |
| 62 | void unlock() |
| 63 | { |
| 64 | (void)::pthread_mutex_unlock(&mutex_); // Ignore EINVAL. |
| 65 | } |
| 66 | |
| 67 | private: |
| 68 | friend class posix_event; |
| 69 | ::pthread_mutex_t mutex_; |
| 70 | }; |
| 71 | |
| 72 | } // namespace detail |
| 73 | } // namespace asio |
| 74 | } // namespace boost |
| 75 | |
| 76 | #include <boost/asio/detail/pop_options.hpp> |
| 77 | |
| 78 | #if defined(BOOST_ASIO_HEADER_ONLY1) |
| 79 | # include <boost/asio/detail/impl/posix_mutex.ipp> |
| 80 | #endif // defined(BOOST_ASIO_HEADER_ONLY) |
| 81 | |
| 82 | #endif // defined(BOOST_ASIO_HAS_PTHREADS) |
| 83 | |
| 84 | #endif // BOOST_ASIO_DETAIL_POSIX_MUTEX_HPP |
| 1 | // |
| 2 | // config.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 | |
| 11 | #ifndef BOOST_ASIO_CONFIG_HPP |
| 12 | #define BOOST_ASIO_CONFIG_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 | #include <boost/asio/detail/throw_exception.hpp> |
| 20 | #include <boost/asio/detail/type_traits.hpp> |
| 21 | #include <boost/asio/execution_context.hpp> |
| 22 | #include <cstddef> |
| 23 | #include <string> |
| 24 | |
| 25 | #include <boost/asio/detail/push_options.hpp> |
| 26 | |
| 27 | namespace boost { |
| 28 | namespace asio { |
| 29 | |
| 30 | /// Base class for configuration implementations. |
| 31 | class config_service : |
| 32 | #if defined(GENERATING_DOCUMENTATION) |
| 33 | public execution_context::service |
| 34 | #else // defined(GENERATING_DOCUMENTATION) |
| 35 | public detail::execution_context_service_base<config_service> |
| 36 | #endif // defined(GENERATING_DOCUMENTATION) |
| 37 | { |
| 38 | public: |
| 39 | #if defined(GENERATING_DOCUMENTATION) |
| 40 | typedef config_service key_type; |
| 41 | #endif // defined(GENERATING_DOCUMENTATION) |
| 42 | |
| 43 | /// Constructor. |
| 44 | BOOST_ASIO_DECLinline explicit config_service(execution_context& ctx); |
| 45 | |
| 46 | /// Shutdown the service. |
| 47 | BOOST_ASIO_DECLinline void shutdown() override; |
| 48 | |
| 49 | /// Retrieve a configuration value. |
| 50 | BOOST_ASIO_DECLinline virtual const char* get_value(const char* section, |
| 51 | const char* key_name, char* value, std::size_t value_len) const; |
| 52 | }; |
| 53 | |
| 54 | /// Provides access to the configuration values associated with an execution |
| 55 | /// context. |
| 56 | class config |
| 57 | { |
| 58 | public: |
| 59 | /// Constructor. |
| 60 | /** |
| 61 | * This constructor initialises a @c config object to retrieve configuration |
| 62 | * values associated with the specified execution context. |
| 63 | */ |
| 64 | explicit config(execution_context& context) |
| 65 | : service_(use_service<config_service>(context)) |
| 66 | { |
| 67 | } |
| 68 | |
| 69 | /// Copy constructor. |
| 70 | config(const config& other) noexcept |
| 71 | : service_(other.service_) |
| 72 | { |
| 73 | } |
| 74 | |
| 75 | /// Retrieve an integral configuration value. |
| 76 | template <typename T> |
| 77 | constraint_t<is_integral<T>::value, T> |
| 78 | get(const char* section, const char* key_name, T default_value) const; |
| 79 | |
| 80 | private: |
| 81 | config_service& service_; |
| 82 | }; |
| 83 | |
| 84 | /// Configures an execution context based on a concurrency hint. |
| 85 | /** |
| 86 | * This configuration service is provided for backwards compatibility with |
| 87 | * the existing concurrency hint mechanism. |
| 88 | * |
| 89 | * @par Example |
| 90 | * @code boost::asio::io_context my_io_context{ |
| 91 | * boost::asio::config_from_concurrency_hint{1}}; @endcode |
| 92 | */ |
| 93 | class config_from_concurrency_hint : public execution_context::service_maker |
| 94 | { |
| 95 | public: |
| 96 | /// Construct with a default concurrency hint. |
| 97 | BOOST_ASIO_DECLinline config_from_concurrency_hint(); |
| 98 | |
| 99 | /// Construct with a specified concurrency hint. |
| 100 | explicit config_from_concurrency_hint(int concurrency_hint) |
| 101 | : concurrency_hint_(concurrency_hint) |
| 102 | { |
| 103 | } |
| 104 | |
| 105 | /// Add a concrete service to the specified execution context. |
| 106 | BOOST_ASIO_DECLinline void make(execution_context& ctx) const override; |
| 107 | |
| 108 | private: |
| 109 | int concurrency_hint_; |
| 110 | }; |
| 111 | |
| 112 | /// Configures an execution context by reading variables from a string. |
| 113 | /** |
| 114 | * Each variable must be on a line of its own, and of the form: |
| 115 | * |
| 116 | * <tt>section.key=value</tt> |
| 117 | * |
| 118 | * or, if an optional prefix is specified: |
| 119 | * |
| 120 | * <tt>prefix.section.key=value</tt> |
| 121 | * |
| 122 | * Blank lines and lines starting with <tt>#</tt> are ignored. It is also |
| 123 | * permitted to include a comment starting with <tt>#</tt> after the value. |
| 124 | * |
| 125 | * @par Example |
| 126 | * @code boost::asio::io_context my_io_context{ |
| 127 | * boost::asio::config_from_string{ |
| 128 | * "scheduler.concurrency_hint=10\n" |
| 129 | * "scheduler.locking=1"}}; @endcode |
| 130 | */ |
| 131 | class config_from_string : public execution_context::service_maker |
| 132 | { |
| 133 | public: |
| 134 | /// Construct with the default prefix "asio". |
| 135 | explicit config_from_string(std::string s) |
| 136 | : string_(static_cast<std::string&&>(s)), |
| 137 | prefix_() |
| 138 | { |
| 139 | } |
| 140 | |
| 141 | /// Construct with a specified prefix. |
| 142 | config_from_string(std::string s, std::string prefix) |
| 143 | : string_(static_cast<std::string&&>(s)), |
| 144 | prefix_(static_cast<std::string&&>(prefix)) |
| 145 | { |
| 146 | } |
| 147 | |
| 148 | /// Add a concrete service to the specified execution context. |
| 149 | BOOST_ASIO_DECLinline void make(execution_context& ctx) const override; |
| 150 | |
| 151 | private: |
| 152 | std::string string_; |
| 153 | std::string prefix_; |
| 154 | }; |
| 155 | |
| 156 | /// Configures an execution context by reading environment variables. |
| 157 | /** |
| 158 | * The environment variable names are formed by concatenating the prefix, |
| 159 | * section, and key, with underscore as delimiter, and then converting the |
| 160 | * resulting string to upper case. |
| 161 | * |
| 162 | * @par Example |
| 163 | * @code boost::asio::io_context my_io_context{ |
| 164 | * boost::asio::config_from_env{"my_app"}}; @endcode |
| 165 | */ |
| 166 | class config_from_env : public execution_context::service_maker |
| 167 | { |
| 168 | public: |
| 169 | /// Construct with the default prefix "asio". |
| 170 | BOOST_ASIO_DECLinline config_from_env(); |
| 171 | |
| 172 | /// Construct with a specified prefix. |
| 173 | explicit config_from_env(std::string prefix) |
| 174 | : prefix_(static_cast<std::string&&>(prefix)) |
| 175 | { |
| 176 | } |
| 177 | |
| 178 | /// Add a concrete service to the specified execution context. |
| 179 | BOOST_ASIO_DECLinline void make(execution_context& ctx) const override; |
| 180 | |
| 181 | private: |
| 182 | std::string prefix_; |
| 183 | }; |
| 184 | |
| 185 | } // namespace asio |
| 186 | } // namespace boost |
| 187 | |
| 188 | #include <boost/asio/detail/pop_options.hpp> |
| 189 | |
| 190 | #include <boost/asio/impl/config.hpp> |
| 191 | #if defined(BOOST_ASIO_HEADER_ONLY1) |
| 192 | # include <boost/asio/impl/config.ipp> |
| 193 | #endif // defined(BOOST_ASIO_HEADER_ONLY) |
| 194 | |
| 195 | #endif // BOOST_ASIO_CONFIG_HPP |
| 1 | // |
| 2 | // detail/reactive_socket_service_base.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 | |
| 11 | #ifndef BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP |
| 12 | #define BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_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_HAS_IOCP) \ |
| 21 | && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \ |
| 22 | && !defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 23 | |
| 24 | #include <boost/asio/associated_cancellation_slot.hpp> |
| 25 | #include <boost/asio/buffer.hpp> |
| 26 | #include <boost/asio/cancellation_type.hpp> |
| 27 | #include <boost/asio/error.hpp> |
| 28 | #include <boost/asio/execution_context.hpp> |
| 29 | #include <boost/asio/socket_base.hpp> |
| 30 | #include <boost/asio/detail/buffer_sequence_adapter.hpp> |
| 31 | #include <boost/asio/detail/memory.hpp> |
| 32 | #include <boost/asio/detail/reactive_null_buffers_op.hpp> |
| 33 | #include <boost/asio/detail/reactive_socket_recv_op.hpp> |
| 34 | #include <boost/asio/detail/reactive_socket_recvmsg_op.hpp> |
| 35 | #include <boost/asio/detail/reactive_socket_send_op.hpp> |
| 36 | #include <boost/asio/detail/reactive_wait_op.hpp> |
| 37 | #include <boost/asio/detail/reactor.hpp> |
| 38 | #include <boost/asio/detail/reactor_op.hpp> |
| 39 | #include <boost/asio/detail/socket_holder.hpp> |
| 40 | #include <boost/asio/detail/socket_ops.hpp> |
| 41 | #include <boost/asio/detail/socket_types.hpp> |
| 42 | |
| 43 | #include <boost/asio/detail/push_options.hpp> |
| 44 | |
| 45 | namespace boost { |
| 46 | namespace asio { |
| 47 | namespace detail { |
| 48 | |
| 49 | class reactive_socket_service_base |
| 50 | { |
| 51 | public: |
| 52 | // The native type of a socket. |
| 53 | typedef socket_type native_handle_type; |
| 54 | |
| 55 | // The implementation type of the socket. |
| 56 | struct base_implementation_type |
| 57 | { |
| 58 | // The native socket representation. |
| 59 | socket_type socket_; |
| 60 | |
| 61 | // The current state of the socket. |
| 62 | socket_ops::state_type state_; |
| 63 | |
| 64 | // Per-descriptor data used by the reactor. |
| 65 | reactor::per_descriptor_data reactor_data_; |
| 66 | }; |
| 67 | |
| 68 | // Constructor. |
| 69 | BOOST_ASIO_DECLinline reactive_socket_service_base(execution_context& context); |
| 70 | |
| 71 | // Destroy all user-defined handler objects owned by the service. |
| 72 | BOOST_ASIO_DECLinline void base_shutdown(); |
| 73 | |
| 74 | // Construct a new socket implementation. |
| 75 | BOOST_ASIO_DECLinline void construct(base_implementation_type& impl); |
| 76 | |
| 77 | // Move-construct a new socket implementation. |
| 78 | BOOST_ASIO_DECLinline void base_move_construct(base_implementation_type& impl, |
| 79 | base_implementation_type& other_impl) noexcept; |
| 80 | |
| 81 | // Move-assign from another socket implementation. |
| 82 | BOOST_ASIO_DECLinline void base_move_assign(base_implementation_type& impl, |
| 83 | reactive_socket_service_base& other_service, |
| 84 | base_implementation_type& other_impl); |
| 85 | |
| 86 | // Destroy a socket implementation. |
| 87 | BOOST_ASIO_DECLinline void destroy(base_implementation_type& impl); |
| 88 | |
| 89 | // Determine whether the socket is open. |
| 90 | bool is_open(const base_implementation_type& impl) const |
| 91 | { |
| 92 | return impl.socket_ != invalid_socket; |
| 93 | } |
| 94 | |
| 95 | // Destroy a socket implementation. |
| 96 | BOOST_ASIO_DECLinline boost::system::error_code close( |
| 97 | base_implementation_type& impl, boost::system::error_code& ec); |
| 98 | |
| 99 | // Release ownership of the socket. |
| 100 | BOOST_ASIO_DECLinline socket_type release( |
| 101 | base_implementation_type& impl, boost::system::error_code& ec); |
| 102 | |
| 103 | // Get the native socket representation. |
| 104 | native_handle_type native_handle(base_implementation_type& impl) |
| 105 | { |
| 106 | return impl.socket_; |
| 107 | } |
| 108 | |
| 109 | // Cancel all operations associated with the socket. |
| 110 | BOOST_ASIO_DECLinline boost::system::error_code cancel( |
| 111 | base_implementation_type& impl, boost::system::error_code& ec); |
| 112 | |
| 113 | // Determine whether the socket is at the out-of-band data mark. |
| 114 | bool at_mark(const base_implementation_type& impl, |
| 115 | boost::system::error_code& ec) const |
| 116 | { |
| 117 | return socket_ops::sockatmark(impl.socket_, ec); |
| 118 | } |
| 119 | |
| 120 | // Determine the number of bytes available for reading. |
| 121 | std::size_t available(const base_implementation_type& impl, |
| 122 | boost::system::error_code& ec) const |
| 123 | { |
| 124 | return socket_ops::available(impl.socket_, ec); |
| 125 | } |
| 126 | |
| 127 | // Place the socket into the state where it will listen for new connections. |
| 128 | boost::system::error_code listen(base_implementation_type& impl, |
| 129 | int backlog, boost::system::error_code& ec) |
| 130 | { |
| 131 | socket_ops::listen(impl.socket_, backlog, ec); |
| 132 | return ec; |
| 133 | } |
| 134 | |
| 135 | // Perform an IO control command on the socket. |
| 136 | template <typename IO_Control_Command> |
| 137 | boost::system::error_code io_control(base_implementation_type& impl, |
| 138 | IO_Control_Command& command, boost::system::error_code& ec) |
| 139 | { |
| 140 | socket_ops::ioctl(impl.socket_, impl.state_, command.name(), |
| 141 | static_cast<ioctl_arg_type*>(command.data()), ec); |
| 142 | return ec; |
| 143 | } |
| 144 | |
| 145 | // Gets the non-blocking mode of the socket. |
| 146 | bool non_blocking(const base_implementation_type& impl) const |
| 147 | { |
| 148 | return (impl.state_ & socket_ops::user_set_non_blocking) != 0; |
| 149 | } |
| 150 | |
| 151 | // Sets the non-blocking mode of the socket. |
| 152 | boost::system::error_code non_blocking(base_implementation_type& impl, |
| 153 | bool mode, boost::system::error_code& ec) |
| 154 | { |
| 155 | socket_ops::set_user_non_blocking(impl.socket_, impl.state_, mode, ec); |
| 156 | return ec; |
| 157 | } |
| 158 | |
| 159 | // Gets the non-blocking mode of the native socket implementation. |
| 160 | bool native_non_blocking(const base_implementation_type& impl) const |
| 161 | { |
| 162 | return (impl.state_ & socket_ops::internal_non_blocking) != 0; |
| 163 | } |
| 164 | |
| 165 | // Sets the non-blocking mode of the native socket implementation. |
| 166 | boost::system::error_code native_non_blocking(base_implementation_type& impl, |
| 167 | bool mode, boost::system::error_code& ec) |
| 168 | { |
| 169 | socket_ops::set_internal_non_blocking(impl.socket_, impl.state_, mode, ec); |
| 170 | return ec; |
| 171 | } |
| 172 | |
| 173 | // Wait for the socket to become ready to read, ready to write, or to have |
| 174 | // pending error conditions. |
| 175 | boost::system::error_code wait(base_implementation_type& impl, |
| 176 | socket_base::wait_type w, boost::system::error_code& ec) |
| 177 | { |
| 178 | switch (w) |
| 179 | { |
| 180 | case socket_base::wait_read: |
| 181 | socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); |
| 182 | break; |
| 183 | case socket_base::wait_write: |
| 184 | socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); |
| 185 | break; |
| 186 | case socket_base::wait_error: |
| 187 | socket_ops::poll_error(impl.socket_, impl.state_, -1, ec); |
| 188 | break; |
| 189 | default: |
| 190 | ec = boost::asio::error::invalid_argument; |
| 191 | break; |
| 192 | } |
| 193 | |
| 194 | return ec; |
| 195 | } |
| 196 | |
| 197 | // Asynchronously wait for the socket to become ready to read, ready to |
| 198 | // write, or to have pending error conditions. |
| 199 | template <typename Handler, typename IoExecutor> |
| 200 | void async_wait(base_implementation_type& impl, |
| 201 | socket_base::wait_type w, Handler& handler, const IoExecutor& io_ex) |
| 202 | { |
| 203 | bool is_continuation = |
| 204 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 205 | |
| 206 | associated_cancellation_slot_t<Handler> slot |
| 207 | = boost::asio::get_associated_cancellation_slot(handler); |
| 208 | |
| 209 | // Allocate and construct an operation to wrap the handler. |
| 210 | typedef reactive_wait_op<Handler, IoExecutor> op; |
| 211 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 212 | op::ptr::allocate(handler), 0 }; |
| 213 | p.p = new (p.v) op(success_ec_, handler, io_ex); |
| 214 | |
| 215 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 216 | &impl, impl.socket_, "async_wait"))(void)0; |
| 217 | |
| 218 | int op_type; |
| 219 | switch (w) |
| 220 | { |
| 221 | case socket_base::wait_read: |
| 222 | op_type = reactor::read_op; |
| 223 | break; |
| 224 | case socket_base::wait_write: |
| 225 | op_type = reactor::write_op; |
| 226 | break; |
| 227 | case socket_base::wait_error: |
| 228 | op_type = reactor::except_op; |
| 229 | break; |
| 230 | default: |
| 231 | p.p->ec_ = boost::asio::error::invalid_argument; |
| 232 | start_op(impl, reactor::read_op, p.p, |
| 233 | is_continuation, false, true, false, &io_ex, 0); |
| 234 | p.v = p.p = 0; |
| 235 | return; |
| 236 | } |
| 237 | |
| 238 | // Optionally register for per-operation cancellation. |
| 239 | if (slot.is_connected()) |
| 240 | { |
| 241 | p.p->cancellation_key_ = |
| 242 | &slot.template emplace<reactor_op_cancellation>( |
| 243 | &reactor_, &impl.reactor_data_, impl.socket_, op_type); |
| 244 | } |
| 245 | |
| 246 | start_op(impl, op_type, p.p, is_continuation, |
| 247 | false, false, false, &io_ex, 0); |
| 248 | p.v = p.p = 0; |
| 249 | } |
| 250 | |
| 251 | // Send the given data to the peer. |
| 252 | template <typename ConstBufferSequence> |
| 253 | size_t send(base_implementation_type& impl, |
| 254 | const ConstBufferSequence& buffers, |
| 255 | socket_base::message_flags flags, boost::system::error_code& ec) |
| 256 | { |
| 257 | typedef buffer_sequence_adapter<boost::asio::const_buffer, |
| 258 | ConstBufferSequence> bufs_type; |
| 259 | |
| 260 | if (bufs_type::is_single_buffer) |
| 261 | { |
| 262 | return socket_ops::sync_send1(impl.socket_, |
| 263 | impl.state_, bufs_type::first(buffers).data(), |
| 264 | bufs_type::first(buffers).size(), flags, ec); |
| 265 | } |
| 266 | else |
| 267 | { |
| 268 | bufs_type bufs(buffers); |
| 269 | return socket_ops::sync_send(impl.socket_, impl.state_, |
| 270 | bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | // Wait until data can be sent without blocking. |
| 275 | size_t send(base_implementation_type& impl, const null_buffers&, |
| 276 | socket_base::message_flags, boost::system::error_code& ec) |
| 277 | { |
| 278 | // Wait for socket to become ready. |
| 279 | socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); |
| 280 | |
| 281 | return 0; |
| 282 | } |
| 283 | |
| 284 | // Start an asynchronous send. The data being sent must be valid for the |
| 285 | // lifetime of the asynchronous operation. |
| 286 | template <typename ConstBufferSequence, typename Handler, typename IoExecutor> |
| 287 | void async_send(base_implementation_type& impl, |
| 288 | const ConstBufferSequence& buffers, socket_base::message_flags flags, |
| 289 | Handler& handler, const IoExecutor& io_ex) |
| 290 | { |
| 291 | bool is_continuation = |
| 292 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 293 | |
| 294 | associated_cancellation_slot_t<Handler> slot |
| 295 | = boost::asio::get_associated_cancellation_slot(handler); |
| 296 | |
| 297 | // Allocate and construct an operation to wrap the handler. |
| 298 | typedef reactive_socket_send_op< |
| 299 | ConstBufferSequence, Handler, IoExecutor> op; |
| 300 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 301 | op::ptr::allocate(handler), 0 }; |
| 302 | p.p = new (p.v) op(success_ec_, impl.socket_, |
| 303 | impl.state_, buffers, flags, handler, io_ex); |
| 304 | |
| 305 | // Optionally register for per-operation cancellation. |
| 306 | if (slot.is_connected()) |
| 307 | { |
| 308 | p.p->cancellation_key_ = |
| 309 | &slot.template emplace<reactor_op_cancellation>( |
| 310 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::write_op); |
| 311 | } |
| 312 | |
| 313 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 314 | &impl, impl.socket_, "async_send"))(void)0; |
| 315 | |
| 316 | start_op(impl, reactor::write_op, p.p, is_continuation, true, |
| 317 | ((impl.state_ & socket_ops::stream_oriented) |
| 318 | && buffer_sequence_adapter<boost::asio::const_buffer, |
| 319 | ConstBufferSequence>::all_empty(buffers)), true, &io_ex, 0); |
| 320 | p.v = p.p = 0; |
| 321 | } |
| 322 | |
| 323 | // Start an asynchronous wait until data can be sent without blocking. |
| 324 | template <typename Handler, typename IoExecutor> |
| 325 | void async_send(base_implementation_type& impl, const null_buffers&, |
| 326 | socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) |
| 327 | { |
| 328 | bool is_continuation = |
| 329 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 330 | |
| 331 | associated_cancellation_slot_t<Handler> slot |
| 332 | = boost::asio::get_associated_cancellation_slot(handler); |
| 333 | |
| 334 | // Allocate and construct an operation to wrap the handler. |
| 335 | typedef reactive_null_buffers_op<Handler, IoExecutor> op; |
| 336 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 337 | op::ptr::allocate(handler), 0 }; |
| 338 | p.p = new (p.v) op(success_ec_, handler, io_ex); |
| 339 | |
| 340 | // Optionally register for per-operation cancellation. |
| 341 | if (slot.is_connected()) |
| 342 | { |
| 343 | p.p->cancellation_key_ = |
| 344 | &slot.template emplace<reactor_op_cancellation>( |
| 345 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::write_op); |
| 346 | } |
| 347 | |
| 348 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 349 | &impl, impl.socket_, "async_send(null_buffers)"))(void)0; |
| 350 | |
| 351 | start_op(impl, reactor::write_op, p.p, |
| 352 | is_continuation, false, false, false, &io_ex, 0); |
| 353 | p.v = p.p = 0; |
| 354 | } |
| 355 | |
| 356 | // Receive some data from the peer. Returns the number of bytes received. |
| 357 | template <typename MutableBufferSequence> |
| 358 | size_t receive(base_implementation_type& impl, |
| 359 | const MutableBufferSequence& buffers, |
| 360 | socket_base::message_flags flags, boost::system::error_code& ec) |
| 361 | { |
| 362 | typedef buffer_sequence_adapter<boost::asio::mutable_buffer, |
| 363 | MutableBufferSequence> bufs_type; |
| 364 | |
| 365 | if (bufs_type::is_single_buffer) |
| 366 | { |
| 367 | return socket_ops::sync_recv1(impl.socket_, |
| 368 | impl.state_, bufs_type::first(buffers).data(), |
| 369 | bufs_type::first(buffers).size(), flags, ec); |
| 370 | } |
| 371 | else |
| 372 | { |
| 373 | bufs_type bufs(buffers); |
| 374 | return socket_ops::sync_recv(impl.socket_, impl.state_, |
| 375 | bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); |
| 376 | } |
| 377 | } |
| 378 | |
| 379 | // Wait until data can be received without blocking. |
| 380 | size_t receive(base_implementation_type& impl, const null_buffers&, |
| 381 | socket_base::message_flags, boost::system::error_code& ec) |
| 382 | { |
| 383 | // Wait for socket to become ready. |
| 384 | socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); |
| 385 | |
| 386 | return 0; |
| 387 | } |
| 388 | |
| 389 | // Start an asynchronous receive. The buffer for the data being received |
| 390 | // must be valid for the lifetime of the asynchronous operation. |
| 391 | template <typename MutableBufferSequence, |
| 392 | typename Handler, typename IoExecutor> |
| 393 | void async_receive(base_implementation_type& impl, |
| 394 | const MutableBufferSequence& buffers, socket_base::message_flags flags, |
| 395 | Handler& handler, const IoExecutor& io_ex) |
| 396 | { |
| 397 | bool is_continuation = |
| 398 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 399 | |
| 400 | associated_cancellation_slot_t<Handler> slot |
| 401 | = boost::asio::get_associated_cancellation_slot(handler); |
| 402 | |
| 403 | // Allocate and construct an operation to wrap the handler. |
| 404 | typedef reactive_socket_recv_op< |
| 405 | MutableBufferSequence, Handler, IoExecutor> op; |
| 406 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 407 | op::ptr::allocate(handler), 0 }; |
| 408 | p.p = new (p.v) op(success_ec_, impl.socket_, |
| 409 | impl.state_, buffers, flags, handler, io_ex); |
| 410 | |
| 411 | // Optionally register for per-operation cancellation. |
| 412 | if (slot.is_connected()) |
| 413 | { |
| 414 | p.p->cancellation_key_ = |
| 415 | &slot.template emplace<reactor_op_cancellation>( |
| 416 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 417 | } |
| 418 | |
| 419 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 420 | &impl, impl.socket_, "async_receive"))(void)0; |
| 421 | |
| 422 | start_op(impl, |
| 423 | (flags & socket_base::message_out_of_band) |
| 424 | ? reactor::except_op : reactor::read_op, |
| 425 | p.p, is_continuation, |
| 426 | (flags & socket_base::message_out_of_band) == 0, |
| 427 | ((impl.state_ & socket_ops::stream_oriented) |
| 428 | && buffer_sequence_adapter<boost::asio::mutable_buffer, |
| 429 | MutableBufferSequence>::all_empty(buffers)), true, &io_ex, 0); |
| 430 | p.v = p.p = 0; |
| 431 | } |
| 432 | |
| 433 | // Wait until data can be received without blocking. |
| 434 | template <typename Handler, typename IoExecutor> |
| 435 | void async_receive(base_implementation_type& impl, |
| 436 | const null_buffers&, socket_base::message_flags flags, |
| 437 | Handler& handler, const IoExecutor& io_ex) |
| 438 | { |
| 439 | bool is_continuation = |
| 440 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 441 | |
| 442 | associated_cancellation_slot_t<Handler> slot |
| 443 | = boost::asio::get_associated_cancellation_slot(handler); |
| 444 | |
| 445 | // Allocate and construct an operation to wrap the handler. |
| 446 | typedef reactive_null_buffers_op<Handler, IoExecutor> op; |
| 447 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 448 | op::ptr::allocate(handler), 0 }; |
| 449 | p.p = new (p.v) op(success_ec_, handler, io_ex); |
| 450 | |
| 451 | // Optionally register for per-operation cancellation. |
| 452 | if (slot.is_connected()) |
| 453 | { |
| 454 | p.p->cancellation_key_ = |
| 455 | &slot.template emplace<reactor_op_cancellation>( |
| 456 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 457 | } |
| 458 | |
| 459 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 460 | &impl, impl.socket_, "async_receive(null_buffers)"))(void)0; |
| 461 | |
| 462 | start_op(impl, |
| 463 | (flags & socket_base::message_out_of_band) |
| 464 | ? reactor::except_op : reactor::read_op, |
| 465 | p.p, is_continuation, false, false, false, &io_ex, 0); |
| 466 | p.v = p.p = 0; |
| 467 | } |
| 468 | |
| 469 | // Receive some data with associated flags. Returns the number of bytes |
| 470 | // received. |
| 471 | template <typename MutableBufferSequence> |
| 472 | size_t receive_with_flags(base_implementation_type& impl, |
| 473 | const MutableBufferSequence& buffers, |
| 474 | socket_base::message_flags in_flags, |
| 475 | socket_base::message_flags& out_flags, boost::system::error_code& ec) |
| 476 | { |
| 477 | buffer_sequence_adapter<boost::asio::mutable_buffer, |
| 478 | MutableBufferSequence> bufs(buffers); |
| 479 | |
| 480 | return socket_ops::sync_recvmsg(impl.socket_, impl.state_, |
| 481 | bufs.buffers(), bufs.count(), in_flags, out_flags, ec); |
| 482 | } |
| 483 | |
| 484 | // Wait until data can be received without blocking. |
| 485 | size_t receive_with_flags(base_implementation_type& impl, |
| 486 | const null_buffers&, socket_base::message_flags, |
| 487 | socket_base::message_flags& out_flags, boost::system::error_code& ec) |
| 488 | { |
| 489 | // Wait for socket to become ready. |
| 490 | socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); |
| 491 | |
| 492 | // Clear out_flags, since we cannot give it any other sensible value when |
| 493 | // performing a null_buffers operation. |
| 494 | out_flags = 0; |
| 495 | |
| 496 | return 0; |
| 497 | } |
| 498 | |
| 499 | // Start an asynchronous receive. The buffer for the data being received |
| 500 | // must be valid for the lifetime of the asynchronous operation. |
| 501 | template <typename MutableBufferSequence, |
| 502 | typename Handler, typename IoExecutor> |
| 503 | void async_receive_with_flags(base_implementation_type& impl, |
| 504 | const MutableBufferSequence& buffers, socket_base::message_flags in_flags, |
| 505 | socket_base::message_flags& out_flags, Handler& handler, |
| 506 | const IoExecutor& io_ex) |
| 507 | { |
| 508 | bool is_continuation = |
| 509 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 510 | |
| 511 | associated_cancellation_slot_t<Handler> slot |
| 512 | = boost::asio::get_associated_cancellation_slot(handler); |
| 513 | |
| 514 | // Allocate and construct an operation to wrap the handler. |
| 515 | typedef reactive_socket_recvmsg_op< |
| 516 | MutableBufferSequence, Handler, IoExecutor> op; |
| 517 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 518 | op::ptr::allocate(handler), 0 }; |
| 519 | p.p = new (p.v) op(success_ec_, impl.socket_, |
| 520 | buffers, in_flags, out_flags, handler, io_ex); |
| 521 | |
| 522 | // Optionally register for per-operation cancellation. |
| 523 | if (slot.is_connected()) |
| 524 | { |
| 525 | p.p->cancellation_key_ = |
| 526 | &slot.template emplace<reactor_op_cancellation>( |
| 527 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 528 | } |
| 529 | |
| 530 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 531 | &impl, impl.socket_, "async_receive_with_flags"))(void)0; |
| 532 | |
| 533 | start_op(impl, |
| 534 | (in_flags & socket_base::message_out_of_band) |
| 535 | ? reactor::except_op : reactor::read_op, |
| 536 | p.p, is_continuation, |
| 537 | (in_flags & socket_base::message_out_of_band) == 0, |
| 538 | false, true, &io_ex, 0); |
| 539 | p.v = p.p = 0; |
| 540 | } |
| 541 | |
| 542 | // Wait until data can be received without blocking. |
| 543 | template <typename Handler, typename IoExecutor> |
| 544 | void async_receive_with_flags(base_implementation_type& impl, |
| 545 | const null_buffers&, socket_base::message_flags in_flags, |
| 546 | socket_base::message_flags& out_flags, Handler& handler, |
| 547 | const IoExecutor& io_ex) |
| 548 | { |
| 549 | bool is_continuation = |
| 550 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 551 | |
| 552 | associated_cancellation_slot_t<Handler> slot |
| 553 | = boost::asio::get_associated_cancellation_slot(handler); |
| 554 | |
| 555 | // Allocate and construct an operation to wrap the handler. |
| 556 | typedef reactive_null_buffers_op<Handler, IoExecutor> op; |
| 557 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 558 | op::ptr::allocate(handler), 0 }; |
| 559 | p.p = new (p.v) op(success_ec_, handler, io_ex); |
| 560 | |
| 561 | // Optionally register for per-operation cancellation. |
| 562 | if (slot.is_connected()) |
| 563 | { |
| 564 | p.p->cancellation_key_ = |
| 565 | &slot.template emplace<reactor_op_cancellation>( |
| 566 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 567 | } |
| 568 | |
| 569 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 570 | &impl, impl.socket_, "async_receive_with_flags(null_buffers)"))(void)0; |
| 571 | |
| 572 | // Clear out_flags, since we cannot give it any other sensible value when |
| 573 | // performing a null_buffers operation. |
| 574 | out_flags = 0; |
| 575 | |
| 576 | start_op(impl, |
| 577 | (in_flags & socket_base::message_out_of_band) |
| 578 | ? reactor::except_op : reactor::read_op, |
| 579 | p.p, is_continuation, false, false, false, &io_ex, 0); |
| 580 | p.v = p.p = 0; |
| 581 | } |
| 582 | |
| 583 | protected: |
| 584 | // Open a new socket implementation. |
| 585 | BOOST_ASIO_DECLinline boost::system::error_code do_open( |
| 586 | base_implementation_type& impl, int af, |
| 587 | int type, int protocol, boost::system::error_code& ec); |
| 588 | |
| 589 | // Assign a native socket to a socket implementation. |
| 590 | BOOST_ASIO_DECLinline boost::system::error_code do_assign( |
| 591 | base_implementation_type& impl, int type, |
| 592 | const native_handle_type& native_socket, boost::system::error_code& ec); |
| 593 | |
| 594 | // Start the asynchronous read or write operation. |
| 595 | BOOST_ASIO_DECLinline void do_start_op(base_implementation_type& impl, |
| 596 | int op_type, reactor_op* op, bool is_continuation, |
| 597 | bool allow_speculative, bool noop, bool needs_non_blocking, |
| 598 | void (*on_immediate)(operation* op, bool, const void*), |
| 599 | const void* immediate_arg); |
| 600 | |
| 601 | // Start the asynchronous operation for handlers that are specialised for |
| 602 | // immediate completion. |
| 603 | template <typename Op> |
| 604 | void start_op(base_implementation_type& impl, int op_type, Op* op, |
| 605 | bool is_continuation, bool allow_speculative, bool noop, |
| 606 | bool needs_non_blocking, const void* io_ex, ...) |
| 607 | { |
| 608 | return do_start_op(impl, op_type, op, is_continuation, allow_speculative, |
| 609 | noop, needs_non_blocking, &Op::do_immediate, io_ex); |
| 610 | } |
| 611 | |
| 612 | // Start the asynchronous operation for handlers that are not specialised for |
| 613 | // immediate completion. |
| 614 | template <typename Op> |
| 615 | void start_op(base_implementation_type& impl, int op_type, |
| 616 | Op* op, bool is_continuation, bool allow_speculative, |
| 617 | bool noop, bool needs_non_blocking, const void*, |
| 618 | enable_if_t< |
| 619 | is_same< |
| 620 | typename associated_immediate_executor< |
| 621 | typename Op::handler_type, |
| 622 | typename Op::io_executor_type |
| 623 | >::asio_associated_immediate_executor_is_unspecialised, |
| 624 | void |
| 625 | >::value |
| 626 | >*) |
| 627 | { |
| 628 | return do_start_op(impl, op_type, op, is_continuation, |
| 629 | allow_speculative, noop, needs_non_blocking, |
| 630 | &reactor::call_post_immediate_completion, &reactor_); |
| 631 | } |
| 632 | |
| 633 | // Start the asynchronous accept operation. |
| 634 | BOOST_ASIO_DECLinline void do_start_accept_op(base_implementation_type& impl, |
| 635 | reactor_op* op, bool is_continuation, bool peer_is_open, |
| 636 | void (*on_immediate)(operation* op, bool, const void*), |
| 637 | const void* immediate_arg); |
| 638 | |
| 639 | // Start the asynchronous accept operation for handlers that are specialised |
| 640 | // for immediate completion. |
| 641 | template <typename Op> |
| 642 | void start_accept_op(base_implementation_type& impl, Op* op, |
| 643 | bool is_continuation, bool peer_is_open, const void* io_ex, ...) |
| 644 | { |
| 645 | return do_start_accept_op(impl, op, is_continuation, |
| 646 | peer_is_open, &Op::do_immediate, io_ex); |
| 647 | } |
| 648 | |
| 649 | // Start the asynchronous operation for handlers that are not specialised for |
| 650 | // immediate completion. |
| 651 | template <typename Op> |
| 652 | void start_accept_op(base_implementation_type& impl, Op* op, |
| 653 | bool is_continuation, bool peer_is_open, const void*, |
| 654 | enable_if_t< |
| 655 | is_same< |
| 656 | typename associated_immediate_executor< |
| 657 | typename Op::handler_type, |
| 658 | typename Op::io_executor_type |
| 659 | >::asio_associated_immediate_executor_is_unspecialised, |
| 660 | void |
| 661 | >::value |
| 662 | >*) |
| 663 | { |
| 664 | return do_start_accept_op(impl, op, is_continuation, peer_is_open, |
| 665 | &reactor::call_post_immediate_completion, &reactor_); |
| 666 | } |
| 667 | |
| 668 | // Start the asynchronous connect operation. |
| 669 | BOOST_ASIO_DECLinline void do_start_connect_op(base_implementation_type& impl, |
| 670 | reactor_op* op, bool is_continuation, const void* addr, size_t addrlen, |
| 671 | void (*on_immediate)(operation* op, bool, const void*), |
| 672 | const void* immediate_arg); |
| 673 | |
| 674 | // Start the asynchronous operation for handlers that are specialised for |
| 675 | // immediate completion. |
| 676 | template <typename Op> |
| 677 | void start_connect_op(base_implementation_type& impl, |
| 678 | Op* op, bool is_continuation, const void* addr, |
| 679 | size_t addrlen, const void* io_ex, ...) |
| 680 | { |
| 681 | return do_start_connect_op(impl, op, is_continuation, |
| 682 | addr, addrlen, &Op::do_immediate, io_ex); |
| 683 | } |
| 684 | |
| 685 | // Start the asynchronous operation for handlers that are not specialised for |
| 686 | // immediate completion. |
| 687 | template <typename Op> |
| 688 | void start_connect_op(base_implementation_type& impl, Op* op, |
| 689 | bool is_continuation, const void* addr, size_t addrlen, const void*, |
| 690 | enable_if_t< |
| 691 | is_same< |
| 692 | typename associated_immediate_executor< |
| 693 | typename Op::handler_type, |
| 694 | typename Op::io_executor_type |
| 695 | >::asio_associated_immediate_executor_is_unspecialised, |
| 696 | void |
| 697 | >::value |
| 698 | >*) |
| 699 | { |
| 700 | return do_start_connect_op(impl, op, is_continuation, addr, |
| 701 | addrlen, &reactor::call_post_immediate_completion, &reactor_); |
| 702 | } |
| 703 | |
| 704 | // Helper class used to implement per-operation cancellation |
| 705 | class reactor_op_cancellation |
| 706 | { |
| 707 | public: |
| 708 | reactor_op_cancellation(reactor* r, |
| 709 | reactor::per_descriptor_data* p, socket_type d, int o) |
| 710 | : reactor_(r), |
| 711 | reactor_data_(p), |
| 712 | descriptor_(d), |
| 713 | op_type_(o) |
| 714 | { |
| 715 | } |
| 716 | |
| 717 | void operator()(cancellation_type_t type) |
| 718 | { |
| 719 | if (!!(type & |
| 720 | (cancellation_type::terminal |
| 721 | | cancellation_type::partial |
| 722 | | cancellation_type::total))) |
| 723 | { |
| 724 | reactor_->cancel_ops_by_key(descriptor_, |
| 725 | *reactor_data_, op_type_, this); |
| 726 | } |
| 727 | } |
| 728 | |
| 729 | private: |
| 730 | reactor* reactor_; |
| 731 | reactor::per_descriptor_data* reactor_data_; |
| 732 | socket_type descriptor_; |
| 733 | int op_type_; |
| 734 | }; |
| 735 | |
| 736 | // The selector that performs event demultiplexing for the service. |
| 737 | reactor& reactor_; |
| 738 | |
| 739 | // Cached success value to avoid accessing category singleton. |
| 740 | const boost::system::error_code success_ec_; |
| 741 | |
| 742 | // Extra state flags to be applied to newly opened sockets. |
| 743 | socket_ops::state_type extra_state_; |
| 744 | }; |
| 745 | |
| 746 | } // namespace detail |
| 747 | } // namespace asio |
| 748 | } // namespace boost |
| 749 | |
| 750 | #include <boost/asio/detail/pop_options.hpp> |
| 751 | |
| 752 | #if defined(BOOST_ASIO_HEADER_ONLY1) |
| 753 | # include <boost/asio/detail/impl/reactive_socket_service_base.ipp> |
| 754 | #endif // defined(BOOST_ASIO_HEADER_ONLY) |
| 755 | |
| 756 | #endif // !defined(BOOST_ASIO_HAS_IOCP) |
| 757 | // && !defined(BOOST_ASIO_WINDOWS_RUNTIME) |
| 758 | // && !defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 759 | |
| 760 | #endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP |
| 1 | // | ||||||||||||||||||||||||||||||||||||
| 2 | // detail/impl/socket_ops.ipp | ||||||||||||||||||||||||||||||||||||
| 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 | |||||||||||||||||||||||||||||||||||||
| 11 | #ifndef BOOST_ASIO_DETAIL_SOCKET_OPS_IPP | ||||||||||||||||||||||||||||||||||||
| 12 | #define BOOST_ASIO_DETAIL_SOCKET_OPS_IPP | ||||||||||||||||||||||||||||||||||||
| 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 | #include <cctype> | ||||||||||||||||||||||||||||||||||||
| 21 | #include <cstdio> | ||||||||||||||||||||||||||||||||||||
| 22 | #include <cstdlib> | ||||||||||||||||||||||||||||||||||||
| 23 | #include <cstring> | ||||||||||||||||||||||||||||||||||||
| 24 | #include <cerrno> | ||||||||||||||||||||||||||||||||||||
| 25 | #include <new> | ||||||||||||||||||||||||||||||||||||
| 26 | #include <boost/asio/detail/assert.hpp> | ||||||||||||||||||||||||||||||||||||
| 27 | #include <boost/asio/detail/socket_ops.hpp> | ||||||||||||||||||||||||||||||||||||
| 28 | #include <boost/asio/error.hpp> | ||||||||||||||||||||||||||||||||||||
| 29 | |||||||||||||||||||||||||||||||||||||
| 30 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 31 | # include <codecvt> | ||||||||||||||||||||||||||||||||||||
| 32 | # include <locale> | ||||||||||||||||||||||||||||||||||||
| 33 | # include <string> | ||||||||||||||||||||||||||||||||||||
| 34 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 35 | |||||||||||||||||||||||||||||||||||||
| 36 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) \ | ||||||||||||||||||||||||||||||||||||
| 37 | || defined(__MACH__) && defined(__APPLE__) | ||||||||||||||||||||||||||||||||||||
| 38 | # if defined(BOOST_ASIO_HAS_PTHREADS1) | ||||||||||||||||||||||||||||||||||||
| 39 | # include <pthread.h> | ||||||||||||||||||||||||||||||||||||
| 40 | # endif // defined(BOOST_ASIO_HAS_PTHREADS) | ||||||||||||||||||||||||||||||||||||
| 41 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 42 | // || defined(__MACH__) && defined(__APPLE__) | ||||||||||||||||||||||||||||||||||||
| 43 | |||||||||||||||||||||||||||||||||||||
| 44 | #if defined(_MSC_VER) && (_MSC_VER >= 1800) | ||||||||||||||||||||||||||||||||||||
| 45 | # include <malloc.h> | ||||||||||||||||||||||||||||||||||||
| 46 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1800) | ||||||||||||||||||||||||||||||||||||
| 47 | |||||||||||||||||||||||||||||||||||||
| 48 | #include <boost/asio/detail/push_options.hpp> | ||||||||||||||||||||||||||||||||||||
| 49 | |||||||||||||||||||||||||||||||||||||
| 50 | namespace boost { | ||||||||||||||||||||||||||||||||||||
| 51 | namespace asio { | ||||||||||||||||||||||||||||||||||||
| 52 | namespace detail { | ||||||||||||||||||||||||||||||||||||
| 53 | namespace socket_ops { | ||||||||||||||||||||||||||||||||||||
| 54 | |||||||||||||||||||||||||||||||||||||
| 55 | #if !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 56 | |||||||||||||||||||||||||||||||||||||
| 57 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 58 | struct msghdr { int msg_namelen; }; | ||||||||||||||||||||||||||||||||||||
| 59 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 60 | |||||||||||||||||||||||||||||||||||||
| 61 | #if defined(__hpux) | ||||||||||||||||||||||||||||||||||||
| 62 | // HP-UX doesn't declare these functions extern "C", so they are declared again | ||||||||||||||||||||||||||||||||||||
| 63 | // here to avoid linker errors about undefined symbols. | ||||||||||||||||||||||||||||||||||||
| 64 | extern "C" char* if_indextoname(unsigned int, char*); | ||||||||||||||||||||||||||||||||||||
| 65 | extern "C" unsigned int if_nametoindex(const char*); | ||||||||||||||||||||||||||||||||||||
| 66 | #endif // defined(__hpux) | ||||||||||||||||||||||||||||||||||||
| 67 | |||||||||||||||||||||||||||||||||||||
| 68 | #endif // !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 69 | |||||||||||||||||||||||||||||||||||||
| 70 | inline void clear_last_error() | ||||||||||||||||||||||||||||||||||||
| 71 | { | ||||||||||||||||||||||||||||||||||||
| 72 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 73 | WSASetLastError(0); | ||||||||||||||||||||||||||||||||||||
| 74 | #else | ||||||||||||||||||||||||||||||||||||
| 75 | errno(*__errno_location ()) = 0; | ||||||||||||||||||||||||||||||||||||
| 76 | #endif | ||||||||||||||||||||||||||||||||||||
| 77 | } | ||||||||||||||||||||||||||||||||||||
| 78 | |||||||||||||||||||||||||||||||||||||
| 79 | #if !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 80 | |||||||||||||||||||||||||||||||||||||
| 81 | inline void get_last_error( | ||||||||||||||||||||||||||||||||||||
| 82 | boost::system::error_code& ec, bool is_error_condition) | ||||||||||||||||||||||||||||||||||||
| 83 | { | ||||||||||||||||||||||||||||||||||||
| 84 | if (!is_error_condition) | ||||||||||||||||||||||||||||||||||||
| 85 | { | ||||||||||||||||||||||||||||||||||||
| 86 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 87 | } | ||||||||||||||||||||||||||||||||||||
| 88 | else | ||||||||||||||||||||||||||||||||||||
| 89 | { | ||||||||||||||||||||||||||||||||||||
| 90 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 91 | ec = boost::system::error_code(WSAGetLastError(), | ||||||||||||||||||||||||||||||||||||
| 92 | boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||||
| 93 | #else | ||||||||||||||||||||||||||||||||||||
| 94 | ec = boost::system::error_code(errno(*__errno_location ()), | ||||||||||||||||||||||||||||||||||||
| 95 | boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||||
| 96 | #endif | ||||||||||||||||||||||||||||||||||||
| 97 | } | ||||||||||||||||||||||||||||||||||||
| 98 | } | ||||||||||||||||||||||||||||||||||||
| 99 | |||||||||||||||||||||||||||||||||||||
| 100 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||||
| 101 | inline socket_type call_accept(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||||
| 102 | socket_type s, void* addr, std::size_t* addrlen) | ||||||||||||||||||||||||||||||||||||
| 103 | { | ||||||||||||||||||||||||||||||||||||
| 104 | SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; | ||||||||||||||||||||||||||||||||||||
| 105 | socket_type result = ::accept(s, | ||||||||||||||||||||||||||||||||||||
| 106 | static_cast<socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||||
| 107 | addrlen ? &tmp_addrlen : 0); | ||||||||||||||||||||||||||||||||||||
| 108 | if (addrlen) | ||||||||||||||||||||||||||||||||||||
| 109 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||||
| 110 | return result; | ||||||||||||||||||||||||||||||||||||
| 111 | } | ||||||||||||||||||||||||||||||||||||
| 112 | |||||||||||||||||||||||||||||||||||||
| 113 | socket_type accept(socket_type s, void* addr, | ||||||||||||||||||||||||||||||||||||
| 114 | std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 115 | { | ||||||||||||||||||||||||||||||||||||
| 116 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 117 | { | ||||||||||||||||||||||||||||||||||||
| 118 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 119 | return invalid_socket; | ||||||||||||||||||||||||||||||||||||
| 120 | } | ||||||||||||||||||||||||||||||||||||
| 121 | |||||||||||||||||||||||||||||||||||||
| 122 | socket_type new_s = call_accept(&msghdr::msg_namelen, s, addr, addrlen); | ||||||||||||||||||||||||||||||||||||
| 123 | get_last_error(ec, new_s == invalid_socket); | ||||||||||||||||||||||||||||||||||||
| 124 | if (new_s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 125 | return new_s; | ||||||||||||||||||||||||||||||||||||
| 126 | |||||||||||||||||||||||||||||||||||||
| 127 | #if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) | ||||||||||||||||||||||||||||||||||||
| 128 | int optval = 1; | ||||||||||||||||||||||||||||||||||||
| 129 | int result = ::setsockopt(new_s, SOL_SOCKET1, | ||||||||||||||||||||||||||||||||||||
| 130 | SO_NOSIGPIPE, &optval, sizeof(optval)); | ||||||||||||||||||||||||||||||||||||
| 131 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 132 | if (result != 0) | ||||||||||||||||||||||||||||||||||||
| 133 | { | ||||||||||||||||||||||||||||||||||||
| 134 | ::close(new_s); | ||||||||||||||||||||||||||||||||||||
| 135 | return invalid_socket; | ||||||||||||||||||||||||||||||||||||
| 136 | } | ||||||||||||||||||||||||||||||||||||
| 137 | #endif | ||||||||||||||||||||||||||||||||||||
| 138 | |||||||||||||||||||||||||||||||||||||
| 139 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 140 | return new_s; | ||||||||||||||||||||||||||||||||||||
| 141 | } | ||||||||||||||||||||||||||||||||||||
| 142 | |||||||||||||||||||||||||||||||||||||
| 143 | socket_type sync_accept(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||||
| 144 | void* addr, std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 145 | { | ||||||||||||||||||||||||||||||||||||
| 146 | // Accept a socket. | ||||||||||||||||||||||||||||||||||||
| 147 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 148 | { | ||||||||||||||||||||||||||||||||||||
| 149 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||||
| 150 | socket_type new_socket = socket_ops::accept(s, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||||
| 151 | |||||||||||||||||||||||||||||||||||||
| 152 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 153 | if (new_socket != invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 154 | return new_socket; | ||||||||||||||||||||||||||||||||||||
| 155 | |||||||||||||||||||||||||||||||||||||
| 156 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 157 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 158 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 159 | { | ||||||||||||||||||||||||||||||||||||
| 160 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 161 | return invalid_socket; | ||||||||||||||||||||||||||||||||||||
| 162 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||||
| 163 | } | ||||||||||||||||||||||||||||||||||||
| 164 | else if (ec == boost::asio::error::connection_aborted) | ||||||||||||||||||||||||||||||||||||
| 165 | { | ||||||||||||||||||||||||||||||||||||
| 166 | if (state & enable_connection_aborted) | ||||||||||||||||||||||||||||||||||||
| 167 | return invalid_socket; | ||||||||||||||||||||||||||||||||||||
| 168 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||||
| 169 | } | ||||||||||||||||||||||||||||||||||||
| 170 | #if defined(EPROTO71) | ||||||||||||||||||||||||||||||||||||
| 171 | else if (ec.value() == EPROTO71) | ||||||||||||||||||||||||||||||||||||
| 172 | { | ||||||||||||||||||||||||||||||||||||
| 173 | if (state & enable_connection_aborted) | ||||||||||||||||||||||||||||||||||||
| 174 | return invalid_socket; | ||||||||||||||||||||||||||||||||||||
| 175 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||||
| 176 | } | ||||||||||||||||||||||||||||||||||||
| 177 | #endif // defined(EPROTO) | ||||||||||||||||||||||||||||||||||||
| 178 | else | ||||||||||||||||||||||||||||||||||||
| 179 | return invalid_socket; | ||||||||||||||||||||||||||||||||||||
| 180 | |||||||||||||||||||||||||||||||||||||
| 181 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||||
| 182 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||||
| 183 | return invalid_socket; | ||||||||||||||||||||||||||||||||||||
| 184 | } | ||||||||||||||||||||||||||||||||||||
| 185 | } | ||||||||||||||||||||||||||||||||||||
| 186 | |||||||||||||||||||||||||||||||||||||
| 187 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 188 | |||||||||||||||||||||||||||||||||||||
| 189 | void complete_iocp_accept(socket_type s, void* output_buffer, | ||||||||||||||||||||||||||||||||||||
| 190 | DWORD address_length, void* addr, std::size_t* addrlen, | ||||||||||||||||||||||||||||||||||||
| 191 | socket_type new_socket, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 192 | { | ||||||||||||||||||||||||||||||||||||
| 193 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||||
| 194 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 195 | ec = boost::asio::error::connection_aborted; | ||||||||||||||||||||||||||||||||||||
| 196 | |||||||||||||||||||||||||||||||||||||
| 197 | if (!ec) | ||||||||||||||||||||||||||||||||||||
| 198 | { | ||||||||||||||||||||||||||||||||||||
| 199 | // Get the address of the peer. | ||||||||||||||||||||||||||||||||||||
| 200 | if (addr && addrlen) | ||||||||||||||||||||||||||||||||||||
| 201 | { | ||||||||||||||||||||||||||||||||||||
| 202 | LPSOCKADDR local_addr = 0; | ||||||||||||||||||||||||||||||||||||
| 203 | int local_addr_length = 0; | ||||||||||||||||||||||||||||||||||||
| 204 | LPSOCKADDR remote_addr = 0; | ||||||||||||||||||||||||||||||||||||
| 205 | int remote_addr_length = 0; | ||||||||||||||||||||||||||||||||||||
| 206 | GetAcceptExSockaddrs(output_buffer, 0, address_length, | ||||||||||||||||||||||||||||||||||||
| 207 | address_length, &local_addr, &local_addr_length, | ||||||||||||||||||||||||||||||||||||
| 208 | &remote_addr, &remote_addr_length); | ||||||||||||||||||||||||||||||||||||
| 209 | if (static_cast<std::size_t>(remote_addr_length) > *addrlen) | ||||||||||||||||||||||||||||||||||||
| 210 | { | ||||||||||||||||||||||||||||||||||||
| 211 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 212 | } | ||||||||||||||||||||||||||||||||||||
| 213 | else | ||||||||||||||||||||||||||||||||||||
| 214 | { | ||||||||||||||||||||||||||||||||||||
| 215 | using namespace std; // For memcpy. | ||||||||||||||||||||||||||||||||||||
| 216 | memcpy(addr, remote_addr, remote_addr_length); | ||||||||||||||||||||||||||||||||||||
| 217 | *addrlen = static_cast<std::size_t>(remote_addr_length); | ||||||||||||||||||||||||||||||||||||
| 218 | } | ||||||||||||||||||||||||||||||||||||
| 219 | } | ||||||||||||||||||||||||||||||||||||
| 220 | |||||||||||||||||||||||||||||||||||||
| 221 | // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname | ||||||||||||||||||||||||||||||||||||
| 222 | // and getpeername will work on the accepted socket. | ||||||||||||||||||||||||||||||||||||
| 223 | SOCKET update_ctx_param = s; | ||||||||||||||||||||||||||||||||||||
| 224 | socket_ops::state_type state = 0; | ||||||||||||||||||||||||||||||||||||
| 225 | socket_ops::setsockopt(new_socket, state, | ||||||||||||||||||||||||||||||||||||
| 226 | SOL_SOCKET1, SO_UPDATE_ACCEPT_CONTEXT, | ||||||||||||||||||||||||||||||||||||
| 227 | &update_ctx_param, sizeof(SOCKET), ec); | ||||||||||||||||||||||||||||||||||||
| 228 | } | ||||||||||||||||||||||||||||||||||||
| 229 | } | ||||||||||||||||||||||||||||||||||||
| 230 | |||||||||||||||||||||||||||||||||||||
| 231 | #else // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 232 | |||||||||||||||||||||||||||||||||||||
| 233 | bool non_blocking_accept(socket_type s, | ||||||||||||||||||||||||||||||||||||
| 234 | state_type state, void* addr, std::size_t* addrlen, | ||||||||||||||||||||||||||||||||||||
| 235 | boost::system::error_code& ec, socket_type& new_socket) | ||||||||||||||||||||||||||||||||||||
| 236 | { | ||||||||||||||||||||||||||||||||||||
| 237 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 238 | { | ||||||||||||||||||||||||||||||||||||
| 239 | // Accept the waiting connection. | ||||||||||||||||||||||||||||||||||||
| 240 | new_socket = socket_ops::accept(s, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||||
| 241 | |||||||||||||||||||||||||||||||||||||
| 242 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 243 | if (new_socket != invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 244 | return true; | ||||||||||||||||||||||||||||||||||||
| 245 | |||||||||||||||||||||||||||||||||||||
| 246 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||||
| 247 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||||
| 248 | continue; | ||||||||||||||||||||||||||||||||||||
| 249 | |||||||||||||||||||||||||||||||||||||
| 250 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 251 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 252 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 253 | { | ||||||||||||||||||||||||||||||||||||
| 254 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||||
| 255 | } | ||||||||||||||||||||||||||||||||||||
| 256 | else if (ec == boost::asio::error::connection_aborted) | ||||||||||||||||||||||||||||||||||||
| 257 | { | ||||||||||||||||||||||||||||||||||||
| 258 | if (state & enable_connection_aborted) | ||||||||||||||||||||||||||||||||||||
| 259 | return true; | ||||||||||||||||||||||||||||||||||||
| 260 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||||
| 261 | } | ||||||||||||||||||||||||||||||||||||
| 262 | #if defined(EPROTO71) | ||||||||||||||||||||||||||||||||||||
| 263 | else if (ec.value() == EPROTO71) | ||||||||||||||||||||||||||||||||||||
| 264 | { | ||||||||||||||||||||||||||||||||||||
| 265 | if (state & enable_connection_aborted) | ||||||||||||||||||||||||||||||||||||
| 266 | return true; | ||||||||||||||||||||||||||||||||||||
| 267 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||||
| 268 | } | ||||||||||||||||||||||||||||||||||||
| 269 | #endif // defined(EPROTO) | ||||||||||||||||||||||||||||||||||||
| 270 | else | ||||||||||||||||||||||||||||||||||||
| 271 | return true; | ||||||||||||||||||||||||||||||||||||
| 272 | |||||||||||||||||||||||||||||||||||||
| 273 | return false; | ||||||||||||||||||||||||||||||||||||
| 274 | } | ||||||||||||||||||||||||||||||||||||
| 275 | } | ||||||||||||||||||||||||||||||||||||
| 276 | |||||||||||||||||||||||||||||||||||||
| 277 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 278 | |||||||||||||||||||||||||||||||||||||
| 279 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||||
| 280 | inline int call_bind(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||||
| 281 | socket_type s, const void* addr, std::size_t addrlen) | ||||||||||||||||||||||||||||||||||||
| 282 | { | ||||||||||||||||||||||||||||||||||||
| 283 | return ::bind(s, static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||||
| 284 | (SockLenType)addrlen); | ||||||||||||||||||||||||||||||||||||
| 285 | } | ||||||||||||||||||||||||||||||||||||
| 286 | |||||||||||||||||||||||||||||||||||||
| 287 | int bind(socket_type s, const void* addr, | ||||||||||||||||||||||||||||||||||||
| 288 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 289 | { | ||||||||||||||||||||||||||||||||||||
| 290 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 291 | { | ||||||||||||||||||||||||||||||||||||
| 292 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 293 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 294 | } | ||||||||||||||||||||||||||||||||||||
| 295 | |||||||||||||||||||||||||||||||||||||
| 296 | int result = call_bind(&msghdr::msg_namelen, s, addr, addrlen); | ||||||||||||||||||||||||||||||||||||
| 297 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 298 | return result; | ||||||||||||||||||||||||||||||||||||
| 299 | } | ||||||||||||||||||||||||||||||||||||
| 300 | |||||||||||||||||||||||||||||||||||||
| 301 | int close(socket_type s, state_type& state, | ||||||||||||||||||||||||||||||||||||
| 302 | bool destruction, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 303 | { | ||||||||||||||||||||||||||||||||||||
| 304 | int result = 0; | ||||||||||||||||||||||||||||||||||||
| 305 | if (s != invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 306 | { | ||||||||||||||||||||||||||||||||||||
| 307 | // We don't want the destructor to block, so set the socket to linger in | ||||||||||||||||||||||||||||||||||||
| 308 | // the background. If the user doesn't like this behaviour then they need | ||||||||||||||||||||||||||||||||||||
| 309 | // to explicitly close the socket. | ||||||||||||||||||||||||||||||||||||
| 310 | if (destruction && (state & user_set_linger)) | ||||||||||||||||||||||||||||||||||||
| 311 | { | ||||||||||||||||||||||||||||||||||||
| 312 | ::linger opt; | ||||||||||||||||||||||||||||||||||||
| 313 | opt.l_onoff = 0; | ||||||||||||||||||||||||||||||||||||
| 314 | opt.l_linger = 0; | ||||||||||||||||||||||||||||||||||||
| 315 | boost::system::error_code ignored_ec; | ||||||||||||||||||||||||||||||||||||
| 316 | socket_ops::setsockopt(s, state, SOL_SOCKET1, | ||||||||||||||||||||||||||||||||||||
| 317 | SO_LINGER13, &opt, sizeof(opt), ignored_ec); | ||||||||||||||||||||||||||||||||||||
| 318 | } | ||||||||||||||||||||||||||||||||||||
| 319 | |||||||||||||||||||||||||||||||||||||
| 320 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 321 | result = ::closesocket(s); | ||||||||||||||||||||||||||||||||||||
| 322 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 323 | result = ::close(s); | ||||||||||||||||||||||||||||||||||||
| 324 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 325 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 326 | |||||||||||||||||||||||||||||||||||||
| 327 | if (result != 0 | ||||||||||||||||||||||||||||||||||||
| 328 | && (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 329 | || ec == boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||||
| 330 | { | ||||||||||||||||||||||||||||||||||||
| 331 | // According to UNIX Network Programming Vol. 1, it is possible for | ||||||||||||||||||||||||||||||||||||
| 332 | // close() to fail with EWOULDBLOCK under certain circumstances. What | ||||||||||||||||||||||||||||||||||||
| 333 | // isn't clear is the state of the descriptor after this error. The one | ||||||||||||||||||||||||||||||||||||
| 334 | // current OS where this behaviour is seen, Windows, says that the socket | ||||||||||||||||||||||||||||||||||||
| 335 | // remains open. Therefore we'll put the descriptor back into blocking | ||||||||||||||||||||||||||||||||||||
| 336 | // mode and have another attempt at closing it. | ||||||||||||||||||||||||||||||||||||
| 337 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 338 | ioctl_arg_type arg = 0; | ||||||||||||||||||||||||||||||||||||
| 339 | ::ioctlsocket(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||||
| 340 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 341 | # if defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||||
| 342 | int flags = ::fcntl(s, F_GETFL3, 0); | ||||||||||||||||||||||||||||||||||||
| 343 | if (flags >= 0) | ||||||||||||||||||||||||||||||||||||
| 344 | ::fcntl(s, F_SETFL4, flags & ~O_NONBLOCK04000); | ||||||||||||||||||||||||||||||||||||
| 345 | # else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||||
| 346 | ioctl_arg_type arg = 0; | ||||||||||||||||||||||||||||||||||||
| 347 | if ((state & possible_dup) == 0) | ||||||||||||||||||||||||||||||||||||
| 348 | { | ||||||||||||||||||||||||||||||||||||
| 349 | result = ::ioctl(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||||
| 350 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 351 | } | ||||||||||||||||||||||||||||||||||||
| 352 | if ((state & possible_dup) != 0 | ||||||||||||||||||||||||||||||||||||
| 353 | # if defined(ENOTTY25) | ||||||||||||||||||||||||||||||||||||
| 354 | || ec.value() == ENOTTY25 | ||||||||||||||||||||||||||||||||||||
| 355 | # endif // defined(ENOTTY) | ||||||||||||||||||||||||||||||||||||
| 356 | # if defined(ENOTCAPABLE) | ||||||||||||||||||||||||||||||||||||
| 357 | || ec.value() == ENOTCAPABLE | ||||||||||||||||||||||||||||||||||||
| 358 | # endif // defined(ENOTCAPABLE) | ||||||||||||||||||||||||||||||||||||
| 359 | ) | ||||||||||||||||||||||||||||||||||||
| 360 | { | ||||||||||||||||||||||||||||||||||||
| 361 | int flags = ::fcntl(s, F_GETFL3, 0); | ||||||||||||||||||||||||||||||||||||
| 362 | if (flags >= 0) | ||||||||||||||||||||||||||||||||||||
| 363 | ::fcntl(s, F_SETFL4, flags & ~O_NONBLOCK04000); | ||||||||||||||||||||||||||||||||||||
| 364 | } | ||||||||||||||||||||||||||||||||||||
| 365 | # endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||||
| 366 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 367 | state &= ~non_blocking; | ||||||||||||||||||||||||||||||||||||
| 368 | |||||||||||||||||||||||||||||||||||||
| 369 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 370 | result = ::closesocket(s); | ||||||||||||||||||||||||||||||||||||
| 371 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 372 | result = ::close(s); | ||||||||||||||||||||||||||||||||||||
| 373 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 374 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 375 | } | ||||||||||||||||||||||||||||||||||||
| 376 | } | ||||||||||||||||||||||||||||||||||||
| 377 | |||||||||||||||||||||||||||||||||||||
| 378 | return result; | ||||||||||||||||||||||||||||||||||||
| 379 | } | ||||||||||||||||||||||||||||||||||||
| 380 | |||||||||||||||||||||||||||||||||||||
| 381 | bool set_user_non_blocking(socket_type s, | ||||||||||||||||||||||||||||||||||||
| 382 | state_type& state, bool value, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 383 | { | ||||||||||||||||||||||||||||||||||||
| 384 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 385 | { | ||||||||||||||||||||||||||||||||||||
| 386 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 387 | return false; | ||||||||||||||||||||||||||||||||||||
| 388 | } | ||||||||||||||||||||||||||||||||||||
| 389 | |||||||||||||||||||||||||||||||||||||
| 390 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 391 | ioctl_arg_type arg = (value ? 1 : 0); | ||||||||||||||||||||||||||||||||||||
| 392 | int result = ::ioctlsocket(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||||
| 393 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 394 | #elif defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||||
| 395 | int result = ::fcntl(s, F_GETFL3, 0); | ||||||||||||||||||||||||||||||||||||
| 396 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 397 | if (result >= 0) | ||||||||||||||||||||||||||||||||||||
| 398 | { | ||||||||||||||||||||||||||||||||||||
| 399 | int flag = (value ? (result | O_NONBLOCK04000) : (result & ~O_NONBLOCK04000)); | ||||||||||||||||||||||||||||||||||||
| 400 | result = (flag != result) ? ::fcntl(s, F_SETFL4, flag) : 0; | ||||||||||||||||||||||||||||||||||||
| 401 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 402 | } | ||||||||||||||||||||||||||||||||||||
| 403 | #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||||
| 404 | ioctl_arg_type arg = (value ? 1 : 0); | ||||||||||||||||||||||||||||||||||||
| 405 | int result = 0; | ||||||||||||||||||||||||||||||||||||
| 406 | if ((state & possible_dup) == 0) | ||||||||||||||||||||||||||||||||||||
| 407 | { | ||||||||||||||||||||||||||||||||||||
| 408 | result = ::ioctl(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||||
| 409 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 410 | } | ||||||||||||||||||||||||||||||||||||
| 411 | if ((state & possible_dup) != 0 | ||||||||||||||||||||||||||||||||||||
| 412 | # if defined(ENOTTY25) | ||||||||||||||||||||||||||||||||||||
| 413 | || ec.value() == ENOTTY25 | ||||||||||||||||||||||||||||||||||||
| 414 | # endif // defined(ENOTTY) | ||||||||||||||||||||||||||||||||||||
| 415 | # if defined(ENOTCAPABLE) | ||||||||||||||||||||||||||||||||||||
| 416 | || ec.value() == ENOTCAPABLE | ||||||||||||||||||||||||||||||||||||
| 417 | # endif // defined(ENOTCAPABLE) | ||||||||||||||||||||||||||||||||||||
| 418 | ) | ||||||||||||||||||||||||||||||||||||
| 419 | { | ||||||||||||||||||||||||||||||||||||
| 420 | result = ::fcntl(s, F_GETFL3, 0); | ||||||||||||||||||||||||||||||||||||
| 421 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 422 | if (result >= 0) | ||||||||||||||||||||||||||||||||||||
| 423 | { | ||||||||||||||||||||||||||||||||||||
| 424 | int flag = (value ? (result | O_NONBLOCK04000) : (result & ~O_NONBLOCK04000)); | ||||||||||||||||||||||||||||||||||||
| 425 | result = (flag != result) ? ::fcntl(s, F_SETFL4, flag) : 0; | ||||||||||||||||||||||||||||||||||||
| 426 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 427 | } | ||||||||||||||||||||||||||||||||||||
| 428 | } | ||||||||||||||||||||||||||||||||||||
| 429 | #endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||||
| 430 | |||||||||||||||||||||||||||||||||||||
| 431 | if (result >= 0) | ||||||||||||||||||||||||||||||||||||
| 432 | { | ||||||||||||||||||||||||||||||||||||
| 433 | if (value) | ||||||||||||||||||||||||||||||||||||
| 434 | state |= user_set_non_blocking; | ||||||||||||||||||||||||||||||||||||
| 435 | else | ||||||||||||||||||||||||||||||||||||
| 436 | { | ||||||||||||||||||||||||||||||||||||
| 437 | // Clearing the user-set non-blocking mode always overrides any | ||||||||||||||||||||||||||||||||||||
| 438 | // internally-set non-blocking flag. Any subsequent asynchronous | ||||||||||||||||||||||||||||||||||||
| 439 | // operations will need to re-enable non-blocking I/O. | ||||||||||||||||||||||||||||||||||||
| 440 | state &= ~(user_set_non_blocking | internal_non_blocking); | ||||||||||||||||||||||||||||||||||||
| 441 | } | ||||||||||||||||||||||||||||||||||||
| 442 | return true; | ||||||||||||||||||||||||||||||||||||
| 443 | } | ||||||||||||||||||||||||||||||||||||
| 444 | |||||||||||||||||||||||||||||||||||||
| 445 | return false; | ||||||||||||||||||||||||||||||||||||
| 446 | } | ||||||||||||||||||||||||||||||||||||
| 447 | |||||||||||||||||||||||||||||||||||||
| 448 | bool set_internal_non_blocking(socket_type s, | ||||||||||||||||||||||||||||||||||||
| 449 | state_type& state, bool value, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 450 | { | ||||||||||||||||||||||||||||||||||||
| 451 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 452 | { | ||||||||||||||||||||||||||||||||||||
| 453 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 454 | return false; | ||||||||||||||||||||||||||||||||||||
| 455 | } | ||||||||||||||||||||||||||||||||||||
| 456 | |||||||||||||||||||||||||||||||||||||
| 457 | if (!value && (state & user_set_non_blocking)) | ||||||||||||||||||||||||||||||||||||
| 458 | { | ||||||||||||||||||||||||||||||||||||
| 459 | // It does not make sense to clear the internal non-blocking flag if the | ||||||||||||||||||||||||||||||||||||
| 460 | // user still wants non-blocking behaviour. Return an error and let the | ||||||||||||||||||||||||||||||||||||
| 461 | // caller figure out whether to update the user-set non-blocking flag. | ||||||||||||||||||||||||||||||||||||
| 462 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 463 | return false; | ||||||||||||||||||||||||||||||||||||
| 464 | } | ||||||||||||||||||||||||||||||||||||
| 465 | |||||||||||||||||||||||||||||||||||||
| 466 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 467 | ioctl_arg_type arg = (value ? 1 : 0); | ||||||||||||||||||||||||||||||||||||
| 468 | int result = ::ioctlsocket(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||||
| 469 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 470 | #elif defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||||
| 471 | int result = ::fcntl(s, F_GETFL3, 0); | ||||||||||||||||||||||||||||||||||||
| 472 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 473 | if (result >= 0) | ||||||||||||||||||||||||||||||||||||
| 474 | { | ||||||||||||||||||||||||||||||||||||
| 475 | int flag = (value ? (result | O_NONBLOCK04000) : (result & ~O_NONBLOCK04000)); | ||||||||||||||||||||||||||||||||||||
| 476 | result = (flag != result) ? ::fcntl(s, F_SETFL4, flag) : 0; | ||||||||||||||||||||||||||||||||||||
| 477 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 478 | } | ||||||||||||||||||||||||||||||||||||
| 479 | #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||||
| 480 | ioctl_arg_type arg = (value ? 1 : 0); | ||||||||||||||||||||||||||||||||||||
| 481 | int result = 0; | ||||||||||||||||||||||||||||||||||||
| 482 | if ((state & possible_dup) == 0) | ||||||||||||||||||||||||||||||||||||
| 483 | { | ||||||||||||||||||||||||||||||||||||
| 484 | result = ::ioctl(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||||
| 485 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 486 | } | ||||||||||||||||||||||||||||||||||||
| 487 | if ((state & possible_dup) != 0 | ||||||||||||||||||||||||||||||||||||
| 488 | # if defined(ENOTTY25) | ||||||||||||||||||||||||||||||||||||
| 489 | || ec.value() == ENOTTY25 | ||||||||||||||||||||||||||||||||||||
| 490 | # endif // defined(ENOTTY) | ||||||||||||||||||||||||||||||||||||
| 491 | # if defined(ENOTCAPABLE) | ||||||||||||||||||||||||||||||||||||
| 492 | || ec.value() == ENOTCAPABLE | ||||||||||||||||||||||||||||||||||||
| 493 | # endif // defined(ENOTCAPABLE) | ||||||||||||||||||||||||||||||||||||
| 494 | ) | ||||||||||||||||||||||||||||||||||||
| 495 | { | ||||||||||||||||||||||||||||||||||||
| 496 | result = ::fcntl(s, F_GETFL3, 0); | ||||||||||||||||||||||||||||||||||||
| 497 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 498 | if (result >= 0) | ||||||||||||||||||||||||||||||||||||
| 499 | { | ||||||||||||||||||||||||||||||||||||
| 500 | int flag = (value ? (result | O_NONBLOCK04000) : (result & ~O_NONBLOCK04000)); | ||||||||||||||||||||||||||||||||||||
| 501 | result = (flag != result) ? ::fcntl(s, F_SETFL4, flag) : 0; | ||||||||||||||||||||||||||||||||||||
| 502 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 503 | } | ||||||||||||||||||||||||||||||||||||
| 504 | } | ||||||||||||||||||||||||||||||||||||
| 505 | #endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||||
| 506 | |||||||||||||||||||||||||||||||||||||
| 507 | if (result >= 0) | ||||||||||||||||||||||||||||||||||||
| 508 | { | ||||||||||||||||||||||||||||||||||||
| 509 | if (value) | ||||||||||||||||||||||||||||||||||||
| 510 | state |= internal_non_blocking; | ||||||||||||||||||||||||||||||||||||
| 511 | else | ||||||||||||||||||||||||||||||||||||
| 512 | state &= ~internal_non_blocking; | ||||||||||||||||||||||||||||||||||||
| 513 | return true; | ||||||||||||||||||||||||||||||||||||
| 514 | } | ||||||||||||||||||||||||||||||||||||
| 515 | |||||||||||||||||||||||||||||||||||||
| 516 | return false; | ||||||||||||||||||||||||||||||||||||
| 517 | } | ||||||||||||||||||||||||||||||||||||
| 518 | |||||||||||||||||||||||||||||||||||||
| 519 | int shutdown(socket_type s, int what, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 520 | { | ||||||||||||||||||||||||||||||||||||
| 521 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 522 | { | ||||||||||||||||||||||||||||||||||||
| 523 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 524 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 525 | } | ||||||||||||||||||||||||||||||||||||
| 526 | |||||||||||||||||||||||||||||||||||||
| 527 | int result = ::shutdown(s, what); | ||||||||||||||||||||||||||||||||||||
| 528 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 529 | return result; | ||||||||||||||||||||||||||||||||||||
| 530 | } | ||||||||||||||||||||||||||||||||||||
| 531 | |||||||||||||||||||||||||||||||||||||
| 532 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||||
| 533 | inline int call_connect(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||||
| 534 | socket_type s, const void* addr, std::size_t addrlen) | ||||||||||||||||||||||||||||||||||||
| 535 | { | ||||||||||||||||||||||||||||||||||||
| 536 | return ::connect(s, static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||||
| 537 | (SockLenType)addrlen); | ||||||||||||||||||||||||||||||||||||
| 538 | } | ||||||||||||||||||||||||||||||||||||
| 539 | |||||||||||||||||||||||||||||||||||||
| 540 | int connect(socket_type s, const void* addr, | ||||||||||||||||||||||||||||||||||||
| 541 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 542 | { | ||||||||||||||||||||||||||||||||||||
| 543 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 544 | { | ||||||||||||||||||||||||||||||||||||
| 545 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 546 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 547 | } | ||||||||||||||||||||||||||||||||||||
| 548 | |||||||||||||||||||||||||||||||||||||
| 549 | int result = call_connect(&msghdr::msg_namelen, s, addr, addrlen); | ||||||||||||||||||||||||||||||||||||
| 550 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 551 | #if defined(__linux__1) | ||||||||||||||||||||||||||||||||||||
| 552 | if (result != 0 && ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 553 | { | ||||||||||||||||||||||||||||||||||||
| 554 | if (static_cast<const socket_addr_type*>(addr)->sa_family == AF_UNIX1) | ||||||||||||||||||||||||||||||||||||
| 555 | ec = boost::asio::error::in_progress; | ||||||||||||||||||||||||||||||||||||
| 556 | else | ||||||||||||||||||||||||||||||||||||
| 557 | ec = boost::asio::error::no_buffer_space; | ||||||||||||||||||||||||||||||||||||
| 558 | } | ||||||||||||||||||||||||||||||||||||
| 559 | #endif // defined(__linux__) | ||||||||||||||||||||||||||||||||||||
| 560 | return result; | ||||||||||||||||||||||||||||||||||||
| 561 | } | ||||||||||||||||||||||||||||||||||||
| 562 | |||||||||||||||||||||||||||||||||||||
| 563 | void sync_connect(socket_type s, const void* addr, | ||||||||||||||||||||||||||||||||||||
| 564 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 565 | { | ||||||||||||||||||||||||||||||||||||
| 566 | // Perform the connect operation. | ||||||||||||||||||||||||||||||||||||
| 567 | socket_ops::connect(s, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||||
| 568 | if (ec != boost::asio::error::in_progress | ||||||||||||||||||||||||||||||||||||
| 569 | && ec != boost::asio::error::would_block) | ||||||||||||||||||||||||||||||||||||
| 570 | { | ||||||||||||||||||||||||||||||||||||
| 571 | // The connect operation finished immediately. | ||||||||||||||||||||||||||||||||||||
| 572 | return; | ||||||||||||||||||||||||||||||||||||
| 573 | } | ||||||||||||||||||||||||||||||||||||
| 574 | |||||||||||||||||||||||||||||||||||||
| 575 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||||
| 576 | if (socket_ops::poll_connect(s, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||||
| 577 | return; | ||||||||||||||||||||||||||||||||||||
| 578 | |||||||||||||||||||||||||||||||||||||
| 579 | // Get the error code from the connect operation. | ||||||||||||||||||||||||||||||||||||
| 580 | int connect_error = 0; | ||||||||||||||||||||||||||||||||||||
| 581 | size_t connect_error_len = sizeof(connect_error); | ||||||||||||||||||||||||||||||||||||
| 582 | if (socket_ops::getsockopt(s, 0, SOL_SOCKET1, SO_ERROR4, | ||||||||||||||||||||||||||||||||||||
| 583 | &connect_error, &connect_error_len, ec) == socket_error_retval) | ||||||||||||||||||||||||||||||||||||
| 584 | return; | ||||||||||||||||||||||||||||||||||||
| 585 | |||||||||||||||||||||||||||||||||||||
| 586 | // Return the result of the connect operation. | ||||||||||||||||||||||||||||||||||||
| 587 | ec = boost::system::error_code(connect_error, | ||||||||||||||||||||||||||||||||||||
| 588 | boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||||
| 589 | } | ||||||||||||||||||||||||||||||||||||
| 590 | |||||||||||||||||||||||||||||||||||||
| 591 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 592 | |||||||||||||||||||||||||||||||||||||
| 593 | void complete_iocp_connect(socket_type s, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 594 | { | ||||||||||||||||||||||||||||||||||||
| 595 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||||
| 596 | switch (ec.value()) | ||||||||||||||||||||||||||||||||||||
| 597 | { | ||||||||||||||||||||||||||||||||||||
| 598 | case ERROR_CONNECTION_REFUSED: | ||||||||||||||||||||||||||||||||||||
| 599 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 600 | break; | ||||||||||||||||||||||||||||||||||||
| 601 | case ERROR_NETWORK_UNREACHABLE: | ||||||||||||||||||||||||||||||||||||
| 602 | ec = boost::asio::error::network_unreachable; | ||||||||||||||||||||||||||||||||||||
| 603 | break; | ||||||||||||||||||||||||||||||||||||
| 604 | case ERROR_HOST_UNREACHABLE: | ||||||||||||||||||||||||||||||||||||
| 605 | ec = boost::asio::error::host_unreachable; | ||||||||||||||||||||||||||||||||||||
| 606 | break; | ||||||||||||||||||||||||||||||||||||
| 607 | case ERROR_SEM_TIMEOUT: | ||||||||||||||||||||||||||||||||||||
| 608 | ec = boost::asio::error::timed_out; | ||||||||||||||||||||||||||||||||||||
| 609 | break; | ||||||||||||||||||||||||||||||||||||
| 610 | default: | ||||||||||||||||||||||||||||||||||||
| 611 | break; | ||||||||||||||||||||||||||||||||||||
| 612 | } | ||||||||||||||||||||||||||||||||||||
| 613 | |||||||||||||||||||||||||||||||||||||
| 614 | if (!ec) | ||||||||||||||||||||||||||||||||||||
| 615 | { | ||||||||||||||||||||||||||||||||||||
| 616 | // Need to set the SO_UPDATE_CONNECT_CONTEXT option so that getsockname | ||||||||||||||||||||||||||||||||||||
| 617 | // and getpeername will work on the connected socket. | ||||||||||||||||||||||||||||||||||||
| 618 | socket_ops::state_type state = 0; | ||||||||||||||||||||||||||||||||||||
| 619 | const int so_update_connect_context = 0x7010; | ||||||||||||||||||||||||||||||||||||
| 620 | socket_ops::setsockopt(s, state, SOL_SOCKET1, | ||||||||||||||||||||||||||||||||||||
| 621 | so_update_connect_context, 0, 0, ec); | ||||||||||||||||||||||||||||||||||||
| 622 | } | ||||||||||||||||||||||||||||||||||||
| 623 | } | ||||||||||||||||||||||||||||||||||||
| 624 | |||||||||||||||||||||||||||||||||||||
| 625 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 626 | |||||||||||||||||||||||||||||||||||||
| 627 | bool non_blocking_connect(socket_type s, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 628 | { | ||||||||||||||||||||||||||||||||||||
| 629 | // Check if the connect operation has finished. This is required since we may | ||||||||||||||||||||||||||||||||||||
| 630 | // get spurious readiness notifications from the reactor. | ||||||||||||||||||||||||||||||||||||
| 631 | #if defined(BOOST_ASIO_WINDOWS) \ | ||||||||||||||||||||||||||||||||||||
| 632 | || defined(__CYGWIN__) \ | ||||||||||||||||||||||||||||||||||||
| 633 | || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 634 | fd_set write_fds; | ||||||||||||||||||||||||||||||||||||
| 635 | FD_ZERO(&write_fds)do { unsigned int __i; fd_set *__arr = (&write_fds); for ( __i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i ) ((__arr)->fds_bits)[__i] = 0; } while (0); | ||||||||||||||||||||||||||||||||||||
| 636 | FD_SET(s, &write_fds)((void) (((&write_fds)->fds_bits)[((s) / (8 * (int) sizeof (__fd_mask)))] |= ((__fd_mask) (1UL << ((s) % (8 * (int ) sizeof (__fd_mask))))))); | ||||||||||||||||||||||||||||||||||||
| 637 | fd_set except_fds; | ||||||||||||||||||||||||||||||||||||
| 638 | FD_ZERO(&except_fds)do { unsigned int __i; fd_set *__arr = (&except_fds); for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i ) ((__arr)->fds_bits)[__i] = 0; } while (0); | ||||||||||||||||||||||||||||||||||||
| 639 | FD_SET(s, &except_fds)((void) (((&except_fds)->fds_bits)[((s) / (8 * (int) sizeof (__fd_mask)))] |= ((__fd_mask) (1UL << ((s) % (8 * (int ) sizeof (__fd_mask))))))); | ||||||||||||||||||||||||||||||||||||
| 640 | timeval zero_timeout; | ||||||||||||||||||||||||||||||||||||
| 641 | zero_timeout.tv_sec = 0; | ||||||||||||||||||||||||||||||||||||
| 642 | zero_timeout.tv_usec = 0; | ||||||||||||||||||||||||||||||||||||
| 643 | int ready = ::select(s + 1, 0, &write_fds, &except_fds, &zero_timeout); | ||||||||||||||||||||||||||||||||||||
| 644 | #else // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||||
| 645 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 646 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 647 | pollfd fds; | ||||||||||||||||||||||||||||||||||||
| 648 | fds.fd = s; | ||||||||||||||||||||||||||||||||||||
| 649 | fds.events = POLLOUT0x004; | ||||||||||||||||||||||||||||||||||||
| 650 | fds.revents = 0; | ||||||||||||||||||||||||||||||||||||
| 651 | int ready = ::poll(&fds, 1, 0); | ||||||||||||||||||||||||||||||||||||
| 652 | #endif // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||||
| 653 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 654 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 655 | if (ready == 0) | ||||||||||||||||||||||||||||||||||||
| 656 | { | ||||||||||||||||||||||||||||||||||||
| 657 | // The asynchronous connect operation is still in progress. | ||||||||||||||||||||||||||||||||||||
| 658 | return false; | ||||||||||||||||||||||||||||||||||||
| 659 | } | ||||||||||||||||||||||||||||||||||||
| 660 | |||||||||||||||||||||||||||||||||||||
| 661 | // Get the error code from the connect operation. | ||||||||||||||||||||||||||||||||||||
| 662 | int connect_error = 0; | ||||||||||||||||||||||||||||||||||||
| 663 | size_t connect_error_len = sizeof(connect_error); | ||||||||||||||||||||||||||||||||||||
| 664 | if (socket_ops::getsockopt(s, 0, SOL_SOCKET1, SO_ERROR4, | ||||||||||||||||||||||||||||||||||||
| 665 | &connect_error, &connect_error_len, ec) == 0) | ||||||||||||||||||||||||||||||||||||
| 666 | { | ||||||||||||||||||||||||||||||||||||
| 667 | if (connect_error) | ||||||||||||||||||||||||||||||||||||
| 668 | { | ||||||||||||||||||||||||||||||||||||
| 669 | ec = boost::system::error_code(connect_error, | ||||||||||||||||||||||||||||||||||||
| 670 | boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||||
| 671 | } | ||||||||||||||||||||||||||||||||||||
| 672 | else | ||||||||||||||||||||||||||||||||||||
| 673 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 674 | } | ||||||||||||||||||||||||||||||||||||
| 675 | |||||||||||||||||||||||||||||||||||||
| 676 | return true; | ||||||||||||||||||||||||||||||||||||
| 677 | } | ||||||||||||||||||||||||||||||||||||
| 678 | |||||||||||||||||||||||||||||||||||||
| 679 | int socketpair(int af, int type, int protocol, | ||||||||||||||||||||||||||||||||||||
| 680 | socket_type sv[2], boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 681 | { | ||||||||||||||||||||||||||||||||||||
| 682 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 683 | (void)(af); | ||||||||||||||||||||||||||||||||||||
| 684 | (void)(type); | ||||||||||||||||||||||||||||||||||||
| 685 | (void)(protocol); | ||||||||||||||||||||||||||||||||||||
| 686 | (void)(sv); | ||||||||||||||||||||||||||||||||||||
| 687 | ec = boost::asio::error::operation_not_supported; | ||||||||||||||||||||||||||||||||||||
| 688 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 689 | #else | ||||||||||||||||||||||||||||||||||||
| 690 | int result = ::socketpair(af, type, protocol, sv); | ||||||||||||||||||||||||||||||||||||
| 691 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 692 | return result; | ||||||||||||||||||||||||||||||||||||
| 693 | #endif | ||||||||||||||||||||||||||||||||||||
| 694 | } | ||||||||||||||||||||||||||||||||||||
| 695 | |||||||||||||||||||||||||||||||||||||
| 696 | bool sockatmark(socket_type s, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 697 | { | ||||||||||||||||||||||||||||||||||||
| 698 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 699 | { | ||||||||||||||||||||||||||||||||||||
| 700 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 701 | return false; | ||||||||||||||||||||||||||||||||||||
| 702 | } | ||||||||||||||||||||||||||||||||||||
| 703 | |||||||||||||||||||||||||||||||||||||
| 704 | #if defined(SIOCATMARK0x8905) | ||||||||||||||||||||||||||||||||||||
| 705 | ioctl_arg_type value = 0; | ||||||||||||||||||||||||||||||||||||
| 706 | # if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 707 | int result = ::ioctlsocket(s, SIOCATMARK0x8905, &value); | ||||||||||||||||||||||||||||||||||||
| 708 | # else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 709 | int result = ::ioctl(s, SIOCATMARK0x8905, &value); | ||||||||||||||||||||||||||||||||||||
| 710 | # endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 711 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 712 | # if defined(ENOTTY25) | ||||||||||||||||||||||||||||||||||||
| 713 | if (ec.value() == ENOTTY25) | ||||||||||||||||||||||||||||||||||||
| 714 | ec = boost::asio::error::not_socket; | ||||||||||||||||||||||||||||||||||||
| 715 | # endif // defined(ENOTTY) | ||||||||||||||||||||||||||||||||||||
| 716 | #else // defined(SIOCATMARK) | ||||||||||||||||||||||||||||||||||||
| 717 | int value = ::sockatmark(s); | ||||||||||||||||||||||||||||||||||||
| 718 | get_last_error(ec, value < 0); | ||||||||||||||||||||||||||||||||||||
| 719 | #endif // defined(SIOCATMARK) | ||||||||||||||||||||||||||||||||||||
| 720 | |||||||||||||||||||||||||||||||||||||
| 721 | return ec ? false : value != 0; | ||||||||||||||||||||||||||||||||||||
| 722 | } | ||||||||||||||||||||||||||||||||||||
| 723 | |||||||||||||||||||||||||||||||||||||
| 724 | size_t available(socket_type s, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 725 | { | ||||||||||||||||||||||||||||||||||||
| 726 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 727 | { | ||||||||||||||||||||||||||||||||||||
| 728 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 729 | return 0; | ||||||||||||||||||||||||||||||||||||
| 730 | } | ||||||||||||||||||||||||||||||||||||
| 731 | |||||||||||||||||||||||||||||||||||||
| 732 | ioctl_arg_type value = 0; | ||||||||||||||||||||||||||||||||||||
| 733 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 734 | int result = ::ioctlsocket(s, FIONREAD0x541B, &value); | ||||||||||||||||||||||||||||||||||||
| 735 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 736 | int result = ::ioctl(s, FIONREAD0x541B, &value); | ||||||||||||||||||||||||||||||||||||
| 737 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 738 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 739 | #if defined(ENOTTY25) | ||||||||||||||||||||||||||||||||||||
| 740 | if (ec.value() == ENOTTY25) | ||||||||||||||||||||||||||||||||||||
| 741 | ec = boost::asio::error::not_socket; | ||||||||||||||||||||||||||||||||||||
| 742 | #endif // defined(ENOTTY) | ||||||||||||||||||||||||||||||||||||
| 743 | |||||||||||||||||||||||||||||||||||||
| 744 | return ec ? static_cast<size_t>(0) : static_cast<size_t>(value); | ||||||||||||||||||||||||||||||||||||
| 745 | } | ||||||||||||||||||||||||||||||||||||
| 746 | |||||||||||||||||||||||||||||||||||||
| 747 | int listen(socket_type s, int backlog, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 748 | { | ||||||||||||||||||||||||||||||||||||
| 749 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 750 | { | ||||||||||||||||||||||||||||||||||||
| 751 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 752 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 753 | } | ||||||||||||||||||||||||||||||||||||
| 754 | |||||||||||||||||||||||||||||||||||||
| 755 | int result = ::listen(s, backlog); | ||||||||||||||||||||||||||||||||||||
| 756 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 757 | return result; | ||||||||||||||||||||||||||||||||||||
| 758 | } | ||||||||||||||||||||||||||||||||||||
| 759 | |||||||||||||||||||||||||||||||||||||
| 760 | inline void init_buf_iov_base(void*& base, void* addr) | ||||||||||||||||||||||||||||||||||||
| 761 | { | ||||||||||||||||||||||||||||||||||||
| 762 | base = addr; | ||||||||||||||||||||||||||||||||||||
| 763 | } | ||||||||||||||||||||||||||||||||||||
| 764 | |||||||||||||||||||||||||||||||||||||
| 765 | template <typename T> | ||||||||||||||||||||||||||||||||||||
| 766 | inline void init_buf_iov_base(T& base, void* addr) | ||||||||||||||||||||||||||||||||||||
| 767 | { | ||||||||||||||||||||||||||||||||||||
| 768 | base = static_cast<T>(addr); | ||||||||||||||||||||||||||||||||||||
| 769 | } | ||||||||||||||||||||||||||||||||||||
| 770 | |||||||||||||||||||||||||||||||||||||
| 771 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 772 | typedef WSABUF buf; | ||||||||||||||||||||||||||||||||||||
| 773 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 774 | typedef iovec buf; | ||||||||||||||||||||||||||||||||||||
| 775 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 776 | |||||||||||||||||||||||||||||||||||||
| 777 | void init_buf(buf& b, void* data, size_t size) | ||||||||||||||||||||||||||||||||||||
| 778 | { | ||||||||||||||||||||||||||||||||||||
| 779 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 780 | b.buf = static_cast<char*>(data); | ||||||||||||||||||||||||||||||||||||
| 781 | b.len = static_cast<u_long>(size); | ||||||||||||||||||||||||||||||||||||
| 782 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 783 | init_buf_iov_base(b.iov_base, data); | ||||||||||||||||||||||||||||||||||||
| 784 | b.iov_len = size; | ||||||||||||||||||||||||||||||||||||
| 785 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 786 | } | ||||||||||||||||||||||||||||||||||||
| 787 | |||||||||||||||||||||||||||||||||||||
| 788 | void init_buf(buf& b, const void* data, size_t size) | ||||||||||||||||||||||||||||||||||||
| 789 | { | ||||||||||||||||||||||||||||||||||||
| 790 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 791 | b.buf = static_cast<char*>(const_cast<void*>(data)); | ||||||||||||||||||||||||||||||||||||
| 792 | b.len = static_cast<u_long>(size); | ||||||||||||||||||||||||||||||||||||
| 793 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 794 | init_buf_iov_base(b.iov_base, const_cast<void*>(data)); | ||||||||||||||||||||||||||||||||||||
| 795 | b.iov_len = size; | ||||||||||||||||||||||||||||||||||||
| 796 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 797 | } | ||||||||||||||||||||||||||||||||||||
| 798 | |||||||||||||||||||||||||||||||||||||
| 799 | inline void init_msghdr_msg_name(void*& name, void* addr) | ||||||||||||||||||||||||||||||||||||
| 800 | { | ||||||||||||||||||||||||||||||||||||
| 801 | name = static_cast<socket_addr_type*>(addr); | ||||||||||||||||||||||||||||||||||||
| 802 | } | ||||||||||||||||||||||||||||||||||||
| 803 | |||||||||||||||||||||||||||||||||||||
| 804 | inline void init_msghdr_msg_name(void*& name, const socket_addr_type* addr) | ||||||||||||||||||||||||||||||||||||
| 805 | { | ||||||||||||||||||||||||||||||||||||
| 806 | name = const_cast<socket_addr_type*>(addr); | ||||||||||||||||||||||||||||||||||||
| 807 | } | ||||||||||||||||||||||||||||||||||||
| 808 | |||||||||||||||||||||||||||||||||||||
| 809 | template <typename T> | ||||||||||||||||||||||||||||||||||||
| 810 | inline void init_msghdr_msg_name(T& name, void* addr) | ||||||||||||||||||||||||||||||||||||
| 811 | { | ||||||||||||||||||||||||||||||||||||
| 812 | name = static_cast<T>(addr); | ||||||||||||||||||||||||||||||||||||
| 813 | } | ||||||||||||||||||||||||||||||||||||
| 814 | |||||||||||||||||||||||||||||||||||||
| 815 | template <typename T> | ||||||||||||||||||||||||||||||||||||
| 816 | inline void init_msghdr_msg_name(T& name, const void* addr) | ||||||||||||||||||||||||||||||||||||
| 817 | { | ||||||||||||||||||||||||||||||||||||
| 818 | name = static_cast<T>(const_cast<void*>(addr)); | ||||||||||||||||||||||||||||||||||||
| 819 | } | ||||||||||||||||||||||||||||||||||||
| 820 | |||||||||||||||||||||||||||||||||||||
| 821 | signed_size_type recv(socket_type s, buf* bufs, size_t count, | ||||||||||||||||||||||||||||||||||||
| 822 | int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 823 | { | ||||||||||||||||||||||||||||||||||||
| 824 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 825 | // Receive some data. | ||||||||||||||||||||||||||||||||||||
| 826 | DWORD recv_buf_count = static_cast<DWORD>(count); | ||||||||||||||||||||||||||||||||||||
| 827 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 828 | DWORD recv_flags = flags; | ||||||||||||||||||||||||||||||||||||
| 829 | int result = ::WSARecv(s, bufs, recv_buf_count, | ||||||||||||||||||||||||||||||||||||
| 830 | &bytes_transferred, &recv_flags, 0, 0); | ||||||||||||||||||||||||||||||||||||
| 831 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 832 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 833 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 834 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 835 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 836 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||||
| 837 | result = 0; | ||||||||||||||||||||||||||||||||||||
| 838 | if (result != 0) | ||||||||||||||||||||||||||||||||||||
| 839 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 840 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 841 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||||
| 842 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 843 | msghdr msg = msghdr(); | ||||||||||||||||||||||||||||||||||||
| 844 | msg.msg_iov = bufs; | ||||||||||||||||||||||||||||||||||||
| 845 | msg.msg_iovlen = static_cast<int>(count); | ||||||||||||||||||||||||||||||||||||
| 846 | signed_size_type result = ::recvmsg(s, &msg, flags); | ||||||||||||||||||||||||||||||||||||
| 847 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 848 | return result; | ||||||||||||||||||||||||||||||||||||
| 849 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 850 | } | ||||||||||||||||||||||||||||||||||||
| 851 | |||||||||||||||||||||||||||||||||||||
| 852 | signed_size_type recv1(socket_type s, void* data, size_t size, | ||||||||||||||||||||||||||||||||||||
| 853 | int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 854 | { | ||||||||||||||||||||||||||||||||||||
| 855 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 856 | // Receive some data. | ||||||||||||||||||||||||||||||||||||
| 857 | WSABUF buf; | ||||||||||||||||||||||||||||||||||||
| 858 | buf.buf = const_cast<char*>(static_cast<const char*>(data)); | ||||||||||||||||||||||||||||||||||||
| 859 | buf.len = static_cast<ULONG>(size); | ||||||||||||||||||||||||||||||||||||
| 860 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 861 | DWORD recv_flags = flags; | ||||||||||||||||||||||||||||||||||||
| 862 | int result = ::WSARecv(s, &buf, 1, | ||||||||||||||||||||||||||||||||||||
| 863 | &bytes_transferred, &recv_flags, 0, 0); | ||||||||||||||||||||||||||||||||||||
| 864 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 865 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 866 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 867 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 868 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 869 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||||
| 870 | result = 0; | ||||||||||||||||||||||||||||||||||||
| 871 | if (result != 0) | ||||||||||||||||||||||||||||||||||||
| 872 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 873 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 874 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||||
| 875 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 876 | signed_size_type result = ::recv(s, static_cast<char*>(data), size, flags); | ||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||
| 877 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 878 | return result; | ||||||||||||||||||||||||||||||||||||
| 879 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 880 | } | ||||||||||||||||||||||||||||||||||||
| 881 | |||||||||||||||||||||||||||||||||||||
| 882 | size_t sync_recv(socket_type s, state_type state, buf* bufs, | ||||||||||||||||||||||||||||||||||||
| 883 | size_t count, int flags, bool all_empty, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 884 | { | ||||||||||||||||||||||||||||||||||||
| 885 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 886 | { | ||||||||||||||||||||||||||||||||||||
| 887 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 888 | return 0; | ||||||||||||||||||||||||||||||||||||
| 889 | } | ||||||||||||||||||||||||||||||||||||
| 890 | |||||||||||||||||||||||||||||||||||||
| 891 | // A request to read 0 bytes on a stream is a no-op. | ||||||||||||||||||||||||||||||||||||
| 892 | if (all_empty && (state & stream_oriented)) | ||||||||||||||||||||||||||||||||||||
| 893 | { | ||||||||||||||||||||||||||||||||||||
| 894 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 895 | return 0; | ||||||||||||||||||||||||||||||||||||
| 896 | } | ||||||||||||||||||||||||||||||||||||
| 897 | |||||||||||||||||||||||||||||||||||||
| 898 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 899 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 900 | { | ||||||||||||||||||||||||||||||||||||
| 901 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||||
| 902 | signed_size_type bytes = socket_ops::recv(s, bufs, count, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 903 | |||||||||||||||||||||||||||||||||||||
| 904 | // Check for EOF. | ||||||||||||||||||||||||||||||||||||
| 905 | if ((state & stream_oriented) && bytes == 0) | ||||||||||||||||||||||||||||||||||||
| 906 | { | ||||||||||||||||||||||||||||||||||||
| 907 | ec = boost::asio::error::eof; | ||||||||||||||||||||||||||||||||||||
| 908 | return 0; | ||||||||||||||||||||||||||||||||||||
| 909 | } | ||||||||||||||||||||||||||||||||||||
| 910 | |||||||||||||||||||||||||||||||||||||
| 911 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 912 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 913 | return bytes; | ||||||||||||||||||||||||||||||||||||
| 914 | |||||||||||||||||||||||||||||||||||||
| 915 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 916 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 917 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 918 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||||
| 919 | return 0; | ||||||||||||||||||||||||||||||||||||
| 920 | |||||||||||||||||||||||||||||||||||||
| 921 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||||
| 922 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||||
| 923 | return 0; | ||||||||||||||||||||||||||||||||||||
| 924 | } | ||||||||||||||||||||||||||||||||||||
| 925 | } | ||||||||||||||||||||||||||||||||||||
| 926 | |||||||||||||||||||||||||||||||||||||
| 927 | size_t sync_recv1(socket_type s, state_type state, void* data, | ||||||||||||||||||||||||||||||||||||
| 928 | size_t size, int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 929 | { | ||||||||||||||||||||||||||||||||||||
| 930 | if (s
| ||||||||||||||||||||||||||||||||||||
| 931 | { | ||||||||||||||||||||||||||||||||||||
| 932 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 933 | return 0; | ||||||||||||||||||||||||||||||||||||
| 934 | } | ||||||||||||||||||||||||||||||||||||
| 935 | |||||||||||||||||||||||||||||||||||||
| 936 | // A request to read 0 bytes on a stream is a no-op. | ||||||||||||||||||||||||||||||||||||
| 937 | if (size
| ||||||||||||||||||||||||||||||||||||
| 938 | { | ||||||||||||||||||||||||||||||||||||
| 939 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 940 | return 0; | ||||||||||||||||||||||||||||||||||||
| 941 | } | ||||||||||||||||||||||||||||||||||||
| 942 | |||||||||||||||||||||||||||||||||||||
| 943 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 944 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 945 | { | ||||||||||||||||||||||||||||||||||||
| 946 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||||
| 947 | signed_size_type bytes = socket_ops::recv1(s, data, size, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 948 | |||||||||||||||||||||||||||||||||||||
| 949 | // Check for EOF. | ||||||||||||||||||||||||||||||||||||
| 950 | if ((state & stream_oriented) && bytes == 0) | ||||||||||||||||||||||||||||||||||||
| 951 | { | ||||||||||||||||||||||||||||||||||||
| 952 | ec = boost::asio::error::eof; | ||||||||||||||||||||||||||||||||||||
| 953 | return 0; | ||||||||||||||||||||||||||||||||||||
| 954 | } | ||||||||||||||||||||||||||||||||||||
| 955 | |||||||||||||||||||||||||||||||||||||
| 956 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 957 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 958 | return bytes; | ||||||||||||||||||||||||||||||||||||
| 959 | |||||||||||||||||||||||||||||||||||||
| 960 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 961 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 962 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 963 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||||
| 964 | return 0; | ||||||||||||||||||||||||||||||||||||
| 965 | |||||||||||||||||||||||||||||||||||||
| 966 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||||
| 967 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||||
| 968 | return 0; | ||||||||||||||||||||||||||||||||||||
| 969 | } | ||||||||||||||||||||||||||||||||||||
| 970 | } | ||||||||||||||||||||||||||||||||||||
| 971 | |||||||||||||||||||||||||||||||||||||
| 972 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 973 | |||||||||||||||||||||||||||||||||||||
| 974 | void complete_iocp_recv(state_type state, | ||||||||||||||||||||||||||||||||||||
| 975 | const weak_cancel_token_type& cancel_token, bool all_empty, | ||||||||||||||||||||||||||||||||||||
| 976 | boost::system::error_code& ec, size_t bytes_transferred) | ||||||||||||||||||||||||||||||||||||
| 977 | { | ||||||||||||||||||||||||||||||||||||
| 978 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||||
| 979 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 980 | { | ||||||||||||||||||||||||||||||||||||
| 981 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||||
| 982 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||||
| 983 | else | ||||||||||||||||||||||||||||||||||||
| 984 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 985 | } | ||||||||||||||||||||||||||||||||||||
| 986 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 987 | { | ||||||||||||||||||||||||||||||||||||
| 988 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 989 | } | ||||||||||||||||||||||||||||||||||||
| 990 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||||
| 991 | { | ||||||||||||||||||||||||||||||||||||
| 992 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 993 | } | ||||||||||||||||||||||||||||||||||||
| 994 | |||||||||||||||||||||||||||||||||||||
| 995 | // Check for connection closed. | ||||||||||||||||||||||||||||||||||||
| 996 | else if (!ec && bytes_transferred == 0 | ||||||||||||||||||||||||||||||||||||
| 997 | && (state & stream_oriented) != 0 | ||||||||||||||||||||||||||||||||||||
| 998 | && !all_empty) | ||||||||||||||||||||||||||||||||||||
| 999 | { | ||||||||||||||||||||||||||||||||||||
| 1000 | ec = boost::asio::error::eof; | ||||||||||||||||||||||||||||||||||||
| 1001 | } | ||||||||||||||||||||||||||||||||||||
| 1002 | } | ||||||||||||||||||||||||||||||||||||
| 1003 | |||||||||||||||||||||||||||||||||||||
| 1004 | #else // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1005 | |||||||||||||||||||||||||||||||||||||
| 1006 | bool non_blocking_recv(socket_type s, | ||||||||||||||||||||||||||||||||||||
| 1007 | buf* bufs, size_t count, int flags, bool is_stream, | ||||||||||||||||||||||||||||||||||||
| 1008 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||||
| 1009 | { | ||||||||||||||||||||||||||||||||||||
| 1010 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1011 | { | ||||||||||||||||||||||||||||||||||||
| 1012 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 1013 | signed_size_type bytes = socket_ops::recv(s, bufs, count, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 1014 | |||||||||||||||||||||||||||||||||||||
| 1015 | // Check for end of stream. | ||||||||||||||||||||||||||||||||||||
| 1016 | if (is_stream && bytes == 0) | ||||||||||||||||||||||||||||||||||||
| 1017 | { | ||||||||||||||||||||||||||||||||||||
| 1018 | ec = boost::asio::error::eof; | ||||||||||||||||||||||||||||||||||||
| 1019 | return true; | ||||||||||||||||||||||||||||||||||||
| 1020 | } | ||||||||||||||||||||||||||||||||||||
| 1021 | |||||||||||||||||||||||||||||||||||||
| 1022 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1023 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1024 | { | ||||||||||||||||||||||||||||||||||||
| 1025 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||||
| 1026 | return true; | ||||||||||||||||||||||||||||||||||||
| 1027 | } | ||||||||||||||||||||||||||||||||||||
| 1028 | |||||||||||||||||||||||||||||||||||||
| 1029 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||||
| 1030 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||||
| 1031 | continue; | ||||||||||||||||||||||||||||||||||||
| 1032 | |||||||||||||||||||||||||||||||||||||
| 1033 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||||
| 1034 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1035 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 1036 | return false; | ||||||||||||||||||||||||||||||||||||
| 1037 | |||||||||||||||||||||||||||||||||||||
| 1038 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1039 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1040 | return true; | ||||||||||||||||||||||||||||||||||||
| 1041 | } | ||||||||||||||||||||||||||||||||||||
| 1042 | } | ||||||||||||||||||||||||||||||||||||
| 1043 | |||||||||||||||||||||||||||||||||||||
| 1044 | bool non_blocking_recv1(socket_type s, | ||||||||||||||||||||||||||||||||||||
| 1045 | void* data, size_t size, int flags, bool is_stream, | ||||||||||||||||||||||||||||||||||||
| 1046 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||||
| 1047 | { | ||||||||||||||||||||||||||||||||||||
| 1048 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1049 | { | ||||||||||||||||||||||||||||||||||||
| 1050 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 1051 | signed_size_type bytes = socket_ops::recv1(s, data, size, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 1052 | |||||||||||||||||||||||||||||||||||||
| 1053 | // Check for end of stream. | ||||||||||||||||||||||||||||||||||||
| 1054 | if (is_stream && bytes == 0) | ||||||||||||||||||||||||||||||||||||
| 1055 | { | ||||||||||||||||||||||||||||||||||||
| 1056 | ec = boost::asio::error::eof; | ||||||||||||||||||||||||||||||||||||
| 1057 | return true; | ||||||||||||||||||||||||||||||||||||
| 1058 | } | ||||||||||||||||||||||||||||||||||||
| 1059 | |||||||||||||||||||||||||||||||||||||
| 1060 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1061 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1062 | { | ||||||||||||||||||||||||||||||||||||
| 1063 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||||
| 1064 | return true; | ||||||||||||||||||||||||||||||||||||
| 1065 | } | ||||||||||||||||||||||||||||||||||||
| 1066 | |||||||||||||||||||||||||||||||||||||
| 1067 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||||
| 1068 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||||
| 1069 | continue; | ||||||||||||||||||||||||||||||||||||
| 1070 | |||||||||||||||||||||||||||||||||||||
| 1071 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||||
| 1072 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1073 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 1074 | return false; | ||||||||||||||||||||||||||||||||||||
| 1075 | |||||||||||||||||||||||||||||||||||||
| 1076 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1077 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1078 | return true; | ||||||||||||||||||||||||||||||||||||
| 1079 | } | ||||||||||||||||||||||||||||||||||||
| 1080 | } | ||||||||||||||||||||||||||||||||||||
| 1081 | |||||||||||||||||||||||||||||||||||||
| 1082 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1083 | |||||||||||||||||||||||||||||||||||||
| 1084 | signed_size_type recvfrom(socket_type s, buf* bufs, size_t count, | ||||||||||||||||||||||||||||||||||||
| 1085 | int flags, void* addr, std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1086 | { | ||||||||||||||||||||||||||||||||||||
| 1087 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1088 | // Receive some data. | ||||||||||||||||||||||||||||||||||||
| 1089 | DWORD recv_buf_count = static_cast<DWORD>(count); | ||||||||||||||||||||||||||||||||||||
| 1090 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1091 | DWORD recv_flags = flags; | ||||||||||||||||||||||||||||||||||||
| 1092 | int tmp_addrlen = (int)*addrlen; | ||||||||||||||||||||||||||||||||||||
| 1093 | int result = ::WSARecvFrom(s, bufs, recv_buf_count, &bytes_transferred, | ||||||||||||||||||||||||||||||||||||
| 1094 | &recv_flags, static_cast<socket_addr_type*>(addr), &tmp_addrlen, 0, 0); | ||||||||||||||||||||||||||||||||||||
| 1095 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 1096 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||||
| 1097 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 1098 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 1099 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 1100 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 1101 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||||
| 1102 | result = 0; | ||||||||||||||||||||||||||||||||||||
| 1103 | if (result != 0) | ||||||||||||||||||||||||||||||||||||
| 1104 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 1105 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 1106 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||||
| 1107 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1108 | msghdr msg = msghdr(); | ||||||||||||||||||||||||||||||||||||
| 1109 | init_msghdr_msg_name(msg.msg_name, addr); | ||||||||||||||||||||||||||||||||||||
| 1110 | msg.msg_namelen = static_cast<int>(*addrlen); | ||||||||||||||||||||||||||||||||||||
| 1111 | msg.msg_iov = bufs; | ||||||||||||||||||||||||||||||||||||
| 1112 | msg.msg_iovlen = static_cast<int>(count); | ||||||||||||||||||||||||||||||||||||
| 1113 | signed_size_type result = ::recvmsg(s, &msg, flags); | ||||||||||||||||||||||||||||||||||||
| 1114 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 1115 | *addrlen = msg.msg_namelen; | ||||||||||||||||||||||||||||||||||||
| 1116 | return result; | ||||||||||||||||||||||||||||||||||||
| 1117 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1118 | } | ||||||||||||||||||||||||||||||||||||
| 1119 | |||||||||||||||||||||||||||||||||||||
| 1120 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||||
| 1121 | inline signed_size_type call_recvfrom(SockLenType msghdr::*, socket_type s, | ||||||||||||||||||||||||||||||||||||
| 1122 | void* data, size_t size, int flags, void* addr, std::size_t* addrlen) | ||||||||||||||||||||||||||||||||||||
| 1123 | { | ||||||||||||||||||||||||||||||||||||
| 1124 | SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; | ||||||||||||||||||||||||||||||||||||
| 1125 | signed_size_type result = ::recvfrom(s, static_cast<char*>(data), size, | ||||||||||||||||||||||||||||||||||||
| 1126 | flags, static_cast<socket_addr_type*>(addr), addrlen ? &tmp_addrlen : 0); | ||||||||||||||||||||||||||||||||||||
| 1127 | if (addrlen) | ||||||||||||||||||||||||||||||||||||
| 1128 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||||
| 1129 | return result; | ||||||||||||||||||||||||||||||||||||
| 1130 | } | ||||||||||||||||||||||||||||||||||||
| 1131 | |||||||||||||||||||||||||||||||||||||
| 1132 | signed_size_type recvfrom1(socket_type s, void* data, size_t size, | ||||||||||||||||||||||||||||||||||||
| 1133 | int flags, void* addr, std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1134 | { | ||||||||||||||||||||||||||||||||||||
| 1135 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1136 | // Receive some data. | ||||||||||||||||||||||||||||||||||||
| 1137 | WSABUF buf; | ||||||||||||||||||||||||||||||||||||
| 1138 | buf.buf = static_cast<char*>(data); | ||||||||||||||||||||||||||||||||||||
| 1139 | buf.len = static_cast<ULONG>(size); | ||||||||||||||||||||||||||||||||||||
| 1140 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1141 | DWORD recv_flags = flags; | ||||||||||||||||||||||||||||||||||||
| 1142 | int tmp_addrlen = (int)*addrlen; | ||||||||||||||||||||||||||||||||||||
| 1143 | int result = ::WSARecvFrom(s, &buf, 1, &bytes_transferred, &recv_flags, | ||||||||||||||||||||||||||||||||||||
| 1144 | static_cast<socket_addr_type*>(addr), &tmp_addrlen, 0, 0); | ||||||||||||||||||||||||||||||||||||
| 1145 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 1146 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||||
| 1147 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 1148 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 1149 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 1150 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 1151 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||||
| 1152 | result = 0; | ||||||||||||||||||||||||||||||||||||
| 1153 | if (result != 0) | ||||||||||||||||||||||||||||||||||||
| 1154 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 1155 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 1156 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||||
| 1157 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1158 | signed_size_type result = call_recvfrom(&msghdr::msg_namelen, | ||||||||||||||||||||||||||||||||||||
| 1159 | s, data, size, flags, addr, addrlen); | ||||||||||||||||||||||||||||||||||||
| 1160 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 1161 | return result; | ||||||||||||||||||||||||||||||||||||
| 1162 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1163 | } | ||||||||||||||||||||||||||||||||||||
| 1164 | |||||||||||||||||||||||||||||||||||||
| 1165 | size_t sync_recvfrom(socket_type s, state_type state, buf* bufs, size_t count, | ||||||||||||||||||||||||||||||||||||
| 1166 | int flags, void* addr, std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1167 | { | ||||||||||||||||||||||||||||||||||||
| 1168 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 1169 | { | ||||||||||||||||||||||||||||||||||||
| 1170 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 1171 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1172 | } | ||||||||||||||||||||||||||||||||||||
| 1173 | |||||||||||||||||||||||||||||||||||||
| 1174 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 1175 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1176 | { | ||||||||||||||||||||||||||||||||||||
| 1177 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||||
| 1178 | signed_size_type bytes = socket_ops::recvfrom( | ||||||||||||||||||||||||||||||||||||
| 1179 | s, bufs, count, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||||
| 1180 | |||||||||||||||||||||||||||||||||||||
| 1181 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1182 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1183 | return bytes; | ||||||||||||||||||||||||||||||||||||
| 1184 | |||||||||||||||||||||||||||||||||||||
| 1185 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1186 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 1187 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1188 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||||
| 1189 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1190 | |||||||||||||||||||||||||||||||||||||
| 1191 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||||
| 1192 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||||
| 1193 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1194 | } | ||||||||||||||||||||||||||||||||||||
| 1195 | } | ||||||||||||||||||||||||||||||||||||
| 1196 | |||||||||||||||||||||||||||||||||||||
| 1197 | size_t sync_recvfrom1(socket_type s, state_type state, void* data, size_t size, | ||||||||||||||||||||||||||||||||||||
| 1198 | int flags, void* addr, std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1199 | { | ||||||||||||||||||||||||||||||||||||
| 1200 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 1201 | { | ||||||||||||||||||||||||||||||||||||
| 1202 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 1203 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1204 | } | ||||||||||||||||||||||||||||||||||||
| 1205 | |||||||||||||||||||||||||||||||||||||
| 1206 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 1207 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1208 | { | ||||||||||||||||||||||||||||||||||||
| 1209 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||||
| 1210 | signed_size_type bytes = socket_ops::recvfrom1( | ||||||||||||||||||||||||||||||||||||
| 1211 | s, data, size, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||||
| 1212 | |||||||||||||||||||||||||||||||||||||
| 1213 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1214 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1215 | return bytes; | ||||||||||||||||||||||||||||||||||||
| 1216 | |||||||||||||||||||||||||||||||||||||
| 1217 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1218 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 1219 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1220 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||||
| 1221 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1222 | |||||||||||||||||||||||||||||||||||||
| 1223 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||||
| 1224 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||||
| 1225 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1226 | } | ||||||||||||||||||||||||||||||||||||
| 1227 | } | ||||||||||||||||||||||||||||||||||||
| 1228 | |||||||||||||||||||||||||||||||||||||
| 1229 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1230 | |||||||||||||||||||||||||||||||||||||
| 1231 | void complete_iocp_recvfrom( | ||||||||||||||||||||||||||||||||||||
| 1232 | const weak_cancel_token_type& cancel_token, | ||||||||||||||||||||||||||||||||||||
| 1233 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1234 | { | ||||||||||||||||||||||||||||||||||||
| 1235 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||||
| 1236 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 1237 | { | ||||||||||||||||||||||||||||||||||||
| 1238 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||||
| 1239 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||||
| 1240 | else | ||||||||||||||||||||||||||||||||||||
| 1241 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 1242 | } | ||||||||||||||||||||||||||||||||||||
| 1243 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 1244 | { | ||||||||||||||||||||||||||||||||||||
| 1245 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 1246 | } | ||||||||||||||||||||||||||||||||||||
| 1247 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||||
| 1248 | { | ||||||||||||||||||||||||||||||||||||
| 1249 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 1250 | } | ||||||||||||||||||||||||||||||||||||
| 1251 | } | ||||||||||||||||||||||||||||||||||||
| 1252 | |||||||||||||||||||||||||||||||||||||
| 1253 | #else // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1254 | |||||||||||||||||||||||||||||||||||||
| 1255 | bool non_blocking_recvfrom(socket_type s, buf* bufs, | ||||||||||||||||||||||||||||||||||||
| 1256 | size_t count, int flags, void* addr, std::size_t* addrlen, | ||||||||||||||||||||||||||||||||||||
| 1257 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||||
| 1258 | { | ||||||||||||||||||||||||||||||||||||
| 1259 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1260 | { | ||||||||||||||||||||||||||||||||||||
| 1261 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 1262 | signed_size_type bytes = socket_ops::recvfrom( | ||||||||||||||||||||||||||||||||||||
| 1263 | s, bufs, count, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||||
| 1264 | |||||||||||||||||||||||||||||||||||||
| 1265 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1266 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1267 | { | ||||||||||||||||||||||||||||||||||||
| 1268 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||||
| 1269 | return true; | ||||||||||||||||||||||||||||||||||||
| 1270 | } | ||||||||||||||||||||||||||||||||||||
| 1271 | |||||||||||||||||||||||||||||||||||||
| 1272 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||||
| 1273 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||||
| 1274 | continue; | ||||||||||||||||||||||||||||||||||||
| 1275 | |||||||||||||||||||||||||||||||||||||
| 1276 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||||
| 1277 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1278 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 1279 | return false; | ||||||||||||||||||||||||||||||||||||
| 1280 | |||||||||||||||||||||||||||||||||||||
| 1281 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1282 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1283 | return true; | ||||||||||||||||||||||||||||||||||||
| 1284 | } | ||||||||||||||||||||||||||||||||||||
| 1285 | } | ||||||||||||||||||||||||||||||||||||
| 1286 | |||||||||||||||||||||||||||||||||||||
| 1287 | bool non_blocking_recvfrom1(socket_type s, void* data, | ||||||||||||||||||||||||||||||||||||
| 1288 | size_t size, int flags, void* addr, std::size_t* addrlen, | ||||||||||||||||||||||||||||||||||||
| 1289 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||||
| 1290 | { | ||||||||||||||||||||||||||||||||||||
| 1291 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1292 | { | ||||||||||||||||||||||||||||||||||||
| 1293 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 1294 | signed_size_type bytes = socket_ops::recvfrom1( | ||||||||||||||||||||||||||||||||||||
| 1295 | s, data, size, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||||
| 1296 | |||||||||||||||||||||||||||||||||||||
| 1297 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1298 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1299 | { | ||||||||||||||||||||||||||||||||||||
| 1300 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||||
| 1301 | return true; | ||||||||||||||||||||||||||||||||||||
| 1302 | } | ||||||||||||||||||||||||||||||||||||
| 1303 | |||||||||||||||||||||||||||||||||||||
| 1304 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||||
| 1305 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||||
| 1306 | continue; | ||||||||||||||||||||||||||||||||||||
| 1307 | |||||||||||||||||||||||||||||||||||||
| 1308 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||||
| 1309 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1310 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 1311 | return false; | ||||||||||||||||||||||||||||||||||||
| 1312 | |||||||||||||||||||||||||||||||||||||
| 1313 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1314 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1315 | return true; | ||||||||||||||||||||||||||||||||||||
| 1316 | } | ||||||||||||||||||||||||||||||||||||
| 1317 | } | ||||||||||||||||||||||||||||||||||||
| 1318 | |||||||||||||||||||||||||||||||||||||
| 1319 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1320 | |||||||||||||||||||||||||||||||||||||
| 1321 | signed_size_type recvmsg(socket_type s, buf* bufs, size_t count, | ||||||||||||||||||||||||||||||||||||
| 1322 | int in_flags, int& out_flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1323 | { | ||||||||||||||||||||||||||||||||||||
| 1324 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1325 | out_flags = 0; | ||||||||||||||||||||||||||||||||||||
| 1326 | return socket_ops::recv(s, bufs, count, in_flags, ec); | ||||||||||||||||||||||||||||||||||||
| 1327 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1328 | msghdr msg = msghdr(); | ||||||||||||||||||||||||||||||||||||
| 1329 | msg.msg_iov = bufs; | ||||||||||||||||||||||||||||||||||||
| 1330 | msg.msg_iovlen = static_cast<int>(count); | ||||||||||||||||||||||||||||||||||||
| 1331 | signed_size_type result = ::recvmsg(s, &msg, in_flags); | ||||||||||||||||||||||||||||||||||||
| 1332 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 1333 | if (result >= 0) | ||||||||||||||||||||||||||||||||||||
| 1334 | out_flags = msg.msg_flags; | ||||||||||||||||||||||||||||||||||||
| 1335 | else | ||||||||||||||||||||||||||||||||||||
| 1336 | out_flags = 0; | ||||||||||||||||||||||||||||||||||||
| 1337 | return result; | ||||||||||||||||||||||||||||||||||||
| 1338 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1339 | } | ||||||||||||||||||||||||||||||||||||
| 1340 | |||||||||||||||||||||||||||||||||||||
| 1341 | size_t sync_recvmsg(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||||
| 1342 | buf* bufs, size_t count, int in_flags, int& out_flags, | ||||||||||||||||||||||||||||||||||||
| 1343 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1344 | { | ||||||||||||||||||||||||||||||||||||
| 1345 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 1346 | { | ||||||||||||||||||||||||||||||||||||
| 1347 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 1348 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1349 | } | ||||||||||||||||||||||||||||||||||||
| 1350 | |||||||||||||||||||||||||||||||||||||
| 1351 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 1352 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1353 | { | ||||||||||||||||||||||||||||||||||||
| 1354 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||||
| 1355 | signed_size_type bytes = socket_ops::recvmsg( | ||||||||||||||||||||||||||||||||||||
| 1356 | s, bufs, count, in_flags, out_flags, ec); | ||||||||||||||||||||||||||||||||||||
| 1357 | |||||||||||||||||||||||||||||||||||||
| 1358 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1359 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1360 | return bytes; | ||||||||||||||||||||||||||||||||||||
| 1361 | |||||||||||||||||||||||||||||||||||||
| 1362 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1363 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 1364 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1365 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||||
| 1366 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1367 | |||||||||||||||||||||||||||||||||||||
| 1368 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||||
| 1369 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||||
| 1370 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1371 | } | ||||||||||||||||||||||||||||||||||||
| 1372 | } | ||||||||||||||||||||||||||||||||||||
| 1373 | |||||||||||||||||||||||||||||||||||||
| 1374 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1375 | |||||||||||||||||||||||||||||||||||||
| 1376 | void complete_iocp_recvmsg( | ||||||||||||||||||||||||||||||||||||
| 1377 | const weak_cancel_token_type& cancel_token, | ||||||||||||||||||||||||||||||||||||
| 1378 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1379 | { | ||||||||||||||||||||||||||||||||||||
| 1380 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||||
| 1381 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 1382 | { | ||||||||||||||||||||||||||||||||||||
| 1383 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||||
| 1384 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||||
| 1385 | else | ||||||||||||||||||||||||||||||||||||
| 1386 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 1387 | } | ||||||||||||||||||||||||||||||||||||
| 1388 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 1389 | { | ||||||||||||||||||||||||||||||||||||
| 1390 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 1391 | } | ||||||||||||||||||||||||||||||||||||
| 1392 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||||
| 1393 | { | ||||||||||||||||||||||||||||||||||||
| 1394 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 1395 | } | ||||||||||||||||||||||||||||||||||||
| 1396 | } | ||||||||||||||||||||||||||||||||||||
| 1397 | |||||||||||||||||||||||||||||||||||||
| 1398 | #else // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1399 | |||||||||||||||||||||||||||||||||||||
| 1400 | bool non_blocking_recvmsg(socket_type s, | ||||||||||||||||||||||||||||||||||||
| 1401 | buf* bufs, size_t count, int in_flags, int& out_flags, | ||||||||||||||||||||||||||||||||||||
| 1402 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||||
| 1403 | { | ||||||||||||||||||||||||||||||||||||
| 1404 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1405 | { | ||||||||||||||||||||||||||||||||||||
| 1406 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 1407 | signed_size_type bytes = socket_ops::recvmsg( | ||||||||||||||||||||||||||||||||||||
| 1408 | s, bufs, count, in_flags, out_flags, ec); | ||||||||||||||||||||||||||||||||||||
| 1409 | |||||||||||||||||||||||||||||||||||||
| 1410 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1411 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1412 | { | ||||||||||||||||||||||||||||||||||||
| 1413 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||||
| 1414 | return true; | ||||||||||||||||||||||||||||||||||||
| 1415 | } | ||||||||||||||||||||||||||||||||||||
| 1416 | |||||||||||||||||||||||||||||||||||||
| 1417 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||||
| 1418 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||||
| 1419 | continue; | ||||||||||||||||||||||||||||||||||||
| 1420 | |||||||||||||||||||||||||||||||||||||
| 1421 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||||
| 1422 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1423 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 1424 | return false; | ||||||||||||||||||||||||||||||||||||
| 1425 | |||||||||||||||||||||||||||||||||||||
| 1426 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1427 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1428 | return true; | ||||||||||||||||||||||||||||||||||||
| 1429 | } | ||||||||||||||||||||||||||||||||||||
| 1430 | } | ||||||||||||||||||||||||||||||||||||
| 1431 | |||||||||||||||||||||||||||||||||||||
| 1432 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1433 | |||||||||||||||||||||||||||||||||||||
| 1434 | signed_size_type send(socket_type s, const buf* bufs, size_t count, | ||||||||||||||||||||||||||||||||||||
| 1435 | int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1436 | { | ||||||||||||||||||||||||||||||||||||
| 1437 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1438 | // Send the data. | ||||||||||||||||||||||||||||||||||||
| 1439 | DWORD send_buf_count = static_cast<DWORD>(count); | ||||||||||||||||||||||||||||||||||||
| 1440 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1441 | DWORD send_flags = flags; | ||||||||||||||||||||||||||||||||||||
| 1442 | int result = ::WSASend(s, const_cast<buf*>(bufs), | ||||||||||||||||||||||||||||||||||||
| 1443 | send_buf_count, &bytes_transferred, send_flags, 0, 0); | ||||||||||||||||||||||||||||||||||||
| 1444 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 1445 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 1446 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 1447 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 1448 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 1449 | if (result != 0) | ||||||||||||||||||||||||||||||||||||
| 1450 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 1451 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 1452 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||||
| 1453 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1454 | msghdr msg = msghdr(); | ||||||||||||||||||||||||||||||||||||
| 1455 | msg.msg_iov = const_cast<buf*>(bufs); | ||||||||||||||||||||||||||||||||||||
| 1456 | msg.msg_iovlen = static_cast<int>(count); | ||||||||||||||||||||||||||||||||||||
| 1457 | #if defined(BOOST_ASIO_HAS_MSG_NOSIGNAL1) | ||||||||||||||||||||||||||||||||||||
| 1458 | flags |= MSG_NOSIGNALMSG_NOSIGNAL; | ||||||||||||||||||||||||||||||||||||
| 1459 | #endif // defined(BOOST_ASIO_HAS_MSG_NOSIGNAL) | ||||||||||||||||||||||||||||||||||||
| 1460 | signed_size_type result = ::sendmsg(s, &msg, flags); | ||||||||||||||||||||||||||||||||||||
| 1461 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 1462 | return result; | ||||||||||||||||||||||||||||||||||||
| 1463 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1464 | } | ||||||||||||||||||||||||||||||||||||
| 1465 | |||||||||||||||||||||||||||||||||||||
| 1466 | signed_size_type send1(socket_type s, const void* data, size_t size, | ||||||||||||||||||||||||||||||||||||
| 1467 | int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1468 | { | ||||||||||||||||||||||||||||||||||||
| 1469 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1470 | // Send the data. | ||||||||||||||||||||||||||||||||||||
| 1471 | WSABUF buf; | ||||||||||||||||||||||||||||||||||||
| 1472 | buf.buf = const_cast<char*>(static_cast<const char*>(data)); | ||||||||||||||||||||||||||||||||||||
| 1473 | buf.len = static_cast<ULONG>(size); | ||||||||||||||||||||||||||||||||||||
| 1474 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1475 | DWORD send_flags = flags; | ||||||||||||||||||||||||||||||||||||
| 1476 | int result = ::WSASend(s, &buf, 1, | ||||||||||||||||||||||||||||||||||||
| 1477 | &bytes_transferred, send_flags, 0, 0); | ||||||||||||||||||||||||||||||||||||
| 1478 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 1479 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 1480 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 1481 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 1482 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 1483 | if (result != 0) | ||||||||||||||||||||||||||||||||||||
| 1484 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 1485 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 1486 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||||
| 1487 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1488 | #if defined(BOOST_ASIO_HAS_MSG_NOSIGNAL1) | ||||||||||||||||||||||||||||||||||||
| 1489 | flags |= MSG_NOSIGNALMSG_NOSIGNAL; | ||||||||||||||||||||||||||||||||||||
| 1490 | #endif // defined(BOOST_ASIO_HAS_MSG_NOSIGNAL) | ||||||||||||||||||||||||||||||||||||
| 1491 | signed_size_type result = ::send(s, | ||||||||||||||||||||||||||||||||||||
| 1492 | static_cast<const char*>(data), size, flags); | ||||||||||||||||||||||||||||||||||||
| 1493 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 1494 | return result; | ||||||||||||||||||||||||||||||||||||
| 1495 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1496 | } | ||||||||||||||||||||||||||||||||||||
| 1497 | |||||||||||||||||||||||||||||||||||||
| 1498 | size_t sync_send(socket_type s, state_type state, const buf* bufs, | ||||||||||||||||||||||||||||||||||||
| 1499 | size_t count, int flags, bool all_empty, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1500 | { | ||||||||||||||||||||||||||||||||||||
| 1501 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 1502 | { | ||||||||||||||||||||||||||||||||||||
| 1503 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 1504 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1505 | } | ||||||||||||||||||||||||||||||||||||
| 1506 | |||||||||||||||||||||||||||||||||||||
| 1507 | // A request to write 0 bytes to a stream is a no-op. | ||||||||||||||||||||||||||||||||||||
| 1508 | if (all_empty && (state & stream_oriented)) | ||||||||||||||||||||||||||||||||||||
| 1509 | { | ||||||||||||||||||||||||||||||||||||
| 1510 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 1511 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1512 | } | ||||||||||||||||||||||||||||||||||||
| 1513 | |||||||||||||||||||||||||||||||||||||
| 1514 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 1515 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1516 | { | ||||||||||||||||||||||||||||||||||||
| 1517 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||||
| 1518 | signed_size_type bytes = socket_ops::send(s, bufs, count, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 1519 | |||||||||||||||||||||||||||||||||||||
| 1520 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1521 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1522 | return bytes; | ||||||||||||||||||||||||||||||||||||
| 1523 | |||||||||||||||||||||||||||||||||||||
| 1524 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1525 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 1526 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1527 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||||
| 1528 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1529 | |||||||||||||||||||||||||||||||||||||
| 1530 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||||
| 1531 | if (socket_ops::poll_write(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||||
| 1532 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1533 | } | ||||||||||||||||||||||||||||||||||||
| 1534 | } | ||||||||||||||||||||||||||||||||||||
| 1535 | |||||||||||||||||||||||||||||||||||||
| 1536 | size_t sync_send1(socket_type s, state_type state, const void* data, | ||||||||||||||||||||||||||||||||||||
| 1537 | size_t size, int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1538 | { | ||||||||||||||||||||||||||||||||||||
| 1539 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 1540 | { | ||||||||||||||||||||||||||||||||||||
| 1541 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 1542 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1543 | } | ||||||||||||||||||||||||||||||||||||
| 1544 | |||||||||||||||||||||||||||||||||||||
| 1545 | // A request to write 0 bytes to a stream is a no-op. | ||||||||||||||||||||||||||||||||||||
| 1546 | if (size == 0 && (state & stream_oriented)) | ||||||||||||||||||||||||||||||||||||
| 1547 | { | ||||||||||||||||||||||||||||||||||||
| 1548 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 1549 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1550 | } | ||||||||||||||||||||||||||||||||||||
| 1551 | |||||||||||||||||||||||||||||||||||||
| 1552 | // Read some data. | ||||||||||||||||||||||||||||||||||||
| 1553 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1554 | { | ||||||||||||||||||||||||||||||||||||
| 1555 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||||
| 1556 | signed_size_type bytes = socket_ops::send1(s, data, size, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 1557 | |||||||||||||||||||||||||||||||||||||
| 1558 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1559 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1560 | return bytes; | ||||||||||||||||||||||||||||||||||||
| 1561 | |||||||||||||||||||||||||||||||||||||
| 1562 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1563 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 1564 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1565 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||||
| 1566 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1567 | |||||||||||||||||||||||||||||||||||||
| 1568 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||||
| 1569 | if (socket_ops::poll_write(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||||
| 1570 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1571 | } | ||||||||||||||||||||||||||||||||||||
| 1572 | } | ||||||||||||||||||||||||||||||||||||
| 1573 | |||||||||||||||||||||||||||||||||||||
| 1574 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1575 | |||||||||||||||||||||||||||||||||||||
| 1576 | void complete_iocp_send( | ||||||||||||||||||||||||||||||||||||
| 1577 | const weak_cancel_token_type& cancel_token, | ||||||||||||||||||||||||||||||||||||
| 1578 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1579 | { | ||||||||||||||||||||||||||||||||||||
| 1580 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||||
| 1581 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 1582 | { | ||||||||||||||||||||||||||||||||||||
| 1583 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||||
| 1584 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||||
| 1585 | else | ||||||||||||||||||||||||||||||||||||
| 1586 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 1587 | } | ||||||||||||||||||||||||||||||||||||
| 1588 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 1589 | { | ||||||||||||||||||||||||||||||||||||
| 1590 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 1591 | } | ||||||||||||||||||||||||||||||||||||
| 1592 | } | ||||||||||||||||||||||||||||||||||||
| 1593 | |||||||||||||||||||||||||||||||||||||
| 1594 | #else // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1595 | |||||||||||||||||||||||||||||||||||||
| 1596 | bool non_blocking_send(socket_type s, | ||||||||||||||||||||||||||||||||||||
| 1597 | const buf* bufs, size_t count, int flags, | ||||||||||||||||||||||||||||||||||||
| 1598 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||||
| 1599 | { | ||||||||||||||||||||||||||||||||||||
| 1600 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1601 | { | ||||||||||||||||||||||||||||||||||||
| 1602 | // Write some data. | ||||||||||||||||||||||||||||||||||||
| 1603 | signed_size_type bytes = socket_ops::send(s, bufs, count, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 1604 | |||||||||||||||||||||||||||||||||||||
| 1605 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1606 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1607 | { | ||||||||||||||||||||||||||||||||||||
| 1608 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||||
| 1609 | return true; | ||||||||||||||||||||||||||||||||||||
| 1610 | } | ||||||||||||||||||||||||||||||||||||
| 1611 | |||||||||||||||||||||||||||||||||||||
| 1612 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||||
| 1613 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||||
| 1614 | continue; | ||||||||||||||||||||||||||||||||||||
| 1615 | |||||||||||||||||||||||||||||||||||||
| 1616 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||||
| 1617 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1618 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 1619 | return false; | ||||||||||||||||||||||||||||||||||||
| 1620 | |||||||||||||||||||||||||||||||||||||
| 1621 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1622 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1623 | return true; | ||||||||||||||||||||||||||||||||||||
| 1624 | } | ||||||||||||||||||||||||||||||||||||
| 1625 | } | ||||||||||||||||||||||||||||||||||||
| 1626 | |||||||||||||||||||||||||||||||||||||
| 1627 | bool non_blocking_send1(socket_type s, | ||||||||||||||||||||||||||||||||||||
| 1628 | const void* data, size_t size, int flags, | ||||||||||||||||||||||||||||||||||||
| 1629 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||||
| 1630 | { | ||||||||||||||||||||||||||||||||||||
| 1631 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1632 | { | ||||||||||||||||||||||||||||||||||||
| 1633 | // Write some data. | ||||||||||||||||||||||||||||||||||||
| 1634 | signed_size_type bytes = socket_ops::send1(s, data, size, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 1635 | |||||||||||||||||||||||||||||||||||||
| 1636 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1637 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1638 | { | ||||||||||||||||||||||||||||||||||||
| 1639 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||||
| 1640 | return true; | ||||||||||||||||||||||||||||||||||||
| 1641 | } | ||||||||||||||||||||||||||||||||||||
| 1642 | |||||||||||||||||||||||||||||||||||||
| 1643 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||||
| 1644 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||||
| 1645 | continue; | ||||||||||||||||||||||||||||||||||||
| 1646 | |||||||||||||||||||||||||||||||||||||
| 1647 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||||
| 1648 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1649 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 1650 | return false; | ||||||||||||||||||||||||||||||||||||
| 1651 | |||||||||||||||||||||||||||||||||||||
| 1652 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1653 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1654 | return true; | ||||||||||||||||||||||||||||||||||||
| 1655 | } | ||||||||||||||||||||||||||||||||||||
| 1656 | } | ||||||||||||||||||||||||||||||||||||
| 1657 | |||||||||||||||||||||||||||||||||||||
| 1658 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1659 | |||||||||||||||||||||||||||||||||||||
| 1660 | signed_size_type sendto(socket_type s, const buf* bufs, | ||||||||||||||||||||||||||||||||||||
| 1661 | size_t count, int flags, const void* addr, | ||||||||||||||||||||||||||||||||||||
| 1662 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1663 | { | ||||||||||||||||||||||||||||||||||||
| 1664 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1665 | // Send the data. | ||||||||||||||||||||||||||||||||||||
| 1666 | DWORD send_buf_count = static_cast<DWORD>(count); | ||||||||||||||||||||||||||||||||||||
| 1667 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1668 | int result = ::WSASendTo(s, const_cast<buf*>(bufs), | ||||||||||||||||||||||||||||||||||||
| 1669 | send_buf_count, &bytes_transferred, flags, | ||||||||||||||||||||||||||||||||||||
| 1670 | static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||||
| 1671 | static_cast<int>(addrlen), 0, 0); | ||||||||||||||||||||||||||||||||||||
| 1672 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 1673 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 1674 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 1675 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 1676 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 1677 | if (result != 0) | ||||||||||||||||||||||||||||||||||||
| 1678 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 1679 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 1680 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||||
| 1681 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1682 | msghdr msg = msghdr(); | ||||||||||||||||||||||||||||||||||||
| 1683 | init_msghdr_msg_name(msg.msg_name, addr); | ||||||||||||||||||||||||||||||||||||
| 1684 | msg.msg_namelen = static_cast<int>(addrlen); | ||||||||||||||||||||||||||||||||||||
| 1685 | msg.msg_iov = const_cast<buf*>(bufs); | ||||||||||||||||||||||||||||||||||||
| 1686 | msg.msg_iovlen = static_cast<int>(count); | ||||||||||||||||||||||||||||||||||||
| 1687 | #if defined(BOOST_ASIO_HAS_MSG_NOSIGNAL1) | ||||||||||||||||||||||||||||||||||||
| 1688 | flags |= MSG_NOSIGNALMSG_NOSIGNAL; | ||||||||||||||||||||||||||||||||||||
| 1689 | #endif // defined(BOOST_ASIO_HAS_MSG_NOSIGNAL) | ||||||||||||||||||||||||||||||||||||
| 1690 | signed_size_type result = ::sendmsg(s, &msg, flags); | ||||||||||||||||||||||||||||||||||||
| 1691 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 1692 | return result; | ||||||||||||||||||||||||||||||||||||
| 1693 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1694 | } | ||||||||||||||||||||||||||||||||||||
| 1695 | |||||||||||||||||||||||||||||||||||||
| 1696 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||||
| 1697 | inline signed_size_type call_sendto(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||||
| 1698 | socket_type s, const void* data, size_t size, int flags, | ||||||||||||||||||||||||||||||||||||
| 1699 | const void* addr, std::size_t addrlen) | ||||||||||||||||||||||||||||||||||||
| 1700 | { | ||||||||||||||||||||||||||||||||||||
| 1701 | return ::sendto(s, static_cast<char*>(const_cast<void*>(data)), size, flags, | ||||||||||||||||||||||||||||||||||||
| 1702 | static_cast<const socket_addr_type*>(addr), (SockLenType)addrlen); | ||||||||||||||||||||||||||||||||||||
| 1703 | } | ||||||||||||||||||||||||||||||||||||
| 1704 | |||||||||||||||||||||||||||||||||||||
| 1705 | signed_size_type sendto1(socket_type s, const void* data, | ||||||||||||||||||||||||||||||||||||
| 1706 | size_t size, int flags, const void* addr, | ||||||||||||||||||||||||||||||||||||
| 1707 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1708 | { | ||||||||||||||||||||||||||||||||||||
| 1709 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1710 | // Send the data. | ||||||||||||||||||||||||||||||||||||
| 1711 | WSABUF buf; | ||||||||||||||||||||||||||||||||||||
| 1712 | buf.buf = const_cast<char*>(static_cast<const char*>(data)); | ||||||||||||||||||||||||||||||||||||
| 1713 | buf.len = static_cast<ULONG>(size); | ||||||||||||||||||||||||||||||||||||
| 1714 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1715 | int result = ::WSASendTo(s, &buf, 1, &bytes_transferred, flags, | ||||||||||||||||||||||||||||||||||||
| 1716 | static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||||
| 1717 | static_cast<int>(addrlen), 0, 0); | ||||||||||||||||||||||||||||||||||||
| 1718 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 1719 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||||
| 1720 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||||
| 1721 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||||
| 1722 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||||
| 1723 | if (result != 0) | ||||||||||||||||||||||||||||||||||||
| 1724 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 1725 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 1726 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||||
| 1727 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1728 | #if defined(BOOST_ASIO_HAS_MSG_NOSIGNAL1) | ||||||||||||||||||||||||||||||||||||
| 1729 | flags |= MSG_NOSIGNALMSG_NOSIGNAL; | ||||||||||||||||||||||||||||||||||||
| 1730 | #endif // defined(BOOST_ASIO_HAS_MSG_NOSIGNAL) | ||||||||||||||||||||||||||||||||||||
| 1731 | signed_size_type result = call_sendto(&msghdr::msg_namelen, | ||||||||||||||||||||||||||||||||||||
| 1732 | s, data, size, flags, addr, addrlen); | ||||||||||||||||||||||||||||||||||||
| 1733 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 1734 | return result; | ||||||||||||||||||||||||||||||||||||
| 1735 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1736 | } | ||||||||||||||||||||||||||||||||||||
| 1737 | |||||||||||||||||||||||||||||||||||||
| 1738 | size_t sync_sendto(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||||
| 1739 | const buf* bufs, size_t count, int flags, const void* addr, | ||||||||||||||||||||||||||||||||||||
| 1740 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1741 | { | ||||||||||||||||||||||||||||||||||||
| 1742 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 1743 | { | ||||||||||||||||||||||||||||||||||||
| 1744 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 1745 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1746 | } | ||||||||||||||||||||||||||||||||||||
| 1747 | |||||||||||||||||||||||||||||||||||||
| 1748 | // Write some data. | ||||||||||||||||||||||||||||||||||||
| 1749 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1750 | { | ||||||||||||||||||||||||||||||||||||
| 1751 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||||
| 1752 | signed_size_type bytes = socket_ops::sendto( | ||||||||||||||||||||||||||||||||||||
| 1753 | s, bufs, count, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||||
| 1754 | |||||||||||||||||||||||||||||||||||||
| 1755 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1756 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1757 | return bytes; | ||||||||||||||||||||||||||||||||||||
| 1758 | |||||||||||||||||||||||||||||||||||||
| 1759 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1760 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 1761 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1762 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||||
| 1763 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1764 | |||||||||||||||||||||||||||||||||||||
| 1765 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||||
| 1766 | if (socket_ops::poll_write(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||||
| 1767 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1768 | } | ||||||||||||||||||||||||||||||||||||
| 1769 | } | ||||||||||||||||||||||||||||||||||||
| 1770 | |||||||||||||||||||||||||||||||||||||
| 1771 | size_t sync_sendto1(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||||
| 1772 | const void* data, size_t size, int flags, const void* addr, | ||||||||||||||||||||||||||||||||||||
| 1773 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1774 | { | ||||||||||||||||||||||||||||||||||||
| 1775 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 1776 | { | ||||||||||||||||||||||||||||||||||||
| 1777 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 1778 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1779 | } | ||||||||||||||||||||||||||||||||||||
| 1780 | |||||||||||||||||||||||||||||||||||||
| 1781 | // Write some data. | ||||||||||||||||||||||||||||||||||||
| 1782 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1783 | { | ||||||||||||||||||||||||||||||||||||
| 1784 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||||
| 1785 | signed_size_type bytes = socket_ops::sendto1( | ||||||||||||||||||||||||||||||||||||
| 1786 | s, data, size, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||||
| 1787 | |||||||||||||||||||||||||||||||||||||
| 1788 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1789 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1790 | return bytes; | ||||||||||||||||||||||||||||||||||||
| 1791 | |||||||||||||||||||||||||||||||||||||
| 1792 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1793 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 1794 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1795 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||||
| 1796 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1797 | |||||||||||||||||||||||||||||||||||||
| 1798 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||||
| 1799 | if (socket_ops::poll_write(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||||
| 1800 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1801 | } | ||||||||||||||||||||||||||||||||||||
| 1802 | } | ||||||||||||||||||||||||||||||||||||
| 1803 | |||||||||||||||||||||||||||||||||||||
| 1804 | #if !defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1805 | |||||||||||||||||||||||||||||||||||||
| 1806 | bool non_blocking_sendto(socket_type s, | ||||||||||||||||||||||||||||||||||||
| 1807 | const buf* bufs, size_t count, int flags, | ||||||||||||||||||||||||||||||||||||
| 1808 | const void* addr, std::size_t addrlen, | ||||||||||||||||||||||||||||||||||||
| 1809 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||||
| 1810 | { | ||||||||||||||||||||||||||||||||||||
| 1811 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1812 | { | ||||||||||||||||||||||||||||||||||||
| 1813 | // Write some data. | ||||||||||||||||||||||||||||||||||||
| 1814 | signed_size_type bytes = socket_ops::sendto( | ||||||||||||||||||||||||||||||||||||
| 1815 | s, bufs, count, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||||
| 1816 | |||||||||||||||||||||||||||||||||||||
| 1817 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1818 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1819 | { | ||||||||||||||||||||||||||||||||||||
| 1820 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||||
| 1821 | return true; | ||||||||||||||||||||||||||||||||||||
| 1822 | } | ||||||||||||||||||||||||||||||||||||
| 1823 | |||||||||||||||||||||||||||||||||||||
| 1824 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||||
| 1825 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||||
| 1826 | continue; | ||||||||||||||||||||||||||||||||||||
| 1827 | |||||||||||||||||||||||||||||||||||||
| 1828 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||||
| 1829 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1830 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 1831 | return false; | ||||||||||||||||||||||||||||||||||||
| 1832 | |||||||||||||||||||||||||||||||||||||
| 1833 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1834 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1835 | return true; | ||||||||||||||||||||||||||||||||||||
| 1836 | } | ||||||||||||||||||||||||||||||||||||
| 1837 | } | ||||||||||||||||||||||||||||||||||||
| 1838 | |||||||||||||||||||||||||||||||||||||
| 1839 | bool non_blocking_sendto1(socket_type s, | ||||||||||||||||||||||||||||||||||||
| 1840 | const void* data, size_t size, int flags, | ||||||||||||||||||||||||||||||||||||
| 1841 | const void* addr, std::size_t addrlen, | ||||||||||||||||||||||||||||||||||||
| 1842 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||||
| 1843 | { | ||||||||||||||||||||||||||||||||||||
| 1844 | for (;;) | ||||||||||||||||||||||||||||||||||||
| 1845 | { | ||||||||||||||||||||||||||||||||||||
| 1846 | // Write some data. | ||||||||||||||||||||||||||||||||||||
| 1847 | signed_size_type bytes = socket_ops::sendto1( | ||||||||||||||||||||||||||||||||||||
| 1848 | s, data, size, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||||
| 1849 | |||||||||||||||||||||||||||||||||||||
| 1850 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||||
| 1851 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||||
| 1852 | { | ||||||||||||||||||||||||||||||||||||
| 1853 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||||
| 1854 | return true; | ||||||||||||||||||||||||||||||||||||
| 1855 | } | ||||||||||||||||||||||||||||||||||||
| 1856 | |||||||||||||||||||||||||||||||||||||
| 1857 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||||
| 1858 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||||
| 1859 | continue; | ||||||||||||||||||||||||||||||||||||
| 1860 | |||||||||||||||||||||||||||||||||||||
| 1861 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||||
| 1862 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||||
| 1863 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||||
| 1864 | return false; | ||||||||||||||||||||||||||||||||||||
| 1865 | |||||||||||||||||||||||||||||||||||||
| 1866 | // Operation failed. | ||||||||||||||||||||||||||||||||||||
| 1867 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||||
| 1868 | return true; | ||||||||||||||||||||||||||||||||||||
| 1869 | } | ||||||||||||||||||||||||||||||||||||
| 1870 | } | ||||||||||||||||||||||||||||||||||||
| 1871 | |||||||||||||||||||||||||||||||||||||
| 1872 | #endif // !defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||||
| 1873 | |||||||||||||||||||||||||||||||||||||
| 1874 | socket_type socket(int af, int type, int protocol, | ||||||||||||||||||||||||||||||||||||
| 1875 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1876 | { | ||||||||||||||||||||||||||||||||||||
| 1877 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 1878 | socket_type s = ::WSASocketW(af, type, protocol, 0, 0, WSA_FLAG_OVERLAPPED); | ||||||||||||||||||||||||||||||||||||
| 1879 | get_last_error(ec, s == invalid_socket); | ||||||||||||||||||||||||||||||||||||
| 1880 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 1881 | return s; | ||||||||||||||||||||||||||||||||||||
| 1882 | |||||||||||||||||||||||||||||||||||||
| 1883 | if (af == BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||||
| 1884 | { | ||||||||||||||||||||||||||||||||||||
| 1885 | // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to | ||||||||||||||||||||||||||||||||||||
| 1886 | // false. This will only succeed on Windows Vista and later versions of | ||||||||||||||||||||||||||||||||||||
| 1887 | // Windows, where a dual-stack IPv4/v6 implementation is available. | ||||||||||||||||||||||||||||||||||||
| 1888 | DWORD optval = 0; | ||||||||||||||||||||||||||||||||||||
| 1889 | ::setsockopt(s, IPPROTO_IPV6IPPROTO_IPV6, IPV6_V6ONLY26, | ||||||||||||||||||||||||||||||||||||
| 1890 | reinterpret_cast<const char*>(&optval), sizeof(optval)); | ||||||||||||||||||||||||||||||||||||
| 1891 | } | ||||||||||||||||||||||||||||||||||||
| 1892 | |||||||||||||||||||||||||||||||||||||
| 1893 | return s; | ||||||||||||||||||||||||||||||||||||
| 1894 | #elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) | ||||||||||||||||||||||||||||||||||||
| 1895 | socket_type s = ::socket(af, type, protocol); | ||||||||||||||||||||||||||||||||||||
| 1896 | get_last_error(ec, s == invalid_socket); | ||||||||||||||||||||||||||||||||||||
| 1897 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 1898 | return s; | ||||||||||||||||||||||||||||||||||||
| 1899 | |||||||||||||||||||||||||||||||||||||
| 1900 | int optval = 1; | ||||||||||||||||||||||||||||||||||||
| 1901 | int result = ::setsockopt(s, SOL_SOCKET1, | ||||||||||||||||||||||||||||||||||||
| 1902 | SO_NOSIGPIPE, &optval, sizeof(optval)); | ||||||||||||||||||||||||||||||||||||
| 1903 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 1904 | if (result != 0) | ||||||||||||||||||||||||||||||||||||
| 1905 | { | ||||||||||||||||||||||||||||||||||||
| 1906 | ::close(s); | ||||||||||||||||||||||||||||||||||||
| 1907 | return invalid_socket; | ||||||||||||||||||||||||||||||||||||
| 1908 | } | ||||||||||||||||||||||||||||||||||||
| 1909 | |||||||||||||||||||||||||||||||||||||
| 1910 | return s; | ||||||||||||||||||||||||||||||||||||
| 1911 | #else | ||||||||||||||||||||||||||||||||||||
| 1912 | int s = ::socket(af, type, protocol); | ||||||||||||||||||||||||||||||||||||
| 1913 | get_last_error(ec, s < 0); | ||||||||||||||||||||||||||||||||||||
| 1914 | return s; | ||||||||||||||||||||||||||||||||||||
| 1915 | #endif | ||||||||||||||||||||||||||||||||||||
| 1916 | } | ||||||||||||||||||||||||||||||||||||
| 1917 | |||||||||||||||||||||||||||||||||||||
| 1918 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||||
| 1919 | inline int call_setsockopt(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||||
| 1920 | socket_type s, int level, int optname, | ||||||||||||||||||||||||||||||||||||
| 1921 | const void* optval, std::size_t optlen) | ||||||||||||||||||||||||||||||||||||
| 1922 | { | ||||||||||||||||||||||||||||||||||||
| 1923 | return ::setsockopt(s, level, optname, | ||||||||||||||||||||||||||||||||||||
| 1924 | (const char*)optval, (SockLenType)optlen); | ||||||||||||||||||||||||||||||||||||
| 1925 | } | ||||||||||||||||||||||||||||||||||||
| 1926 | |||||||||||||||||||||||||||||||||||||
| 1927 | int setsockopt(socket_type s, state_type& state, int level, int optname, | ||||||||||||||||||||||||||||||||||||
| 1928 | const void* optval, std::size_t optlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 1929 | { | ||||||||||||||||||||||||||||||||||||
| 1930 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 1931 | { | ||||||||||||||||||||||||||||||||||||
| 1932 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 1933 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 1934 | } | ||||||||||||||||||||||||||||||||||||
| 1935 | |||||||||||||||||||||||||||||||||||||
| 1936 | if (level == custom_socket_option_level && optname == always_fail_option) | ||||||||||||||||||||||||||||||||||||
| 1937 | { | ||||||||||||||||||||||||||||||||||||
| 1938 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 1939 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 1940 | } | ||||||||||||||||||||||||||||||||||||
| 1941 | |||||||||||||||||||||||||||||||||||||
| 1942 | if (level == custom_socket_option_level | ||||||||||||||||||||||||||||||||||||
| 1943 | && optname == enable_connection_aborted_option) | ||||||||||||||||||||||||||||||||||||
| 1944 | { | ||||||||||||||||||||||||||||||||||||
| 1945 | if (optlen != sizeof(int)) | ||||||||||||||||||||||||||||||||||||
| 1946 | { | ||||||||||||||||||||||||||||||||||||
| 1947 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 1948 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 1949 | } | ||||||||||||||||||||||||||||||||||||
| 1950 | |||||||||||||||||||||||||||||||||||||
| 1951 | if (*static_cast<const int*>(optval)) | ||||||||||||||||||||||||||||||||||||
| 1952 | state |= enable_connection_aborted; | ||||||||||||||||||||||||||||||||||||
| 1953 | else | ||||||||||||||||||||||||||||||||||||
| 1954 | state &= ~enable_connection_aborted; | ||||||||||||||||||||||||||||||||||||
| 1955 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 1956 | return 0; | ||||||||||||||||||||||||||||||||||||
| 1957 | } | ||||||||||||||||||||||||||||||||||||
| 1958 | |||||||||||||||||||||||||||||||||||||
| 1959 | if (level == SOL_SOCKET1 && optname == SO_LINGER13) | ||||||||||||||||||||||||||||||||||||
| 1960 | state |= user_set_linger; | ||||||||||||||||||||||||||||||||||||
| 1961 | |||||||||||||||||||||||||||||||||||||
| 1962 | #if defined(__BORLANDC__) | ||||||||||||||||||||||||||||||||||||
| 1963 | // Mysteriously, using the getsockopt and setsockopt functions directly with | ||||||||||||||||||||||||||||||||||||
| 1964 | // Borland C++ results in incorrect values being set and read. The bug can be | ||||||||||||||||||||||||||||||||||||
| 1965 | // worked around by using function addresses resolved with GetProcAddress. | ||||||||||||||||||||||||||||||||||||
| 1966 | if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) | ||||||||||||||||||||||||||||||||||||
| 1967 | { | ||||||||||||||||||||||||||||||||||||
| 1968 | typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int); | ||||||||||||||||||||||||||||||||||||
| 1969 | if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt")) | ||||||||||||||||||||||||||||||||||||
| 1970 | { | ||||||||||||||||||||||||||||||||||||
| 1971 | int result = sso(s, level, optname, | ||||||||||||||||||||||||||||||||||||
| 1972 | reinterpret_cast<const char*>(optval), | ||||||||||||||||||||||||||||||||||||
| 1973 | static_cast<int>(optlen)); | ||||||||||||||||||||||||||||||||||||
| 1974 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 1975 | return result; | ||||||||||||||||||||||||||||||||||||
| 1976 | } | ||||||||||||||||||||||||||||||||||||
| 1977 | } | ||||||||||||||||||||||||||||||||||||
| 1978 | ec = boost::asio::error::fault; | ||||||||||||||||||||||||||||||||||||
| 1979 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 1980 | #else // defined(__BORLANDC__) | ||||||||||||||||||||||||||||||||||||
| 1981 | int result = call_setsockopt(&msghdr::msg_namelen, | ||||||||||||||||||||||||||||||||||||
| 1982 | s, level, optname, optval, optlen); | ||||||||||||||||||||||||||||||||||||
| 1983 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 1984 | if (result == 0) | ||||||||||||||||||||||||||||||||||||
| 1985 | { | ||||||||||||||||||||||||||||||||||||
| 1986 | #if defined(__MACH__) && defined(__APPLE__) \ | ||||||||||||||||||||||||||||||||||||
| 1987 | || defined(__NetBSD__) || defined(__FreeBSD__) \ | ||||||||||||||||||||||||||||||||||||
| 1988 | || defined(__OpenBSD__) || defined(__QNX__) | ||||||||||||||||||||||||||||||||||||
| 1989 | // To implement portable behaviour for SO_REUSEADDR with UDP sockets we | ||||||||||||||||||||||||||||||||||||
| 1990 | // need to also set SO_REUSEPORT on BSD-based platforms. | ||||||||||||||||||||||||||||||||||||
| 1991 | if ((state & datagram_oriented) | ||||||||||||||||||||||||||||||||||||
| 1992 | && level == SOL_SOCKET1 && optname == SO_REUSEADDR2) | ||||||||||||||||||||||||||||||||||||
| 1993 | { | ||||||||||||||||||||||||||||||||||||
| 1994 | call_setsockopt(&msghdr::msg_namelen, s, | ||||||||||||||||||||||||||||||||||||
| 1995 | SOL_SOCKET1, SO_REUSEPORT15, optval, optlen); | ||||||||||||||||||||||||||||||||||||
| 1996 | } | ||||||||||||||||||||||||||||||||||||
| 1997 | #endif | ||||||||||||||||||||||||||||||||||||
| 1998 | } | ||||||||||||||||||||||||||||||||||||
| 1999 | |||||||||||||||||||||||||||||||||||||
| 2000 | return result; | ||||||||||||||||||||||||||||||||||||
| 2001 | #endif // defined(__BORLANDC__) | ||||||||||||||||||||||||||||||||||||
| 2002 | } | ||||||||||||||||||||||||||||||||||||
| 2003 | |||||||||||||||||||||||||||||||||||||
| 2004 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||||
| 2005 | inline int call_getsockopt(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||||
| 2006 | socket_type s, int level, int optname, | ||||||||||||||||||||||||||||||||||||
| 2007 | void* optval, std::size_t* optlen) | ||||||||||||||||||||||||||||||||||||
| 2008 | { | ||||||||||||||||||||||||||||||||||||
| 2009 | SockLenType tmp_optlen = (SockLenType)*optlen; | ||||||||||||||||||||||||||||||||||||
| 2010 | int result = ::getsockopt(s, level, optname, (char*)optval, &tmp_optlen); | ||||||||||||||||||||||||||||||||||||
| 2011 | *optlen = (std::size_t)tmp_optlen; | ||||||||||||||||||||||||||||||||||||
| 2012 | return result; | ||||||||||||||||||||||||||||||||||||
| 2013 | } | ||||||||||||||||||||||||||||||||||||
| 2014 | |||||||||||||||||||||||||||||||||||||
| 2015 | int getsockopt(socket_type s, state_type state, int level, int optname, | ||||||||||||||||||||||||||||||||||||
| 2016 | void* optval, size_t* optlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2017 | { | ||||||||||||||||||||||||||||||||||||
| 2018 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 2019 | { | ||||||||||||||||||||||||||||||||||||
| 2020 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 2021 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2022 | } | ||||||||||||||||||||||||||||||||||||
| 2023 | |||||||||||||||||||||||||||||||||||||
| 2024 | if (level == custom_socket_option_level && optname == always_fail_option) | ||||||||||||||||||||||||||||||||||||
| 2025 | { | ||||||||||||||||||||||||||||||||||||
| 2026 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2027 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2028 | } | ||||||||||||||||||||||||||||||||||||
| 2029 | |||||||||||||||||||||||||||||||||||||
| 2030 | if (level == custom_socket_option_level | ||||||||||||||||||||||||||||||||||||
| 2031 | && optname == enable_connection_aborted_option) | ||||||||||||||||||||||||||||||||||||
| 2032 | { | ||||||||||||||||||||||||||||||||||||
| 2033 | if (*optlen != sizeof(int)) | ||||||||||||||||||||||||||||||||||||
| 2034 | { | ||||||||||||||||||||||||||||||||||||
| 2035 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2036 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2037 | } | ||||||||||||||||||||||||||||||||||||
| 2038 | |||||||||||||||||||||||||||||||||||||
| 2039 | *static_cast<int*>(optval) = (state & enable_connection_aborted) ? 1 : 0; | ||||||||||||||||||||||||||||||||||||
| 2040 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2041 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2042 | } | ||||||||||||||||||||||||||||||||||||
| 2043 | |||||||||||||||||||||||||||||||||||||
| 2044 | #if defined(__BORLANDC__) | ||||||||||||||||||||||||||||||||||||
| 2045 | // Mysteriously, using the getsockopt and setsockopt functions directly with | ||||||||||||||||||||||||||||||||||||
| 2046 | // Borland C++ results in incorrect values being set and read. The bug can be | ||||||||||||||||||||||||||||||||||||
| 2047 | // worked around by using function addresses resolved with GetProcAddress. | ||||||||||||||||||||||||||||||||||||
| 2048 | if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) | ||||||||||||||||||||||||||||||||||||
| 2049 | { | ||||||||||||||||||||||||||||||||||||
| 2050 | typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*); | ||||||||||||||||||||||||||||||||||||
| 2051 | if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt")) | ||||||||||||||||||||||||||||||||||||
| 2052 | { | ||||||||||||||||||||||||||||||||||||
| 2053 | int tmp_optlen = static_cast<int>(*optlen); | ||||||||||||||||||||||||||||||||||||
| 2054 | int result = gso(s, level, optname, | ||||||||||||||||||||||||||||||||||||
| 2055 | reinterpret_cast<char*>(optval), &tmp_optlen); | ||||||||||||||||||||||||||||||||||||
| 2056 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 2057 | *optlen = static_cast<size_t>(tmp_optlen); | ||||||||||||||||||||||||||||||||||||
| 2058 | if (result != 0 && level == IPPROTO_IPV6IPPROTO_IPV6 && optname == IPV6_V6ONLY26 | ||||||||||||||||||||||||||||||||||||
| 2059 | && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) | ||||||||||||||||||||||||||||||||||||
| 2060 | { | ||||||||||||||||||||||||||||||||||||
| 2061 | // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are | ||||||||||||||||||||||||||||||||||||
| 2062 | // only supported on Windows Vista and later. To simplify program logic | ||||||||||||||||||||||||||||||||||||
| 2063 | // we will fake success of getting this option and specify that the | ||||||||||||||||||||||||||||||||||||
| 2064 | // value is non-zero (i.e. true). This corresponds to the behavior of | ||||||||||||||||||||||||||||||||||||
| 2065 | // IPv6 sockets on Windows platforms pre-Vista. | ||||||||||||||||||||||||||||||||||||
| 2066 | *static_cast<DWORD*>(optval) = 1; | ||||||||||||||||||||||||||||||||||||
| 2067 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2068 | } | ||||||||||||||||||||||||||||||||||||
| 2069 | return result; | ||||||||||||||||||||||||||||||||||||
| 2070 | } | ||||||||||||||||||||||||||||||||||||
| 2071 | } | ||||||||||||||||||||||||||||||||||||
| 2072 | ec = boost::asio::error::fault; | ||||||||||||||||||||||||||||||||||||
| 2073 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2074 | #elif defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2075 | int result = call_getsockopt(&msghdr::msg_namelen, | ||||||||||||||||||||||||||||||||||||
| 2076 | s, level, optname, optval, optlen); | ||||||||||||||||||||||||||||||||||||
| 2077 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 2078 | if (result != 0 && level == IPPROTO_IPV6IPPROTO_IPV6 && optname == IPV6_V6ONLY26 | ||||||||||||||||||||||||||||||||||||
| 2079 | && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) | ||||||||||||||||||||||||||||||||||||
| 2080 | { | ||||||||||||||||||||||||||||||||||||
| 2081 | // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are only | ||||||||||||||||||||||||||||||||||||
| 2082 | // supported on Windows Vista and later. To simplify program logic we will | ||||||||||||||||||||||||||||||||||||
| 2083 | // fake success of getting this option and specify that the value is | ||||||||||||||||||||||||||||||||||||
| 2084 | // non-zero (i.e. true). This corresponds to the behavior of IPv6 sockets | ||||||||||||||||||||||||||||||||||||
| 2085 | // on Windows platforms pre-Vista. | ||||||||||||||||||||||||||||||||||||
| 2086 | *static_cast<DWORD*>(optval) = 1; | ||||||||||||||||||||||||||||||||||||
| 2087 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2088 | } | ||||||||||||||||||||||||||||||||||||
| 2089 | return result; | ||||||||||||||||||||||||||||||||||||
| 2090 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2091 | int result = call_getsockopt(&msghdr::msg_namelen, | ||||||||||||||||||||||||||||||||||||
| 2092 | s, level, optname, optval, optlen); | ||||||||||||||||||||||||||||||||||||
| 2093 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 2094 | #if defined(__linux__1) | ||||||||||||||||||||||||||||||||||||
| 2095 | if (result == 0 && level == SOL_SOCKET1 && *optlen == sizeof(int) | ||||||||||||||||||||||||||||||||||||
| 2096 | && (optname == SO_SNDBUF7 || optname == SO_RCVBUF8)) | ||||||||||||||||||||||||||||||||||||
| 2097 | { | ||||||||||||||||||||||||||||||||||||
| 2098 | // On Linux, setting SO_SNDBUF or SO_RCVBUF to N actually causes the kernel | ||||||||||||||||||||||||||||||||||||
| 2099 | // to set the buffer size to N*2. Linux puts additional stuff into the | ||||||||||||||||||||||||||||||||||||
| 2100 | // buffers so that only about half is actually available to the application. | ||||||||||||||||||||||||||||||||||||
| 2101 | // The retrieved value is divided by 2 here to make it appear as though the | ||||||||||||||||||||||||||||||||||||
| 2102 | // correct value has been set. | ||||||||||||||||||||||||||||||||||||
| 2103 | *static_cast<int*>(optval) /= 2; | ||||||||||||||||||||||||||||||||||||
| 2104 | } | ||||||||||||||||||||||||||||||||||||
| 2105 | #endif // defined(__linux__) | ||||||||||||||||||||||||||||||||||||
| 2106 | return result; | ||||||||||||||||||||||||||||||||||||
| 2107 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2108 | } | ||||||||||||||||||||||||||||||||||||
| 2109 | |||||||||||||||||||||||||||||||||||||
| 2110 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||||
| 2111 | inline int call_getpeername(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||||
| 2112 | socket_type s, void* addr, std::size_t* addrlen) | ||||||||||||||||||||||||||||||||||||
| 2113 | { | ||||||||||||||||||||||||||||||||||||
| 2114 | SockLenType tmp_addrlen = (SockLenType)*addrlen; | ||||||||||||||||||||||||||||||||||||
| 2115 | int result = ::getpeername(s, | ||||||||||||||||||||||||||||||||||||
| 2116 | static_cast<socket_addr_type*>(addr), &tmp_addrlen); | ||||||||||||||||||||||||||||||||||||
| 2117 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||||
| 2118 | return result; | ||||||||||||||||||||||||||||||||||||
| 2119 | } | ||||||||||||||||||||||||||||||||||||
| 2120 | |||||||||||||||||||||||||||||||||||||
| 2121 | int getpeername(socket_type s, void* addr, std::size_t* addrlen, | ||||||||||||||||||||||||||||||||||||
| 2122 | bool cached, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2123 | { | ||||||||||||||||||||||||||||||||||||
| 2124 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 2125 | { | ||||||||||||||||||||||||||||||||||||
| 2126 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 2127 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2128 | } | ||||||||||||||||||||||||||||||||||||
| 2129 | |||||||||||||||||||||||||||||||||||||
| 2130 | #if defined(BOOST_ASIO_WINDOWS) && !defined(BOOST_ASIO_WINDOWS_APP) \ | ||||||||||||||||||||||||||||||||||||
| 2131 | || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2132 | if (cached) | ||||||||||||||||||||||||||||||||||||
| 2133 | { | ||||||||||||||||||||||||||||||||||||
| 2134 | // Check if socket is still connected. | ||||||||||||||||||||||||||||||||||||
| 2135 | DWORD connect_time = 0; | ||||||||||||||||||||||||||||||||||||
| 2136 | size_t connect_time_len = sizeof(connect_time); | ||||||||||||||||||||||||||||||||||||
| 2137 | if (socket_ops::getsockopt(s, 0, SOL_SOCKET1, SO_CONNECT_TIME, | ||||||||||||||||||||||||||||||||||||
| 2138 | &connect_time, &connect_time_len, ec) == socket_error_retval) | ||||||||||||||||||||||||||||||||||||
| 2139 | { | ||||||||||||||||||||||||||||||||||||
| 2140 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2141 | } | ||||||||||||||||||||||||||||||||||||
| 2142 | if (connect_time == 0xFFFFFFFF) | ||||||||||||||||||||||||||||||||||||
| 2143 | { | ||||||||||||||||||||||||||||||||||||
| 2144 | ec = boost::asio::error::not_connected; | ||||||||||||||||||||||||||||||||||||
| 2145 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2146 | } | ||||||||||||||||||||||||||||||||||||
| 2147 | |||||||||||||||||||||||||||||||||||||
| 2148 | // The cached value is still valid. | ||||||||||||||||||||||||||||||||||||
| 2149 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2150 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2151 | } | ||||||||||||||||||||||||||||||||||||
| 2152 | #else // defined(BOOST_ASIO_WINDOWS) && !defined(BOOST_ASIO_WINDOWS_APP) | ||||||||||||||||||||||||||||||||||||
| 2153 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2154 | (void)cached; | ||||||||||||||||||||||||||||||||||||
| 2155 | #endif // defined(BOOST_ASIO_WINDOWS) && !defined(BOOST_ASIO_WINDOWS_APP) | ||||||||||||||||||||||||||||||||||||
| 2156 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2157 | |||||||||||||||||||||||||||||||||||||
| 2158 | int result = call_getpeername(&msghdr::msg_namelen, s, addr, addrlen); | ||||||||||||||||||||||||||||||||||||
| 2159 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 2160 | return result; | ||||||||||||||||||||||||||||||||||||
| 2161 | } | ||||||||||||||||||||||||||||||||||||
| 2162 | |||||||||||||||||||||||||||||||||||||
| 2163 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||||
| 2164 | inline int call_getsockname(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||||
| 2165 | socket_type s, void* addr, std::size_t* addrlen) | ||||||||||||||||||||||||||||||||||||
| 2166 | { | ||||||||||||||||||||||||||||||||||||
| 2167 | SockLenType tmp_addrlen = (SockLenType)*addrlen; | ||||||||||||||||||||||||||||||||||||
| 2168 | int result = ::getsockname(s, | ||||||||||||||||||||||||||||||||||||
| 2169 | static_cast<socket_addr_type*>(addr), &tmp_addrlen); | ||||||||||||||||||||||||||||||||||||
| 2170 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||||
| 2171 | return result; | ||||||||||||||||||||||||||||||||||||
| 2172 | } | ||||||||||||||||||||||||||||||||||||
| 2173 | |||||||||||||||||||||||||||||||||||||
| 2174 | int getsockname(socket_type s, void* addr, | ||||||||||||||||||||||||||||||||||||
| 2175 | std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2176 | { | ||||||||||||||||||||||||||||||||||||
| 2177 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 2178 | { | ||||||||||||||||||||||||||||||||||||
| 2179 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 2180 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2181 | } | ||||||||||||||||||||||||||||||||||||
| 2182 | |||||||||||||||||||||||||||||||||||||
| 2183 | int result = call_getsockname(&msghdr::msg_namelen, s, addr, addrlen); | ||||||||||||||||||||||||||||||||||||
| 2184 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 2185 | return result; | ||||||||||||||||||||||||||||||||||||
| 2186 | } | ||||||||||||||||||||||||||||||||||||
| 2187 | |||||||||||||||||||||||||||||||||||||
| 2188 | int ioctl(socket_type s, state_type& state, int cmd, | ||||||||||||||||||||||||||||||||||||
| 2189 | ioctl_arg_type* arg, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2190 | { | ||||||||||||||||||||||||||||||||||||
| 2191 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 2192 | { | ||||||||||||||||||||||||||||||||||||
| 2193 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 2194 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2195 | } | ||||||||||||||||||||||||||||||||||||
| 2196 | |||||||||||||||||||||||||||||||||||||
| 2197 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2198 | int result = ::ioctlsocket(s, cmd, arg); | ||||||||||||||||||||||||||||||||||||
| 2199 | #elif defined(__MACH__) && defined(__APPLE__) \ | ||||||||||||||||||||||||||||||||||||
| 2200 | || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) | ||||||||||||||||||||||||||||||||||||
| 2201 | int result = ::ioctl(s, static_cast<unsigned int>(cmd), arg); | ||||||||||||||||||||||||||||||||||||
| 2202 | #else | ||||||||||||||||||||||||||||||||||||
| 2203 | int result = ::ioctl(s, cmd, arg); | ||||||||||||||||||||||||||||||||||||
| 2204 | #endif | ||||||||||||||||||||||||||||||||||||
| 2205 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 2206 | if (result >= 0) | ||||||||||||||||||||||||||||||||||||
| 2207 | { | ||||||||||||||||||||||||||||||||||||
| 2208 | // When updating the non-blocking mode we always perform the ioctl syscall, | ||||||||||||||||||||||||||||||||||||
| 2209 | // even if the flags would otherwise indicate that the socket is already in | ||||||||||||||||||||||||||||||||||||
| 2210 | // the correct state. This ensures that the underlying socket is put into | ||||||||||||||||||||||||||||||||||||
| 2211 | // the state that has been requested by the user. If the ioctl syscall was | ||||||||||||||||||||||||||||||||||||
| 2212 | // successful then we need to update the flags to match. | ||||||||||||||||||||||||||||||||||||
| 2213 | if (cmd == static_cast<int>(FIONBIO0x5421)) | ||||||||||||||||||||||||||||||||||||
| 2214 | { | ||||||||||||||||||||||||||||||||||||
| 2215 | if (*arg) | ||||||||||||||||||||||||||||||||||||
| 2216 | { | ||||||||||||||||||||||||||||||||||||
| 2217 | state |= user_set_non_blocking; | ||||||||||||||||||||||||||||||||||||
| 2218 | } | ||||||||||||||||||||||||||||||||||||
| 2219 | else | ||||||||||||||||||||||||||||||||||||
| 2220 | { | ||||||||||||||||||||||||||||||||||||
| 2221 | // Clearing the non-blocking mode always overrides any internally-set | ||||||||||||||||||||||||||||||||||||
| 2222 | // non-blocking flag. Any subsequent asynchronous operations will need | ||||||||||||||||||||||||||||||||||||
| 2223 | // to re-enable non-blocking I/O. | ||||||||||||||||||||||||||||||||||||
| 2224 | state &= ~(user_set_non_blocking | internal_non_blocking); | ||||||||||||||||||||||||||||||||||||
| 2225 | } | ||||||||||||||||||||||||||||||||||||
| 2226 | } | ||||||||||||||||||||||||||||||||||||
| 2227 | } | ||||||||||||||||||||||||||||||||||||
| 2228 | |||||||||||||||||||||||||||||||||||||
| 2229 | return result; | ||||||||||||||||||||||||||||||||||||
| 2230 | } | ||||||||||||||||||||||||||||||||||||
| 2231 | |||||||||||||||||||||||||||||||||||||
| 2232 | int select(int nfds, fd_set* readfds, fd_set* writefds, | ||||||||||||||||||||||||||||||||||||
| 2233 | fd_set* exceptfds, timeval* timeout, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2234 | { | ||||||||||||||||||||||||||||||||||||
| 2235 | #if defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||||
| 2236 | exceptfds = 0; | ||||||||||||||||||||||||||||||||||||
| 2237 | #endif // defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||||
| 2238 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2239 | if (!readfds && !writefds && !exceptfds && timeout) | ||||||||||||||||||||||||||||||||||||
| 2240 | { | ||||||||||||||||||||||||||||||||||||
| 2241 | DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; | ||||||||||||||||||||||||||||||||||||
| 2242 | if (milliseconds == 0) | ||||||||||||||||||||||||||||||||||||
| 2243 | milliseconds = 1; // Force context switch. | ||||||||||||||||||||||||||||||||||||
| 2244 | ::Sleep(milliseconds); | ||||||||||||||||||||||||||||||||||||
| 2245 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2246 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2247 | } | ||||||||||||||||||||||||||||||||||||
| 2248 | |||||||||||||||||||||||||||||||||||||
| 2249 | // The select() call allows timeout values measured in microseconds, but the | ||||||||||||||||||||||||||||||||||||
| 2250 | // system clock (as wrapped by boost::posix_time::microsec_clock) typically | ||||||||||||||||||||||||||||||||||||
| 2251 | // has a resolution of 10 milliseconds. This can lead to a spinning select | ||||||||||||||||||||||||||||||||||||
| 2252 | // reactor, meaning increased CPU usage, when waiting for the earliest | ||||||||||||||||||||||||||||||||||||
| 2253 | // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight | ||||||||||||||||||||||||||||||||||||
| 2254 | // spin we'll use a minimum timeout of 1 millisecond. | ||||||||||||||||||||||||||||||||||||
| 2255 | if (timeout && timeout->tv_sec == 0 | ||||||||||||||||||||||||||||||||||||
| 2256 | && timeout->tv_usec > 0 && timeout->tv_usec < 1000) | ||||||||||||||||||||||||||||||||||||
| 2257 | timeout->tv_usec = 1000; | ||||||||||||||||||||||||||||||||||||
| 2258 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2259 | |||||||||||||||||||||||||||||||||||||
| 2260 | #if defined(__hpux) && defined(__SELECT) | ||||||||||||||||||||||||||||||||||||
| 2261 | timespec ts; | ||||||||||||||||||||||||||||||||||||
| 2262 | ts.tv_sec = timeout ? timeout->tv_sec : 0; | ||||||||||||||||||||||||||||||||||||
| 2263 | ts.tv_nsec = timeout ? timeout->tv_usec * 1000 : 0; | ||||||||||||||||||||||||||||||||||||
| 2264 | int result = ::pselect(nfds, readfds, | ||||||||||||||||||||||||||||||||||||
| 2265 | writefds, exceptfds, timeout ? &ts : 0, 0); | ||||||||||||||||||||||||||||||||||||
| 2266 | #else | ||||||||||||||||||||||||||||||||||||
| 2267 | int result = ::select(nfds, readfds, writefds, exceptfds, timeout); | ||||||||||||||||||||||||||||||||||||
| 2268 | #endif | ||||||||||||||||||||||||||||||||||||
| 2269 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 2270 | return result; | ||||||||||||||||||||||||||||||||||||
| 2271 | } | ||||||||||||||||||||||||||||||||||||
| 2272 | |||||||||||||||||||||||||||||||||||||
| 2273 | int poll_read(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||||
| 2274 | int msec, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2275 | { | ||||||||||||||||||||||||||||||||||||
| 2276 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 2277 | { | ||||||||||||||||||||||||||||||||||||
| 2278 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 2279 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2280 | } | ||||||||||||||||||||||||||||||||||||
| 2281 | |||||||||||||||||||||||||||||||||||||
| 2282 | #if defined(BOOST_ASIO_WINDOWS) \ | ||||||||||||||||||||||||||||||||||||
| 2283 | || defined(__CYGWIN__) \ | ||||||||||||||||||||||||||||||||||||
| 2284 | || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2285 | fd_set fds; | ||||||||||||||||||||||||||||||||||||
| 2286 | FD_ZERO(&fds)do { unsigned int __i; fd_set *__arr = (&fds); for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i) ((__arr )->fds_bits)[__i] = 0; } while (0); | ||||||||||||||||||||||||||||||||||||
| 2287 | FD_SET(s, &fds)((void) (((&fds)->fds_bits)[((s) / (8 * (int) sizeof ( __fd_mask)))] |= ((__fd_mask) (1UL << ((s) % (8 * (int) sizeof (__fd_mask))))))); | ||||||||||||||||||||||||||||||||||||
| 2288 | timeval timeout_obj; | ||||||||||||||||||||||||||||||||||||
| 2289 | timeval* timeout; | ||||||||||||||||||||||||||||||||||||
| 2290 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 2291 | { | ||||||||||||||||||||||||||||||||||||
| 2292 | timeout_obj.tv_sec = 0; | ||||||||||||||||||||||||||||||||||||
| 2293 | timeout_obj.tv_usec = 0; | ||||||||||||||||||||||||||||||||||||
| 2294 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||||
| 2295 | } | ||||||||||||||||||||||||||||||||||||
| 2296 | else if (msec >= 0) | ||||||||||||||||||||||||||||||||||||
| 2297 | { | ||||||||||||||||||||||||||||||||||||
| 2298 | timeout_obj.tv_sec = msec / 1000; | ||||||||||||||||||||||||||||||||||||
| 2299 | timeout_obj.tv_usec = (msec % 1000) * 1000; | ||||||||||||||||||||||||||||||||||||
| 2300 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||||
| 2301 | } | ||||||||||||||||||||||||||||||||||||
| 2302 | else | ||||||||||||||||||||||||||||||||||||
| 2303 | timeout = 0; | ||||||||||||||||||||||||||||||||||||
| 2304 | int result = ::select(s + 1, &fds, 0, 0, timeout); | ||||||||||||||||||||||||||||||||||||
| 2305 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 2306 | #else // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||||
| 2307 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2308 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2309 | pollfd fds; | ||||||||||||||||||||||||||||||||||||
| 2310 | fds.fd = s; | ||||||||||||||||||||||||||||||||||||
| 2311 | fds.events = POLLIN0x001; | ||||||||||||||||||||||||||||||||||||
| 2312 | fds.revents = 0; | ||||||||||||||||||||||||||||||||||||
| 2313 | int timeout = (state & user_set_non_blocking) ? 0 : msec; | ||||||||||||||||||||||||||||||||||||
| 2314 | int result = ::poll(&fds, 1, timeout); | ||||||||||||||||||||||||||||||||||||
| 2315 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 2316 | #endif // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||||
| 2317 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2318 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2319 | if (result == 0) | ||||||||||||||||||||||||||||||||||||
| 2320 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 2321 | ec = boost::asio::error::would_block; | ||||||||||||||||||||||||||||||||||||
| 2322 | return result; | ||||||||||||||||||||||||||||||||||||
| 2323 | } | ||||||||||||||||||||||||||||||||||||
| 2324 | |||||||||||||||||||||||||||||||||||||
| 2325 | int poll_write(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||||
| 2326 | int msec, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2327 | { | ||||||||||||||||||||||||||||||||||||
| 2328 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 2329 | { | ||||||||||||||||||||||||||||||||||||
| 2330 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 2331 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2332 | } | ||||||||||||||||||||||||||||||||||||
| 2333 | |||||||||||||||||||||||||||||||||||||
| 2334 | #if defined(BOOST_ASIO_WINDOWS) \ | ||||||||||||||||||||||||||||||||||||
| 2335 | || defined(__CYGWIN__) \ | ||||||||||||||||||||||||||||||||||||
| 2336 | || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2337 | fd_set fds; | ||||||||||||||||||||||||||||||||||||
| 2338 | FD_ZERO(&fds)do { unsigned int __i; fd_set *__arr = (&fds); for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i) ((__arr )->fds_bits)[__i] = 0; } while (0); | ||||||||||||||||||||||||||||||||||||
| 2339 | FD_SET(s, &fds)((void) (((&fds)->fds_bits)[((s) / (8 * (int) sizeof ( __fd_mask)))] |= ((__fd_mask) (1UL << ((s) % (8 * (int) sizeof (__fd_mask))))))); | ||||||||||||||||||||||||||||||||||||
| 2340 | timeval timeout_obj; | ||||||||||||||||||||||||||||||||||||
| 2341 | timeval* timeout; | ||||||||||||||||||||||||||||||||||||
| 2342 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 2343 | { | ||||||||||||||||||||||||||||||||||||
| 2344 | timeout_obj.tv_sec = 0; | ||||||||||||||||||||||||||||||||||||
| 2345 | timeout_obj.tv_usec = 0; | ||||||||||||||||||||||||||||||||||||
| 2346 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||||
| 2347 | } | ||||||||||||||||||||||||||||||||||||
| 2348 | else if (msec >= 0) | ||||||||||||||||||||||||||||||||||||
| 2349 | { | ||||||||||||||||||||||||||||||||||||
| 2350 | timeout_obj.tv_sec = msec / 1000; | ||||||||||||||||||||||||||||||||||||
| 2351 | timeout_obj.tv_usec = (msec % 1000) * 1000; | ||||||||||||||||||||||||||||||||||||
| 2352 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||||
| 2353 | } | ||||||||||||||||||||||||||||||||||||
| 2354 | else | ||||||||||||||||||||||||||||||||||||
| 2355 | timeout = 0; | ||||||||||||||||||||||||||||||||||||
| 2356 | int result = ::select(s + 1, 0, &fds, 0, timeout); | ||||||||||||||||||||||||||||||||||||
| 2357 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 2358 | #else // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||||
| 2359 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2360 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2361 | pollfd fds; | ||||||||||||||||||||||||||||||||||||
| 2362 | fds.fd = s; | ||||||||||||||||||||||||||||||||||||
| 2363 | fds.events = POLLOUT0x004; | ||||||||||||||||||||||||||||||||||||
| 2364 | fds.revents = 0; | ||||||||||||||||||||||||||||||||||||
| 2365 | int timeout = (state & user_set_non_blocking) ? 0 : msec; | ||||||||||||||||||||||||||||||||||||
| 2366 | int result = ::poll(&fds, 1, timeout); | ||||||||||||||||||||||||||||||||||||
| 2367 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 2368 | #endif // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||||
| 2369 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2370 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2371 | if (result == 0) | ||||||||||||||||||||||||||||||||||||
| 2372 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 2373 | ec = boost::asio::error::would_block; | ||||||||||||||||||||||||||||||||||||
| 2374 | return result; | ||||||||||||||||||||||||||||||||||||
| 2375 | } | ||||||||||||||||||||||||||||||||||||
| 2376 | |||||||||||||||||||||||||||||||||||||
| 2377 | int poll_error(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||||
| 2378 | int msec, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2379 | { | ||||||||||||||||||||||||||||||||||||
| 2380 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 2381 | { | ||||||||||||||||||||||||||||||||||||
| 2382 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 2383 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2384 | } | ||||||||||||||||||||||||||||||||||||
| 2385 | |||||||||||||||||||||||||||||||||||||
| 2386 | #if defined(BOOST_ASIO_WINDOWS) \ | ||||||||||||||||||||||||||||||||||||
| 2387 | || defined(__CYGWIN__) \ | ||||||||||||||||||||||||||||||||||||
| 2388 | || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2389 | fd_set fds; | ||||||||||||||||||||||||||||||||||||
| 2390 | FD_ZERO(&fds)do { unsigned int __i; fd_set *__arr = (&fds); for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i) ((__arr )->fds_bits)[__i] = 0; } while (0); | ||||||||||||||||||||||||||||||||||||
| 2391 | FD_SET(s, &fds)((void) (((&fds)->fds_bits)[((s) / (8 * (int) sizeof ( __fd_mask)))] |= ((__fd_mask) (1UL << ((s) % (8 * (int) sizeof (__fd_mask))))))); | ||||||||||||||||||||||||||||||||||||
| 2392 | timeval timeout_obj; | ||||||||||||||||||||||||||||||||||||
| 2393 | timeval* timeout; | ||||||||||||||||||||||||||||||||||||
| 2394 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 2395 | { | ||||||||||||||||||||||||||||||||||||
| 2396 | timeout_obj.tv_sec = 0; | ||||||||||||||||||||||||||||||||||||
| 2397 | timeout_obj.tv_usec = 0; | ||||||||||||||||||||||||||||||||||||
| 2398 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||||
| 2399 | } | ||||||||||||||||||||||||||||||||||||
| 2400 | else if (msec >= 0) | ||||||||||||||||||||||||||||||||||||
| 2401 | { | ||||||||||||||||||||||||||||||||||||
| 2402 | timeout_obj.tv_sec = msec / 1000; | ||||||||||||||||||||||||||||||||||||
| 2403 | timeout_obj.tv_usec = (msec % 1000) * 1000; | ||||||||||||||||||||||||||||||||||||
| 2404 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||||
| 2405 | } | ||||||||||||||||||||||||||||||||||||
| 2406 | else | ||||||||||||||||||||||||||||||||||||
| 2407 | timeout = 0; | ||||||||||||||||||||||||||||||||||||
| 2408 | int result = ::select(s + 1, 0, 0, &fds, timeout); | ||||||||||||||||||||||||||||||||||||
| 2409 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 2410 | #else // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||||
| 2411 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2412 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2413 | pollfd fds; | ||||||||||||||||||||||||||||||||||||
| 2414 | fds.fd = s; | ||||||||||||||||||||||||||||||||||||
| 2415 | fds.events = POLLPRI0x002 | POLLERR0x008 | POLLHUP0x010; | ||||||||||||||||||||||||||||||||||||
| 2416 | fds.revents = 0; | ||||||||||||||||||||||||||||||||||||
| 2417 | int timeout = (state & user_set_non_blocking) ? 0 : msec; | ||||||||||||||||||||||||||||||||||||
| 2418 | int result = ::poll(&fds, 1, timeout); | ||||||||||||||||||||||||||||||||||||
| 2419 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 2420 | #endif // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||||
| 2421 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2422 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2423 | if (result == 0) | ||||||||||||||||||||||||||||||||||||
| 2424 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||||
| 2425 | ec = boost::asio::error::would_block; | ||||||||||||||||||||||||||||||||||||
| 2426 | return result; | ||||||||||||||||||||||||||||||||||||
| 2427 | } | ||||||||||||||||||||||||||||||||||||
| 2428 | |||||||||||||||||||||||||||||||||||||
| 2429 | int poll_connect(socket_type s, int msec, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2430 | { | ||||||||||||||||||||||||||||||||||||
| 2431 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||||
| 2432 | { | ||||||||||||||||||||||||||||||||||||
| 2433 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||||
| 2434 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||||
| 2435 | } | ||||||||||||||||||||||||||||||||||||
| 2436 | |||||||||||||||||||||||||||||||||||||
| 2437 | #if defined(BOOST_ASIO_WINDOWS) \ | ||||||||||||||||||||||||||||||||||||
| 2438 | || defined(__CYGWIN__) \ | ||||||||||||||||||||||||||||||||||||
| 2439 | || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2440 | fd_set write_fds; | ||||||||||||||||||||||||||||||||||||
| 2441 | FD_ZERO(&write_fds)do { unsigned int __i; fd_set *__arr = (&write_fds); for ( __i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i ) ((__arr)->fds_bits)[__i] = 0; } while (0); | ||||||||||||||||||||||||||||||||||||
| 2442 | FD_SET(s, &write_fds)((void) (((&write_fds)->fds_bits)[((s) / (8 * (int) sizeof (__fd_mask)))] |= ((__fd_mask) (1UL << ((s) % (8 * (int ) sizeof (__fd_mask))))))); | ||||||||||||||||||||||||||||||||||||
| 2443 | fd_set except_fds; | ||||||||||||||||||||||||||||||||||||
| 2444 | FD_ZERO(&except_fds)do { unsigned int __i; fd_set *__arr = (&except_fds); for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i ) ((__arr)->fds_bits)[__i] = 0; } while (0); | ||||||||||||||||||||||||||||||||||||
| 2445 | FD_SET(s, &except_fds)((void) (((&except_fds)->fds_bits)[((s) / (8 * (int) sizeof (__fd_mask)))] |= ((__fd_mask) (1UL << ((s) % (8 * (int ) sizeof (__fd_mask))))))); | ||||||||||||||||||||||||||||||||||||
| 2446 | timeval timeout_obj; | ||||||||||||||||||||||||||||||||||||
| 2447 | timeval* timeout; | ||||||||||||||||||||||||||||||||||||
| 2448 | if (msec >= 0) | ||||||||||||||||||||||||||||||||||||
| 2449 | { | ||||||||||||||||||||||||||||||||||||
| 2450 | timeout_obj.tv_sec = msec / 1000; | ||||||||||||||||||||||||||||||||||||
| 2451 | timeout_obj.tv_usec = (msec % 1000) * 1000; | ||||||||||||||||||||||||||||||||||||
| 2452 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||||
| 2453 | } | ||||||||||||||||||||||||||||||||||||
| 2454 | else | ||||||||||||||||||||||||||||||||||||
| 2455 | timeout = 0; | ||||||||||||||||||||||||||||||||||||
| 2456 | int result = ::select(s + 1, 0, &write_fds, &except_fds, timeout); | ||||||||||||||||||||||||||||||||||||
| 2457 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 2458 | return result; | ||||||||||||||||||||||||||||||||||||
| 2459 | #else // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||||
| 2460 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2461 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2462 | pollfd fds; | ||||||||||||||||||||||||||||||||||||
| 2463 | fds.fd = s; | ||||||||||||||||||||||||||||||||||||
| 2464 | fds.events = POLLOUT0x004; | ||||||||||||||||||||||||||||||||||||
| 2465 | fds.revents = 0; | ||||||||||||||||||||||||||||||||||||
| 2466 | int result = ::poll(&fds, 1, msec); | ||||||||||||||||||||||||||||||||||||
| 2467 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||||
| 2468 | return result; | ||||||||||||||||||||||||||||||||||||
| 2469 | #endif // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||||
| 2470 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2471 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||||
| 2472 | } | ||||||||||||||||||||||||||||||||||||
| 2473 | |||||||||||||||||||||||||||||||||||||
| 2474 | #endif // !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 2475 | |||||||||||||||||||||||||||||||||||||
| 2476 | const char* inet_ntop(int af, const void* src, char* dest, size_t length, | ||||||||||||||||||||||||||||||||||||
| 2477 | unsigned long scope_id, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2478 | { | ||||||||||||||||||||||||||||||||||||
| 2479 | clear_last_error(); | ||||||||||||||||||||||||||||||||||||
| 2480 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 2481 | using namespace std; // For sprintf. | ||||||||||||||||||||||||||||||||||||
| 2482 | const unsigned char* bytes = static_cast<const unsigned char*>(src); | ||||||||||||||||||||||||||||||||||||
| 2483 | if (af == BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||||
| 2484 | { | ||||||||||||||||||||||||||||||||||||
| 2485 | sprintf_s(dest, length, "%u.%u.%u.%u", | ||||||||||||||||||||||||||||||||||||
| 2486 | bytes[0], bytes[1], bytes[2], bytes[3]); | ||||||||||||||||||||||||||||||||||||
| 2487 | return dest; | ||||||||||||||||||||||||||||||||||||
| 2488 | } | ||||||||||||||||||||||||||||||||||||
| 2489 | else if (af == BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||||
| 2490 | { | ||||||||||||||||||||||||||||||||||||
| 2491 | size_t n = 0, b = 0, z = 0; | ||||||||||||||||||||||||||||||||||||
| 2492 | while (n < length && b < 16) | ||||||||||||||||||||||||||||||||||||
| 2493 | { | ||||||||||||||||||||||||||||||||||||
| 2494 | if (bytes[b] == 0 && bytes[b + 1] == 0 && z == 0) | ||||||||||||||||||||||||||||||||||||
| 2495 | { | ||||||||||||||||||||||||||||||||||||
| 2496 | do b += 2; while (b < 16 && bytes[b] == 0 && bytes[b + 1] == 0); | ||||||||||||||||||||||||||||||||||||
| 2497 | n += sprintf_s(dest + n, length - n, ":%s", b < 16 ? "" : ":"), ++z; | ||||||||||||||||||||||||||||||||||||
| 2498 | } | ||||||||||||||||||||||||||||||||||||
| 2499 | else | ||||||||||||||||||||||||||||||||||||
| 2500 | { | ||||||||||||||||||||||||||||||||||||
| 2501 | n += sprintf_s(dest + n, length - n, "%s%x", b ? ":" : "", | ||||||||||||||||||||||||||||||||||||
| 2502 | (static_cast<u_long_type>(bytes[b]) << 8) | bytes[b + 1]); | ||||||||||||||||||||||||||||||||||||
| 2503 | b += 2; | ||||||||||||||||||||||||||||||||||||
| 2504 | } | ||||||||||||||||||||||||||||||||||||
| 2505 | } | ||||||||||||||||||||||||||||||||||||
| 2506 | if (scope_id) | ||||||||||||||||||||||||||||||||||||
| 2507 | n += sprintf_s(dest + n, length - n, "%%%lu", scope_id); | ||||||||||||||||||||||||||||||||||||
| 2508 | return dest; | ||||||||||||||||||||||||||||||||||||
| 2509 | } | ||||||||||||||||||||||||||||||||||||
| 2510 | else | ||||||||||||||||||||||||||||||||||||
| 2511 | { | ||||||||||||||||||||||||||||||||||||
| 2512 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||||
| 2513 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2514 | } | ||||||||||||||||||||||||||||||||||||
| 2515 | #elif defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2516 | using namespace std; // For memcpy. | ||||||||||||||||||||||||||||||||||||
| 2517 | |||||||||||||||||||||||||||||||||||||
| 2518 | if (af != BOOST_ASIO_OS_DEF(AF_INET)2 && af != BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||||
| 2519 | { | ||||||||||||||||||||||||||||||||||||
| 2520 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||||
| 2521 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2522 | } | ||||||||||||||||||||||||||||||||||||
| 2523 | |||||||||||||||||||||||||||||||||||||
| 2524 | union | ||||||||||||||||||||||||||||||||||||
| 2525 | { | ||||||||||||||||||||||||||||||||||||
| 2526 | socket_addr_type base; | ||||||||||||||||||||||||||||||||||||
| 2527 | sockaddr_storage_type storage; | ||||||||||||||||||||||||||||||||||||
| 2528 | sockaddr_in4_type v4; | ||||||||||||||||||||||||||||||||||||
| 2529 | sockaddr_in6_type v6; | ||||||||||||||||||||||||||||||||||||
| 2530 | } address; | ||||||||||||||||||||||||||||||||||||
| 2531 | DWORD address_length; | ||||||||||||||||||||||||||||||||||||
| 2532 | if (af == BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||||
| 2533 | { | ||||||||||||||||||||||||||||||||||||
| 2534 | address_length = sizeof(sockaddr_in4_type); | ||||||||||||||||||||||||||||||||||||
| 2535 | address.v4.sin_family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||||
| 2536 | address.v4.sin_port = 0; | ||||||||||||||||||||||||||||||||||||
| 2537 | memcpy(&address.v4.sin_addr, src, sizeof(in4_addr_type)); | ||||||||||||||||||||||||||||||||||||
| 2538 | } | ||||||||||||||||||||||||||||||||||||
| 2539 | else // AF_INET6 | ||||||||||||||||||||||||||||||||||||
| 2540 | { | ||||||||||||||||||||||||||||||||||||
| 2541 | address_length = sizeof(sockaddr_in6_type); | ||||||||||||||||||||||||||||||||||||
| 2542 | address.v6.sin6_family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||||
| 2543 | address.v6.sin6_port = 0; | ||||||||||||||||||||||||||||||||||||
| 2544 | address.v6.sin6_flowinfo = 0; | ||||||||||||||||||||||||||||||||||||
| 2545 | address.v6.sin6_scope_id = scope_id; | ||||||||||||||||||||||||||||||||||||
| 2546 | memcpy(&address.v6.sin6_addr, src, sizeof(in6_addr_type)); | ||||||||||||||||||||||||||||||||||||
| 2547 | } | ||||||||||||||||||||||||||||||||||||
| 2548 | |||||||||||||||||||||||||||||||||||||
| 2549 | DWORD string_length = static_cast<DWORD>(length); | ||||||||||||||||||||||||||||||||||||
| 2550 | #if defined(BOOST_NO_ANSI_APIS) || (defined(_MSC_VER) && (_MSC_VER >= 1800)) | ||||||||||||||||||||||||||||||||||||
| 2551 | LPWSTR string_buffer = (LPWSTR)_alloca(length * sizeof(WCHAR)); | ||||||||||||||||||||||||||||||||||||
| 2552 | int result = ::WSAAddressToStringW(&address.base, | ||||||||||||||||||||||||||||||||||||
| 2553 | address_length, 0, string_buffer, &string_length); | ||||||||||||||||||||||||||||||||||||
| 2554 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 2555 | ::WideCharToMultiByte(CP_ACP, 0, string_buffer, -1, | ||||||||||||||||||||||||||||||||||||
| 2556 | dest, static_cast<int>(length), 0, 0); | ||||||||||||||||||||||||||||||||||||
| 2557 | #else | ||||||||||||||||||||||||||||||||||||
| 2558 | int result = ::WSAAddressToStringA(&address.base, | ||||||||||||||||||||||||||||||||||||
| 2559 | address_length, 0, dest, &string_length); | ||||||||||||||||||||||||||||||||||||
| 2560 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 2561 | #endif | ||||||||||||||||||||||||||||||||||||
| 2562 | |||||||||||||||||||||||||||||||||||||
| 2563 | // Windows may set error code on success. | ||||||||||||||||||||||||||||||||||||
| 2564 | if (result != socket_error_retval) | ||||||||||||||||||||||||||||||||||||
| 2565 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2566 | |||||||||||||||||||||||||||||||||||||
| 2567 | // Windows may not set an error code on failure. | ||||||||||||||||||||||||||||||||||||
| 2568 | else if (result == socket_error_retval && !ec) | ||||||||||||||||||||||||||||||||||||
| 2569 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2570 | |||||||||||||||||||||||||||||||||||||
| 2571 | return result == socket_error_retval ? 0 : dest; | ||||||||||||||||||||||||||||||||||||
| 2572 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2573 | const char* result = ::inet_ntop(af, src, dest, static_cast<int>(length)); | ||||||||||||||||||||||||||||||||||||
| 2574 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 2575 | if (result == 0 && !ec) | ||||||||||||||||||||||||||||||||||||
| 2576 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2577 | if (result != 0 && af == BOOST_ASIO_OS_DEF(AF_INET6)10 && scope_id != 0) | ||||||||||||||||||||||||||||||||||||
| 2578 | { | ||||||||||||||||||||||||||||||||||||
| 2579 | using namespace std; // For strcat and sprintf. | ||||||||||||||||||||||||||||||||||||
| 2580 | char if_name[(IF_NAMESIZE16 > 21 ? IF_NAMESIZE16 : 21) + 1] = "%"; | ||||||||||||||||||||||||||||||||||||
| 2581 | const in6_addr_type* ipv6_address = static_cast<const in6_addr_type*>(src); | ||||||||||||||||||||||||||||||||||||
| 2582 | bool is_link_local = ((ipv6_address->s6_addr__in6_u.__u6_addr8[0] == 0xfe) | ||||||||||||||||||||||||||||||||||||
| 2583 | && ((ipv6_address->s6_addr__in6_u.__u6_addr8[1] & 0xc0) == 0x80)); | ||||||||||||||||||||||||||||||||||||
| 2584 | bool is_multicast_link_local = ((ipv6_address->s6_addr__in6_u.__u6_addr8[0] == 0xff) | ||||||||||||||||||||||||||||||||||||
| 2585 | && ((ipv6_address->s6_addr__in6_u.__u6_addr8[1] & 0x0f) == 0x02)); | ||||||||||||||||||||||||||||||||||||
| 2586 | if ((!is_link_local && !is_multicast_link_local) | ||||||||||||||||||||||||||||||||||||
| 2587 | || if_indextoname(static_cast<unsigned>(scope_id), if_name + 1) == 0) | ||||||||||||||||||||||||||||||||||||
| 2588 | #if defined(BOOST_ASIO_HAS_SNPRINTF) | ||||||||||||||||||||||||||||||||||||
| 2589 | snprintf(if_name + 1, sizeof(if_name) - 1, "%lu", scope_id); | ||||||||||||||||||||||||||||||||||||
| 2590 | #elif defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 2591 | sprintf_s(if_name + 1, sizeof(if_name) -1, "%lu", scope_id); | ||||||||||||||||||||||||||||||||||||
| 2592 | #else // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 2593 | sprintf(if_name + 1, "%lu", scope_id); | ||||||||||||||||||||||||||||||||||||
| 2594 | #endif // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 2595 | strcat(dest, if_name); | ||||||||||||||||||||||||||||||||||||
| 2596 | } | ||||||||||||||||||||||||||||||||||||
| 2597 | return result; | ||||||||||||||||||||||||||||||||||||
| 2598 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2599 | } | ||||||||||||||||||||||||||||||||||||
| 2600 | |||||||||||||||||||||||||||||||||||||
| 2601 | int inet_pton(int af, const char* src, void* dest, | ||||||||||||||||||||||||||||||||||||
| 2602 | unsigned long* scope_id, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2603 | { | ||||||||||||||||||||||||||||||||||||
| 2604 | clear_last_error(); | ||||||||||||||||||||||||||||||||||||
| 2605 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 2606 | using namespace std; // For sscanf. | ||||||||||||||||||||||||||||||||||||
| 2607 | unsigned char* bytes = static_cast<unsigned char*>(dest); | ||||||||||||||||||||||||||||||||||||
| 2608 | if (af == BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||||
| 2609 | { | ||||||||||||||||||||||||||||||||||||
| 2610 | unsigned int b0, b1, b2, b3; | ||||||||||||||||||||||||||||||||||||
| 2611 | if (sscanf_s(src, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) != 4) | ||||||||||||||||||||||||||||||||||||
| 2612 | { | ||||||||||||||||||||||||||||||||||||
| 2613 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2614 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2615 | } | ||||||||||||||||||||||||||||||||||||
| 2616 | if (b0 > 255 || b1 > 255 || b2 > 255 || b3 > 255) | ||||||||||||||||||||||||||||||||||||
| 2617 | { | ||||||||||||||||||||||||||||||||||||
| 2618 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2619 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2620 | } | ||||||||||||||||||||||||||||||||||||
| 2621 | bytes[0] = static_cast<unsigned char>(b0); | ||||||||||||||||||||||||||||||||||||
| 2622 | bytes[1] = static_cast<unsigned char>(b1); | ||||||||||||||||||||||||||||||||||||
| 2623 | bytes[2] = static_cast<unsigned char>(b2); | ||||||||||||||||||||||||||||||||||||
| 2624 | bytes[3] = static_cast<unsigned char>(b3); | ||||||||||||||||||||||||||||||||||||
| 2625 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2626 | return 1; | ||||||||||||||||||||||||||||||||||||
| 2627 | } | ||||||||||||||||||||||||||||||||||||
| 2628 | else if (af == BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||||
| 2629 | { | ||||||||||||||||||||||||||||||||||||
| 2630 | unsigned char* bytes = static_cast<unsigned char*>(dest); | ||||||||||||||||||||||||||||||||||||
| 2631 | std::memset(bytes, 0, 16); | ||||||||||||||||||||||||||||||||||||
| 2632 | unsigned char back_bytes[16] = { 0 }; | ||||||||||||||||||||||||||||||||||||
| 2633 | int num_front_bytes = 0, num_back_bytes = 0; | ||||||||||||||||||||||||||||||||||||
| 2634 | const char* p = src; | ||||||||||||||||||||||||||||||||||||
| 2635 | |||||||||||||||||||||||||||||||||||||
| 2636 | enum { fword, fcolon, bword, scope, done } state = fword; | ||||||||||||||||||||||||||||||||||||
| 2637 | unsigned long current_word = 0; | ||||||||||||||||||||||||||||||||||||
| 2638 | while (state != done) | ||||||||||||||||||||||||||||||||||||
| 2639 | { | ||||||||||||||||||||||||||||||||||||
| 2640 | if (current_word > 0xFFFF) | ||||||||||||||||||||||||||||||||||||
| 2641 | { | ||||||||||||||||||||||||||||||||||||
| 2642 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2643 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2644 | } | ||||||||||||||||||||||||||||||||||||
| 2645 | |||||||||||||||||||||||||||||||||||||
| 2646 | switch (state) | ||||||||||||||||||||||||||||||||||||
| 2647 | { | ||||||||||||||||||||||||||||||||||||
| 2648 | case fword: | ||||||||||||||||||||||||||||||||||||
| 2649 | if (*p >= '0' && *p <= '9') | ||||||||||||||||||||||||||||||||||||
| 2650 | current_word = current_word * 16 + *p++ - '0'; | ||||||||||||||||||||||||||||||||||||
| 2651 | else if (*p >= 'a' && *p <= 'f') | ||||||||||||||||||||||||||||||||||||
| 2652 | current_word = current_word * 16 + *p++ - 'a' + 10; | ||||||||||||||||||||||||||||||||||||
| 2653 | else if (*p >= 'A' && *p <= 'F') | ||||||||||||||||||||||||||||||||||||
| 2654 | current_word = current_word * 16 + *p++ - 'A' + 10; | ||||||||||||||||||||||||||||||||||||
| 2655 | else | ||||||||||||||||||||||||||||||||||||
| 2656 | { | ||||||||||||||||||||||||||||||||||||
| 2657 | if (num_front_bytes == 16) | ||||||||||||||||||||||||||||||||||||
| 2658 | { | ||||||||||||||||||||||||||||||||||||
| 2659 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2660 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2661 | } | ||||||||||||||||||||||||||||||||||||
| 2662 | |||||||||||||||||||||||||||||||||||||
| 2663 | bytes[num_front_bytes++] = (current_word >> 8) & 0xFF; | ||||||||||||||||||||||||||||||||||||
| 2664 | bytes[num_front_bytes++] = current_word & 0xFF; | ||||||||||||||||||||||||||||||||||||
| 2665 | current_word = 0; | ||||||||||||||||||||||||||||||||||||
| 2666 | |||||||||||||||||||||||||||||||||||||
| 2667 | if (*p == ':') | ||||||||||||||||||||||||||||||||||||
| 2668 | state = fcolon, ++p; | ||||||||||||||||||||||||||||||||||||
| 2669 | else if (*p == '%') | ||||||||||||||||||||||||||||||||||||
| 2670 | state = scope, ++p; | ||||||||||||||||||||||||||||||||||||
| 2671 | else if (*p == 0) | ||||||||||||||||||||||||||||||||||||
| 2672 | state = done; | ||||||||||||||||||||||||||||||||||||
| 2673 | else | ||||||||||||||||||||||||||||||||||||
| 2674 | { | ||||||||||||||||||||||||||||||||||||
| 2675 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2676 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2677 | } | ||||||||||||||||||||||||||||||||||||
| 2678 | } | ||||||||||||||||||||||||||||||||||||
| 2679 | break; | ||||||||||||||||||||||||||||||||||||
| 2680 | |||||||||||||||||||||||||||||||||||||
| 2681 | case fcolon: | ||||||||||||||||||||||||||||||||||||
| 2682 | if (*p == ':') | ||||||||||||||||||||||||||||||||||||
| 2683 | state = bword, ++p; | ||||||||||||||||||||||||||||||||||||
| 2684 | else | ||||||||||||||||||||||||||||||||||||
| 2685 | state = fword; | ||||||||||||||||||||||||||||||||||||
| 2686 | break; | ||||||||||||||||||||||||||||||||||||
| 2687 | |||||||||||||||||||||||||||||||||||||
| 2688 | case bword: | ||||||||||||||||||||||||||||||||||||
| 2689 | if (*p >= '0' && *p <= '9') | ||||||||||||||||||||||||||||||||||||
| 2690 | current_word = current_word * 16 + *p++ - '0'; | ||||||||||||||||||||||||||||||||||||
| 2691 | else if (*p >= 'a' && *p <= 'f') | ||||||||||||||||||||||||||||||||||||
| 2692 | current_word = current_word * 16 + *p++ - 'a' + 10; | ||||||||||||||||||||||||||||||||||||
| 2693 | else if (*p >= 'A' && *p <= 'F') | ||||||||||||||||||||||||||||||||||||
| 2694 | current_word = current_word * 16 + *p++ - 'A' + 10; | ||||||||||||||||||||||||||||||||||||
| 2695 | else | ||||||||||||||||||||||||||||||||||||
| 2696 | { | ||||||||||||||||||||||||||||||||||||
| 2697 | if (num_front_bytes + num_back_bytes == 16) | ||||||||||||||||||||||||||||||||||||
| 2698 | { | ||||||||||||||||||||||||||||||||||||
| 2699 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2700 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2701 | } | ||||||||||||||||||||||||||||||||||||
| 2702 | |||||||||||||||||||||||||||||||||||||
| 2703 | back_bytes[num_back_bytes++] = (current_word >> 8) & 0xFF; | ||||||||||||||||||||||||||||||||||||
| 2704 | back_bytes[num_back_bytes++] = current_word & 0xFF; | ||||||||||||||||||||||||||||||||||||
| 2705 | current_word = 0; | ||||||||||||||||||||||||||||||||||||
| 2706 | |||||||||||||||||||||||||||||||||||||
| 2707 | if (*p == ':') | ||||||||||||||||||||||||||||||||||||
| 2708 | state = bword, ++p; | ||||||||||||||||||||||||||||||||||||
| 2709 | else if (*p == '%') | ||||||||||||||||||||||||||||||||||||
| 2710 | state = scope, ++p; | ||||||||||||||||||||||||||||||||||||
| 2711 | else if (*p == 0) | ||||||||||||||||||||||||||||||||||||
| 2712 | state = done; | ||||||||||||||||||||||||||||||||||||
| 2713 | else | ||||||||||||||||||||||||||||||||||||
| 2714 | { | ||||||||||||||||||||||||||||||||||||
| 2715 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2716 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2717 | } | ||||||||||||||||||||||||||||||||||||
| 2718 | } | ||||||||||||||||||||||||||||||||||||
| 2719 | break; | ||||||||||||||||||||||||||||||||||||
| 2720 | |||||||||||||||||||||||||||||||||||||
| 2721 | case scope: | ||||||||||||||||||||||||||||||||||||
| 2722 | if (*p >= '0' && *p <= '9') | ||||||||||||||||||||||||||||||||||||
| 2723 | current_word = current_word * 10 + *p++ - '0'; | ||||||||||||||||||||||||||||||||||||
| 2724 | else if (*p == 0) | ||||||||||||||||||||||||||||||||||||
| 2725 | *scope_id = current_word, state = done; | ||||||||||||||||||||||||||||||||||||
| 2726 | else | ||||||||||||||||||||||||||||||||||||
| 2727 | { | ||||||||||||||||||||||||||||||||||||
| 2728 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2729 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2730 | } | ||||||||||||||||||||||||||||||||||||
| 2731 | break; | ||||||||||||||||||||||||||||||||||||
| 2732 | |||||||||||||||||||||||||||||||||||||
| 2733 | default: | ||||||||||||||||||||||||||||||||||||
| 2734 | break; | ||||||||||||||||||||||||||||||||||||
| 2735 | } | ||||||||||||||||||||||||||||||||||||
| 2736 | } | ||||||||||||||||||||||||||||||||||||
| 2737 | |||||||||||||||||||||||||||||||||||||
| 2738 | for (int i = 0; i < num_back_bytes; ++i) | ||||||||||||||||||||||||||||||||||||
| 2739 | bytes[16 - num_back_bytes + i] = back_bytes[i]; | ||||||||||||||||||||||||||||||||||||
| 2740 | |||||||||||||||||||||||||||||||||||||
| 2741 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2742 | return 1; | ||||||||||||||||||||||||||||||||||||
| 2743 | } | ||||||||||||||||||||||||||||||||||||
| 2744 | else | ||||||||||||||||||||||||||||||||||||
| 2745 | { | ||||||||||||||||||||||||||||||||||||
| 2746 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||||
| 2747 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2748 | } | ||||||||||||||||||||||||||||||||||||
| 2749 | #elif defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2750 | using namespace std; // For memcpy and strcmp. | ||||||||||||||||||||||||||||||||||||
| 2751 | |||||||||||||||||||||||||||||||||||||
| 2752 | if (af != BOOST_ASIO_OS_DEF(AF_INET)2 && af != BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||||
| 2753 | { | ||||||||||||||||||||||||||||||||||||
| 2754 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||||
| 2755 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2756 | } | ||||||||||||||||||||||||||||||||||||
| 2757 | |||||||||||||||||||||||||||||||||||||
| 2758 | union | ||||||||||||||||||||||||||||||||||||
| 2759 | { | ||||||||||||||||||||||||||||||||||||
| 2760 | socket_addr_type base; | ||||||||||||||||||||||||||||||||||||
| 2761 | sockaddr_storage_type storage; | ||||||||||||||||||||||||||||||||||||
| 2762 | sockaddr_in4_type v4; | ||||||||||||||||||||||||||||||||||||
| 2763 | sockaddr_in6_type v6; | ||||||||||||||||||||||||||||||||||||
| 2764 | } address; | ||||||||||||||||||||||||||||||||||||
| 2765 | int address_length = sizeof(sockaddr_storage_type); | ||||||||||||||||||||||||||||||||||||
| 2766 | #if defined(BOOST_NO_ANSI_APIS) || (defined(_MSC_VER) && (_MSC_VER >= 1800)) | ||||||||||||||||||||||||||||||||||||
| 2767 | int num_wide_chars = static_cast<int>(strlen(src)) + 1; | ||||||||||||||||||||||||||||||||||||
| 2768 | LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR)); | ||||||||||||||||||||||||||||||||||||
| 2769 | ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars); | ||||||||||||||||||||||||||||||||||||
| 2770 | int result = ::WSAStringToAddressW(wide_buffer, | ||||||||||||||||||||||||||||||||||||
| 2771 | af, 0, &address.base, &address_length); | ||||||||||||||||||||||||||||||||||||
| 2772 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 2773 | #else | ||||||||||||||||||||||||||||||||||||
| 2774 | int result = ::WSAStringToAddressA(const_cast<char*>(src), | ||||||||||||||||||||||||||||||||||||
| 2775 | af, 0, &address.base, &address_length); | ||||||||||||||||||||||||||||||||||||
| 2776 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 2777 | #endif | ||||||||||||||||||||||||||||||||||||
| 2778 | |||||||||||||||||||||||||||||||||||||
| 2779 | if (af == BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||||
| 2780 | { | ||||||||||||||||||||||||||||||||||||
| 2781 | if (result != socket_error_retval) | ||||||||||||||||||||||||||||||||||||
| 2782 | { | ||||||||||||||||||||||||||||||||||||
| 2783 | memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type)); | ||||||||||||||||||||||||||||||||||||
| 2784 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2785 | } | ||||||||||||||||||||||||||||||||||||
| 2786 | else if (strcmp(src, "255.255.255.255") == 0) | ||||||||||||||||||||||||||||||||||||
| 2787 | { | ||||||||||||||||||||||||||||||||||||
| 2788 | static_cast<in4_addr_type*>(dest)->s_addr = INADDR_NONE((in_addr_t) 0xffffffff); | ||||||||||||||||||||||||||||||||||||
| 2789 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2790 | } | ||||||||||||||||||||||||||||||||||||
| 2791 | } | ||||||||||||||||||||||||||||||||||||
| 2792 | else // AF_INET6 | ||||||||||||||||||||||||||||||||||||
| 2793 | { | ||||||||||||||||||||||||||||||||||||
| 2794 | if (result != socket_error_retval) | ||||||||||||||||||||||||||||||||||||
| 2795 | { | ||||||||||||||||||||||||||||||||||||
| 2796 | memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type)); | ||||||||||||||||||||||||||||||||||||
| 2797 | if (scope_id) | ||||||||||||||||||||||||||||||||||||
| 2798 | *scope_id = address.v6.sin6_scope_id; | ||||||||||||||||||||||||||||||||||||
| 2799 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2800 | } | ||||||||||||||||||||||||||||||||||||
| 2801 | } | ||||||||||||||||||||||||||||||||||||
| 2802 | |||||||||||||||||||||||||||||||||||||
| 2803 | // Windows may not set an error code on failure. | ||||||||||||||||||||||||||||||||||||
| 2804 | if (result == socket_error_retval && !ec) | ||||||||||||||||||||||||||||||||||||
| 2805 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2806 | |||||||||||||||||||||||||||||||||||||
| 2807 | if (result != socket_error_retval) | ||||||||||||||||||||||||||||||||||||
| 2808 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 2809 | |||||||||||||||||||||||||||||||||||||
| 2810 | return result == socket_error_retval ? -1 : 1; | ||||||||||||||||||||||||||||||||||||
| 2811 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2812 | using namespace std; // For strchr, memcpy and atoi. | ||||||||||||||||||||||||||||||||||||
| 2813 | |||||||||||||||||||||||||||||||||||||
| 2814 | // On some platforms, inet_pton fails if an address string contains a scope | ||||||||||||||||||||||||||||||||||||
| 2815 | // id. Detect and remove the scope id before passing the string to inet_pton. | ||||||||||||||||||||||||||||||||||||
| 2816 | const bool is_v6 = (af == BOOST_ASIO_OS_DEF(AF_INET6)10); | ||||||||||||||||||||||||||||||||||||
| 2817 | const char* if_name = is_v6 ? strchr(src, '%') : 0; | ||||||||||||||||||||||||||||||||||||
| 2818 | char src_buf[max_addr_v6_str_len + 1]; | ||||||||||||||||||||||||||||||||||||
| 2819 | const char* src_ptr = src; | ||||||||||||||||||||||||||||||||||||
| 2820 | if (if_name != 0) | ||||||||||||||||||||||||||||||||||||
| 2821 | { | ||||||||||||||||||||||||||||||||||||
| 2822 | if (if_name - src > max_addr_v6_str_len) | ||||||||||||||||||||||||||||||||||||
| 2823 | { | ||||||||||||||||||||||||||||||||||||
| 2824 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2825 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2826 | } | ||||||||||||||||||||||||||||||||||||
| 2827 | memcpy(src_buf, src, if_name - src); | ||||||||||||||||||||||||||||||||||||
| 2828 | src_buf[if_name - src] = 0; | ||||||||||||||||||||||||||||||||||||
| 2829 | src_ptr = src_buf; | ||||||||||||||||||||||||||||||||||||
| 2830 | } | ||||||||||||||||||||||||||||||||||||
| 2831 | |||||||||||||||||||||||||||||||||||||
| 2832 | int result = ::inet_pton(af, src_ptr, dest); | ||||||||||||||||||||||||||||||||||||
| 2833 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 2834 | if (result <= 0 && !ec) | ||||||||||||||||||||||||||||||||||||
| 2835 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2836 | if (result > 0 && is_v6 && scope_id) | ||||||||||||||||||||||||||||||||||||
| 2837 | { | ||||||||||||||||||||||||||||||||||||
| 2838 | using namespace std; // For strchr and atoi. | ||||||||||||||||||||||||||||||||||||
| 2839 | *scope_id = 0; | ||||||||||||||||||||||||||||||||||||
| 2840 | if (if_name != 0) | ||||||||||||||||||||||||||||||||||||
| 2841 | { | ||||||||||||||||||||||||||||||||||||
| 2842 | in6_addr_type* ipv6_address = static_cast<in6_addr_type*>(dest); | ||||||||||||||||||||||||||||||||||||
| 2843 | bool is_link_local = ((ipv6_address->s6_addr__in6_u.__u6_addr8[0] == 0xfe) | ||||||||||||||||||||||||||||||||||||
| 2844 | && ((ipv6_address->s6_addr__in6_u.__u6_addr8[1] & 0xc0) == 0x80)); | ||||||||||||||||||||||||||||||||||||
| 2845 | bool is_multicast_link_local = ((ipv6_address->s6_addr__in6_u.__u6_addr8[0] == 0xff) | ||||||||||||||||||||||||||||||||||||
| 2846 | && ((ipv6_address->s6_addr__in6_u.__u6_addr8[1] & 0x0f) == 0x02)); | ||||||||||||||||||||||||||||||||||||
| 2847 | if (is_link_local || is_multicast_link_local) | ||||||||||||||||||||||||||||||||||||
| 2848 | *scope_id = if_nametoindex(if_name + 1); | ||||||||||||||||||||||||||||||||||||
| 2849 | if (*scope_id == 0) | ||||||||||||||||||||||||||||||||||||
| 2850 | *scope_id = atoi(if_name + 1); | ||||||||||||||||||||||||||||||||||||
| 2851 | } | ||||||||||||||||||||||||||||||||||||
| 2852 | } | ||||||||||||||||||||||||||||||||||||
| 2853 | return result; | ||||||||||||||||||||||||||||||||||||
| 2854 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2855 | } | ||||||||||||||||||||||||||||||||||||
| 2856 | |||||||||||||||||||||||||||||||||||||
| 2857 | int gethostname(char* name, int namelen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2858 | { | ||||||||||||||||||||||||||||||||||||
| 2859 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 2860 | try | ||||||||||||||||||||||||||||||||||||
| 2861 | { | ||||||||||||||||||||||||||||||||||||
| 2862 | using namespace Windows::Foundation::Collections; | ||||||||||||||||||||||||||||||||||||
| 2863 | using namespace Windows::Networking; | ||||||||||||||||||||||||||||||||||||
| 2864 | using namespace Windows::Networking::Connectivity; | ||||||||||||||||||||||||||||||||||||
| 2865 | IVectorView<HostName^>^ hostnames = NetworkInformation::GetHostNames(); | ||||||||||||||||||||||||||||||||||||
| 2866 | for (unsigned i = 0; i < hostnames->Size; ++i) | ||||||||||||||||||||||||||||||||||||
| 2867 | { | ||||||||||||||||||||||||||||||||||||
| 2868 | HostName^ hostname = hostnames->GetAt(i); | ||||||||||||||||||||||||||||||||||||
| 2869 | if (hostname->Type == HostNameType::DomainName) | ||||||||||||||||||||||||||||||||||||
| 2870 | { | ||||||||||||||||||||||||||||||||||||
| 2871 | std::wstring_convert<std::codecvt_utf8<wchar_t>> converter; | ||||||||||||||||||||||||||||||||||||
| 2872 | std::string raw_name = converter.to_bytes(hostname->RawName->Data()); | ||||||||||||||||||||||||||||||||||||
| 2873 | if (namelen > 0 && raw_name.size() < static_cast<std::size_t>(namelen)) | ||||||||||||||||||||||||||||||||||||
| 2874 | { | ||||||||||||||||||||||||||||||||||||
| 2875 | strcpy_s(name, namelen, raw_name.c_str()); | ||||||||||||||||||||||||||||||||||||
| 2876 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2877 | } | ||||||||||||||||||||||||||||||||||||
| 2878 | } | ||||||||||||||||||||||||||||||||||||
| 2879 | } | ||||||||||||||||||||||||||||||||||||
| 2880 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2881 | } | ||||||||||||||||||||||||||||||||||||
| 2882 | catch (Platform::Exception^ e) | ||||||||||||||||||||||||||||||||||||
| 2883 | { | ||||||||||||||||||||||||||||||||||||
| 2884 | ec = boost::system::error_code(e->HResult, | ||||||||||||||||||||||||||||||||||||
| 2885 | boost::system::system_category()); | ||||||||||||||||||||||||||||||||||||
| 2886 | return -1; | ||||||||||||||||||||||||||||||||||||
| 2887 | } | ||||||||||||||||||||||||||||||||||||
| 2888 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 2889 | int result = ::gethostname(name, namelen); | ||||||||||||||||||||||||||||||||||||
| 2890 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||||
| 2891 | return result; | ||||||||||||||||||||||||||||||||||||
| 2892 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 2893 | } | ||||||||||||||||||||||||||||||||||||
| 2894 | |||||||||||||||||||||||||||||||||||||
| 2895 | #if !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 2896 | |||||||||||||||||||||||||||||||||||||
| 2897 | #if !defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||||
| 2898 | |||||||||||||||||||||||||||||||||||||
| 2899 | // The following functions are only needed for emulation of getaddrinfo and | ||||||||||||||||||||||||||||||||||||
| 2900 | // getnameinfo. | ||||||||||||||||||||||||||||||||||||
| 2901 | |||||||||||||||||||||||||||||||||||||
| 2902 | inline boost::system::error_code translate_netdb_error(int error) | ||||||||||||||||||||||||||||||||||||
| 2903 | { | ||||||||||||||||||||||||||||||||||||
| 2904 | switch (error) | ||||||||||||||||||||||||||||||||||||
| 2905 | { | ||||||||||||||||||||||||||||||||||||
| 2906 | case 0: | ||||||||||||||||||||||||||||||||||||
| 2907 | return boost::system::error_code(); | ||||||||||||||||||||||||||||||||||||
| 2908 | case HOST_NOT_FOUND1: | ||||||||||||||||||||||||||||||||||||
| 2909 | return boost::asio::error::host_not_found; | ||||||||||||||||||||||||||||||||||||
| 2910 | case TRY_AGAIN2: | ||||||||||||||||||||||||||||||||||||
| 2911 | return boost::asio::error::host_not_found_try_again; | ||||||||||||||||||||||||||||||||||||
| 2912 | case NO_RECOVERY3: | ||||||||||||||||||||||||||||||||||||
| 2913 | return boost::asio::error::no_recovery; | ||||||||||||||||||||||||||||||||||||
| 2914 | case NO_DATA4: | ||||||||||||||||||||||||||||||||||||
| 2915 | return boost::asio::error::no_data; | ||||||||||||||||||||||||||||||||||||
| 2916 | default: | ||||||||||||||||||||||||||||||||||||
| 2917 | BOOST_ASIO_ASSERT(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); | ||||||||||||||||||||||||||||||||||||
| 2918 | return boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 2919 | } | ||||||||||||||||||||||||||||||||||||
| 2920 | } | ||||||||||||||||||||||||||||||||||||
| 2921 | |||||||||||||||||||||||||||||||||||||
| 2922 | inline hostent* gethostbyaddr(const char* addr, int length, int af, | ||||||||||||||||||||||||||||||||||||
| 2923 | hostent* result, char* buffer, int buflength, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2924 | { | ||||||||||||||||||||||||||||||||||||
| 2925 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2926 | (void)(buffer); | ||||||||||||||||||||||||||||||||||||
| 2927 | (void)(buflength); | ||||||||||||||||||||||||||||||||||||
| 2928 | hostent* retval = ::gethostbyaddr(addr, length, af); | ||||||||||||||||||||||||||||||||||||
| 2929 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||||
| 2930 | if (!retval) | ||||||||||||||||||||||||||||||||||||
| 2931 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2932 | *result = *retval; | ||||||||||||||||||||||||||||||||||||
| 2933 | return retval; | ||||||||||||||||||||||||||||||||||||
| 2934 | #elif defined(__sun) || defined(__QNX__) | ||||||||||||||||||||||||||||||||||||
| 2935 | int error = 0; | ||||||||||||||||||||||||||||||||||||
| 2936 | hostent* retval = ::gethostbyaddr_r(addr, length, | ||||||||||||||||||||||||||||||||||||
| 2937 | af, result, buffer, buflength, &error); | ||||||||||||||||||||||||||||||||||||
| 2938 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||||
| 2939 | if (error) | ||||||||||||||||||||||||||||||||||||
| 2940 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||||
| 2941 | return retval; | ||||||||||||||||||||||||||||||||||||
| 2942 | #elif defined(__MACH__) && defined(__APPLE__) | ||||||||||||||||||||||||||||||||||||
| 2943 | (void)(buffer); | ||||||||||||||||||||||||||||||||||||
| 2944 | (void)(buflength); | ||||||||||||||||||||||||||||||||||||
| 2945 | int error = 0; | ||||||||||||||||||||||||||||||||||||
| 2946 | hostent* retval = ::getipnodebyaddr(addr, length, af, &error); | ||||||||||||||||||||||||||||||||||||
| 2947 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||||
| 2948 | if (error) | ||||||||||||||||||||||||||||||||||||
| 2949 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||||
| 2950 | if (!retval) | ||||||||||||||||||||||||||||||||||||
| 2951 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2952 | *result = *retval; | ||||||||||||||||||||||||||||||||||||
| 2953 | return retval; | ||||||||||||||||||||||||||||||||||||
| 2954 | #else | ||||||||||||||||||||||||||||||||||||
| 2955 | hostent* retval = 0; | ||||||||||||||||||||||||||||||||||||
| 2956 | int error = 0; | ||||||||||||||||||||||||||||||||||||
| 2957 | clear_last_error(); | ||||||||||||||||||||||||||||||||||||
| 2958 | ::gethostbyaddr_r(addr, length, af, result, | ||||||||||||||||||||||||||||||||||||
| 2959 | buffer, buflength, &retval, &error); | ||||||||||||||||||||||||||||||||||||
| 2960 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 2961 | if (error) | ||||||||||||||||||||||||||||||||||||
| 2962 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||||
| 2963 | return retval; | ||||||||||||||||||||||||||||||||||||
| 2964 | #endif | ||||||||||||||||||||||||||||||||||||
| 2965 | } | ||||||||||||||||||||||||||||||||||||
| 2966 | |||||||||||||||||||||||||||||||||||||
| 2967 | inline hostent* gethostbyname(const char* name, int af, struct hostent* result, | ||||||||||||||||||||||||||||||||||||
| 2968 | char* buffer, int buflength, int ai_flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 2969 | { | ||||||||||||||||||||||||||||||||||||
| 2970 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 2971 | (void)(buffer); | ||||||||||||||||||||||||||||||||||||
| 2972 | (void)(buflength); | ||||||||||||||||||||||||||||||||||||
| 2973 | (void)(ai_flags); | ||||||||||||||||||||||||||||||||||||
| 2974 | if (af != BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||||
| 2975 | { | ||||||||||||||||||||||||||||||||||||
| 2976 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||||
| 2977 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2978 | } | ||||||||||||||||||||||||||||||||||||
| 2979 | hostent* retval = ::gethostbyname(name); | ||||||||||||||||||||||||||||||||||||
| 2980 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||||
| 2981 | if (!retval) | ||||||||||||||||||||||||||||||||||||
| 2982 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2983 | *result = *retval; | ||||||||||||||||||||||||||||||||||||
| 2984 | return result; | ||||||||||||||||||||||||||||||||||||
| 2985 | #elif defined(__sun) || defined(__QNX__) | ||||||||||||||||||||||||||||||||||||
| 2986 | (void)(ai_flags); | ||||||||||||||||||||||||||||||||||||
| 2987 | if (af != BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||||
| 2988 | { | ||||||||||||||||||||||||||||||||||||
| 2989 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||||
| 2990 | return 0; | ||||||||||||||||||||||||||||||||||||
| 2991 | } | ||||||||||||||||||||||||||||||||||||
| 2992 | int error = 0; | ||||||||||||||||||||||||||||||||||||
| 2993 | hostent* retval = ::gethostbyname_r(name, result, buffer, buflength, &error); | ||||||||||||||||||||||||||||||||||||
| 2994 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||||
| 2995 | if (error) | ||||||||||||||||||||||||||||||||||||
| 2996 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||||
| 2997 | return retval; | ||||||||||||||||||||||||||||||||||||
| 2998 | #elif defined(__MACH__) && defined(__APPLE__) | ||||||||||||||||||||||||||||||||||||
| 2999 | (void)(buffer); | ||||||||||||||||||||||||||||||||||||
| 3000 | (void)(buflength); | ||||||||||||||||||||||||||||||||||||
| 3001 | int error = 0; | ||||||||||||||||||||||||||||||||||||
| 3002 | hostent* retval = ::getipnodebyname(name, af, ai_flags, &error); | ||||||||||||||||||||||||||||||||||||
| 3003 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||||
| 3004 | if (error) | ||||||||||||||||||||||||||||||||||||
| 3005 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||||
| 3006 | if (!retval) | ||||||||||||||||||||||||||||||||||||
| 3007 | return 0; | ||||||||||||||||||||||||||||||||||||
| 3008 | *result = *retval; | ||||||||||||||||||||||||||||||||||||
| 3009 | return retval; | ||||||||||||||||||||||||||||||||||||
| 3010 | #else | ||||||||||||||||||||||||||||||||||||
| 3011 | (void)(ai_flags); | ||||||||||||||||||||||||||||||||||||
| 3012 | if (af != BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||||
| 3013 | { | ||||||||||||||||||||||||||||||||||||
| 3014 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||||
| 3015 | return 0; | ||||||||||||||||||||||||||||||||||||
| 3016 | } | ||||||||||||||||||||||||||||||||||||
| 3017 | hostent* retval = 0; | ||||||||||||||||||||||||||||||||||||
| 3018 | int error = 0; | ||||||||||||||||||||||||||||||||||||
| 3019 | clear_last_error(); | ||||||||||||||||||||||||||||||||||||
| 3020 | ::gethostbyname_r(name, result, buffer, buflength, &retval, &error); | ||||||||||||||||||||||||||||||||||||
| 3021 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||||
| 3022 | if (error) | ||||||||||||||||||||||||||||||||||||
| 3023 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||||
| 3024 | return retval; | ||||||||||||||||||||||||||||||||||||
| 3025 | #endif | ||||||||||||||||||||||||||||||||||||
| 3026 | } | ||||||||||||||||||||||||||||||||||||
| 3027 | |||||||||||||||||||||||||||||||||||||
| 3028 | inline void freehostent(hostent* h) | ||||||||||||||||||||||||||||||||||||
| 3029 | { | ||||||||||||||||||||||||||||||||||||
| 3030 | #if defined(__MACH__) && defined(__APPLE__) | ||||||||||||||||||||||||||||||||||||
| 3031 | if (h) | ||||||||||||||||||||||||||||||||||||
| 3032 | ::freehostent(h); | ||||||||||||||||||||||||||||||||||||
| 3033 | #else | ||||||||||||||||||||||||||||||||||||
| 3034 | (void)(h); | ||||||||||||||||||||||||||||||||||||
| 3035 | #endif | ||||||||||||||||||||||||||||||||||||
| 3036 | } | ||||||||||||||||||||||||||||||||||||
| 3037 | |||||||||||||||||||||||||||||||||||||
| 3038 | // Emulation of getaddrinfo based on implementation in: | ||||||||||||||||||||||||||||||||||||
| 3039 | // Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998. | ||||||||||||||||||||||||||||||||||||
| 3040 | |||||||||||||||||||||||||||||||||||||
| 3041 | struct gai_search | ||||||||||||||||||||||||||||||||||||
| 3042 | { | ||||||||||||||||||||||||||||||||||||
| 3043 | const char* host; | ||||||||||||||||||||||||||||||||||||
| 3044 | int family; | ||||||||||||||||||||||||||||||||||||
| 3045 | }; | ||||||||||||||||||||||||||||||||||||
| 3046 | |||||||||||||||||||||||||||||||||||||
| 3047 | inline int gai_nsearch(const char* host, | ||||||||||||||||||||||||||||||||||||
| 3048 | const addrinfo_type* hints, gai_search (&search)[2]) | ||||||||||||||||||||||||||||||||||||
| 3049 | { | ||||||||||||||||||||||||||||||||||||
| 3050 | int search_count = 0; | ||||||||||||||||||||||||||||||||||||
| 3051 | if (host == 0 || host[0] == '\0') | ||||||||||||||||||||||||||||||||||||
| 3052 | { | ||||||||||||||||||||||||||||||||||||
| 3053 | if (hints->ai_flags & AI_PASSIVE0x0001) | ||||||||||||||||||||||||||||||||||||
| 3054 | { | ||||||||||||||||||||||||||||||||||||
| 3055 | // No host and AI_PASSIVE implies wildcard bind. | ||||||||||||||||||||||||||||||||||||
| 3056 | switch (hints->ai_family) | ||||||||||||||||||||||||||||||||||||
| 3057 | { | ||||||||||||||||||||||||||||||||||||
| 3058 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||||
| 3059 | search[search_count].host = "0.0.0.0"; | ||||||||||||||||||||||||||||||||||||
| 3060 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||||
| 3061 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3062 | break; | ||||||||||||||||||||||||||||||||||||
| 3063 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||||
| 3064 | search[search_count].host = "0::0"; | ||||||||||||||||||||||||||||||||||||
| 3065 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||||
| 3066 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3067 | break; | ||||||||||||||||||||||||||||||||||||
| 3068 | case BOOST_ASIO_OS_DEF(AF_UNSPEC)0: | ||||||||||||||||||||||||||||||||||||
| 3069 | search[search_count].host = "0::0"; | ||||||||||||||||||||||||||||||||||||
| 3070 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||||
| 3071 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3072 | search[search_count].host = "0.0.0.0"; | ||||||||||||||||||||||||||||||||||||
| 3073 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||||
| 3074 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3075 | break; | ||||||||||||||||||||||||||||||||||||
| 3076 | default: | ||||||||||||||||||||||||||||||||||||
| 3077 | break; | ||||||||||||||||||||||||||||||||||||
| 3078 | } | ||||||||||||||||||||||||||||||||||||
| 3079 | } | ||||||||||||||||||||||||||||||||||||
| 3080 | else | ||||||||||||||||||||||||||||||||||||
| 3081 | { | ||||||||||||||||||||||||||||||||||||
| 3082 | // No host and not AI_PASSIVE means connect to local host. | ||||||||||||||||||||||||||||||||||||
| 3083 | switch (hints->ai_family) | ||||||||||||||||||||||||||||||||||||
| 3084 | { | ||||||||||||||||||||||||||||||||||||
| 3085 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||||
| 3086 | search[search_count].host = "localhost"; | ||||||||||||||||||||||||||||||||||||
| 3087 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||||
| 3088 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3089 | break; | ||||||||||||||||||||||||||||||||||||
| 3090 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||||
| 3091 | search[search_count].host = "localhost"; | ||||||||||||||||||||||||||||||||||||
| 3092 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||||
| 3093 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3094 | break; | ||||||||||||||||||||||||||||||||||||
| 3095 | case BOOST_ASIO_OS_DEF(AF_UNSPEC)0: | ||||||||||||||||||||||||||||||||||||
| 3096 | search[search_count].host = "localhost"; | ||||||||||||||||||||||||||||||||||||
| 3097 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||||
| 3098 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3099 | search[search_count].host = "localhost"; | ||||||||||||||||||||||||||||||||||||
| 3100 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||||
| 3101 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3102 | break; | ||||||||||||||||||||||||||||||||||||
| 3103 | default: | ||||||||||||||||||||||||||||||||||||
| 3104 | break; | ||||||||||||||||||||||||||||||||||||
| 3105 | } | ||||||||||||||||||||||||||||||||||||
| 3106 | } | ||||||||||||||||||||||||||||||||||||
| 3107 | } | ||||||||||||||||||||||||||||||||||||
| 3108 | else | ||||||||||||||||||||||||||||||||||||
| 3109 | { | ||||||||||||||||||||||||||||||||||||
| 3110 | // Host is specified. | ||||||||||||||||||||||||||||||||||||
| 3111 | switch (hints->ai_family) | ||||||||||||||||||||||||||||||||||||
| 3112 | { | ||||||||||||||||||||||||||||||||||||
| 3113 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||||
| 3114 | search[search_count].host = host; | ||||||||||||||||||||||||||||||||||||
| 3115 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||||
| 3116 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3117 | break; | ||||||||||||||||||||||||||||||||||||
| 3118 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||||
| 3119 | search[search_count].host = host; | ||||||||||||||||||||||||||||||||||||
| 3120 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||||
| 3121 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3122 | break; | ||||||||||||||||||||||||||||||||||||
| 3123 | case BOOST_ASIO_OS_DEF(AF_UNSPEC)0: | ||||||||||||||||||||||||||||||||||||
| 3124 | search[search_count].host = host; | ||||||||||||||||||||||||||||||||||||
| 3125 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||||
| 3126 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3127 | search[search_count].host = host; | ||||||||||||||||||||||||||||||||||||
| 3128 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||||
| 3129 | ++search_count; | ||||||||||||||||||||||||||||||||||||
| 3130 | break; | ||||||||||||||||||||||||||||||||||||
| 3131 | default: | ||||||||||||||||||||||||||||||||||||
| 3132 | break; | ||||||||||||||||||||||||||||||||||||
| 3133 | } | ||||||||||||||||||||||||||||||||||||
| 3134 | } | ||||||||||||||||||||||||||||||||||||
| 3135 | return search_count; | ||||||||||||||||||||||||||||||||||||
| 3136 | } | ||||||||||||||||||||||||||||||||||||
| 3137 | |||||||||||||||||||||||||||||||||||||
| 3138 | template <typename T> | ||||||||||||||||||||||||||||||||||||
| 3139 | inline T* gai_alloc(std::size_t size = sizeof(T)) | ||||||||||||||||||||||||||||||||||||
| 3140 | { | ||||||||||||||||||||||||||||||||||||
| 3141 | using namespace std; | ||||||||||||||||||||||||||||||||||||
| 3142 | T* p = static_cast<T*>(::operator new(size, std::nothrow)); | ||||||||||||||||||||||||||||||||||||
| 3143 | if (p) | ||||||||||||||||||||||||||||||||||||
| 3144 | memset(p, 0, size); | ||||||||||||||||||||||||||||||||||||
| 3145 | return p; | ||||||||||||||||||||||||||||||||||||
| 3146 | } | ||||||||||||||||||||||||||||||||||||
| 3147 | |||||||||||||||||||||||||||||||||||||
| 3148 | inline void gai_free(void* p) | ||||||||||||||||||||||||||||||||||||
| 3149 | { | ||||||||||||||||||||||||||||||||||||
| 3150 | ::operator delete(p); | ||||||||||||||||||||||||||||||||||||
| 3151 | } | ||||||||||||||||||||||||||||||||||||
| 3152 | |||||||||||||||||||||||||||||||||||||
| 3153 | inline void gai_strcpy(char* target, const char* source, std::size_t max_size) | ||||||||||||||||||||||||||||||||||||
| 3154 | { | ||||||||||||||||||||||||||||||||||||
| 3155 | using namespace std; | ||||||||||||||||||||||||||||||||||||
| 3156 | #if defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 3157 | strcpy_s(target, max_size, source); | ||||||||||||||||||||||||||||||||||||
| 3158 | #else // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 3159 | *target = 0; | ||||||||||||||||||||||||||||||||||||
| 3160 | if (max_size > 0) | ||||||||||||||||||||||||||||||||||||
| 3161 | strncat(target, source, max_size - 1); | ||||||||||||||||||||||||||||||||||||
| 3162 | #endif // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 3163 | } | ||||||||||||||||||||||||||||||||||||
| 3164 | |||||||||||||||||||||||||||||||||||||
| 3165 | enum { gai_clone_flag = 1 << 30 }; | ||||||||||||||||||||||||||||||||||||
| 3166 | |||||||||||||||||||||||||||||||||||||
| 3167 | inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints, | ||||||||||||||||||||||||||||||||||||
| 3168 | const void* addr, int family) | ||||||||||||||||||||||||||||||||||||
| 3169 | { | ||||||||||||||||||||||||||||||||||||
| 3170 | using namespace std; | ||||||||||||||||||||||||||||||||||||
| 3171 | |||||||||||||||||||||||||||||||||||||
| 3172 | addrinfo_type* ai = gai_alloc<addrinfo_type>(); | ||||||||||||||||||||||||||||||||||||
| 3173 | if (ai == 0) | ||||||||||||||||||||||||||||||||||||
| 3174 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||||
| 3175 | |||||||||||||||||||||||||||||||||||||
| 3176 | ai->ai_next = 0; | ||||||||||||||||||||||||||||||||||||
| 3177 | **next = ai; | ||||||||||||||||||||||||||||||||||||
| 3178 | *next = &ai->ai_next; | ||||||||||||||||||||||||||||||||||||
| 3179 | |||||||||||||||||||||||||||||||||||||
| 3180 | ai->ai_canonname = 0; | ||||||||||||||||||||||||||||||||||||
| 3181 | ai->ai_socktype = hints->ai_socktype; | ||||||||||||||||||||||||||||||||||||
| 3182 | if (ai->ai_socktype == 0) | ||||||||||||||||||||||||||||||||||||
| 3183 | ai->ai_flags |= gai_clone_flag; | ||||||||||||||||||||||||||||||||||||
| 3184 | ai->ai_protocol = hints->ai_protocol; | ||||||||||||||||||||||||||||||||||||
| 3185 | ai->ai_family = family; | ||||||||||||||||||||||||||||||||||||
| 3186 | |||||||||||||||||||||||||||||||||||||
| 3187 | switch (ai->ai_family) | ||||||||||||||||||||||||||||||||||||
| 3188 | { | ||||||||||||||||||||||||||||||||||||
| 3189 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||||
| 3190 | { | ||||||||||||||||||||||||||||||||||||
| 3191 | sockaddr_in4_type* sinptr = gai_alloc<sockaddr_in4_type>(); | ||||||||||||||||||||||||||||||||||||
| 3192 | if (sinptr == 0) | ||||||||||||||||||||||||||||||||||||
| 3193 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||||
| 3194 | sinptr->sin_family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||||
| 3195 | memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type)); | ||||||||||||||||||||||||||||||||||||
| 3196 | ai->ai_addr = reinterpret_cast<sockaddr*>(sinptr); | ||||||||||||||||||||||||||||||||||||
| 3197 | ai->ai_addrlen = sizeof(sockaddr_in4_type); | ||||||||||||||||||||||||||||||||||||
| 3198 | break; | ||||||||||||||||||||||||||||||||||||
| 3199 | } | ||||||||||||||||||||||||||||||||||||
| 3200 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||||
| 3201 | { | ||||||||||||||||||||||||||||||||||||
| 3202 | sockaddr_in6_type* sin6ptr = gai_alloc<sockaddr_in6_type>(); | ||||||||||||||||||||||||||||||||||||
| 3203 | if (sin6ptr == 0) | ||||||||||||||||||||||||||||||||||||
| 3204 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||||
| 3205 | sin6ptr->sin6_family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||||
| 3206 | memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type)); | ||||||||||||||||||||||||||||||||||||
| 3207 | ai->ai_addr = reinterpret_cast<sockaddr*>(sin6ptr); | ||||||||||||||||||||||||||||||||||||
| 3208 | ai->ai_addrlen = sizeof(sockaddr_in6_type); | ||||||||||||||||||||||||||||||||||||
| 3209 | break; | ||||||||||||||||||||||||||||||||||||
| 3210 | } | ||||||||||||||||||||||||||||||||||||
| 3211 | default: | ||||||||||||||||||||||||||||||||||||
| 3212 | break; | ||||||||||||||||||||||||||||||||||||
| 3213 | } | ||||||||||||||||||||||||||||||||||||
| 3214 | |||||||||||||||||||||||||||||||||||||
| 3215 | return 0; | ||||||||||||||||||||||||||||||||||||
| 3216 | } | ||||||||||||||||||||||||||||||||||||
| 3217 | |||||||||||||||||||||||||||||||||||||
| 3218 | inline addrinfo_type* gai_clone(addrinfo_type* ai) | ||||||||||||||||||||||||||||||||||||
| 3219 | { | ||||||||||||||||||||||||||||||||||||
| 3220 | using namespace std; | ||||||||||||||||||||||||||||||||||||
| 3221 | |||||||||||||||||||||||||||||||||||||
| 3222 | addrinfo_type* new_ai = gai_alloc<addrinfo_type>(); | ||||||||||||||||||||||||||||||||||||
| 3223 | if (new_ai == 0) | ||||||||||||||||||||||||||||||||||||
| 3224 | return new_ai; | ||||||||||||||||||||||||||||||||||||
| 3225 | |||||||||||||||||||||||||||||||||||||
| 3226 | new_ai->ai_next = ai->ai_next; | ||||||||||||||||||||||||||||||||||||
| 3227 | ai->ai_next = new_ai; | ||||||||||||||||||||||||||||||||||||
| 3228 | |||||||||||||||||||||||||||||||||||||
| 3229 | new_ai->ai_flags = 0; | ||||||||||||||||||||||||||||||||||||
| 3230 | new_ai->ai_family = ai->ai_family; | ||||||||||||||||||||||||||||||||||||
| 3231 | new_ai->ai_socktype = ai->ai_socktype; | ||||||||||||||||||||||||||||||||||||
| 3232 | new_ai->ai_protocol = ai->ai_protocol; | ||||||||||||||||||||||||||||||||||||
| 3233 | new_ai->ai_canonname = 0; | ||||||||||||||||||||||||||||||||||||
| 3234 | new_ai->ai_addrlen = ai->ai_addrlen; | ||||||||||||||||||||||||||||||||||||
| 3235 | new_ai->ai_addr = gai_alloc<sockaddr>(ai->ai_addrlen); | ||||||||||||||||||||||||||||||||||||
| 3236 | memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen); | ||||||||||||||||||||||||||||||||||||
| 3237 | |||||||||||||||||||||||||||||||||||||
| 3238 | return new_ai; | ||||||||||||||||||||||||||||||||||||
| 3239 | } | ||||||||||||||||||||||||||||||||||||
| 3240 | |||||||||||||||||||||||||||||||||||||
| 3241 | inline int gai_port(addrinfo_type* aihead, int port, int socktype) | ||||||||||||||||||||||||||||||||||||
| 3242 | { | ||||||||||||||||||||||||||||||||||||
| 3243 | int num_found = 0; | ||||||||||||||||||||||||||||||||||||
| 3244 | |||||||||||||||||||||||||||||||||||||
| 3245 | for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next) | ||||||||||||||||||||||||||||||||||||
| 3246 | { | ||||||||||||||||||||||||||||||||||||
| 3247 | if (ai->ai_flags & gai_clone_flag) | ||||||||||||||||||||||||||||||||||||
| 3248 | { | ||||||||||||||||||||||||||||||||||||
| 3249 | if (ai->ai_socktype != 0) | ||||||||||||||||||||||||||||||||||||
| 3250 | { | ||||||||||||||||||||||||||||||||||||
| 3251 | ai = gai_clone(ai); | ||||||||||||||||||||||||||||||||||||
| 3252 | if (ai == 0) | ||||||||||||||||||||||||||||||||||||
| 3253 | return -1; | ||||||||||||||||||||||||||||||||||||
| 3254 | // ai now points to newly cloned entry. | ||||||||||||||||||||||||||||||||||||
| 3255 | } | ||||||||||||||||||||||||||||||||||||
| 3256 | } | ||||||||||||||||||||||||||||||||||||
| 3257 | else if (ai->ai_socktype != socktype) | ||||||||||||||||||||||||||||||||||||
| 3258 | { | ||||||||||||||||||||||||||||||||||||
| 3259 | // Ignore if mismatch on socket type. | ||||||||||||||||||||||||||||||||||||
| 3260 | continue; | ||||||||||||||||||||||||||||||||||||
| 3261 | } | ||||||||||||||||||||||||||||||||||||
| 3262 | |||||||||||||||||||||||||||||||||||||
| 3263 | ai->ai_socktype = socktype; | ||||||||||||||||||||||||||||||||||||
| 3264 | |||||||||||||||||||||||||||||||||||||
| 3265 | switch (ai->ai_family) | ||||||||||||||||||||||||||||||||||||
| 3266 | { | ||||||||||||||||||||||||||||||||||||
| 3267 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||||
| 3268 | { | ||||||||||||||||||||||||||||||||||||
| 3269 | sockaddr_in4_type* sinptr = | ||||||||||||||||||||||||||||||||||||
| 3270 | reinterpret_cast<sockaddr_in4_type*>(ai->ai_addr); | ||||||||||||||||||||||||||||||||||||
| 3271 | sinptr->sin_port = port; | ||||||||||||||||||||||||||||||||||||
| 3272 | ++num_found; | ||||||||||||||||||||||||||||||||||||
| 3273 | break; | ||||||||||||||||||||||||||||||||||||
| 3274 | } | ||||||||||||||||||||||||||||||||||||
| 3275 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||||
| 3276 | { | ||||||||||||||||||||||||||||||||||||
| 3277 | sockaddr_in6_type* sin6ptr = | ||||||||||||||||||||||||||||||||||||
| 3278 | reinterpret_cast<sockaddr_in6_type*>(ai->ai_addr); | ||||||||||||||||||||||||||||||||||||
| 3279 | sin6ptr->sin6_port = port; | ||||||||||||||||||||||||||||||||||||
| 3280 | ++num_found; | ||||||||||||||||||||||||||||||||||||
| 3281 | break; | ||||||||||||||||||||||||||||||||||||
| 3282 | } | ||||||||||||||||||||||||||||||||||||
| 3283 | default: | ||||||||||||||||||||||||||||||||||||
| 3284 | break; | ||||||||||||||||||||||||||||||||||||
| 3285 | } | ||||||||||||||||||||||||||||||||||||
| 3286 | } | ||||||||||||||||||||||||||||||||||||
| 3287 | |||||||||||||||||||||||||||||||||||||
| 3288 | return num_found; | ||||||||||||||||||||||||||||||||||||
| 3289 | } | ||||||||||||||||||||||||||||||||||||
| 3290 | |||||||||||||||||||||||||||||||||||||
| 3291 | inline int gai_serv(addrinfo_type* aihead, | ||||||||||||||||||||||||||||||||||||
| 3292 | const addrinfo_type* hints, const char* serv) | ||||||||||||||||||||||||||||||||||||
| 3293 | { | ||||||||||||||||||||||||||||||||||||
| 3294 | using namespace std; | ||||||||||||||||||||||||||||||||||||
| 3295 | |||||||||||||||||||||||||||||||||||||
| 3296 | int num_found = 0; | ||||||||||||||||||||||||||||||||||||
| 3297 | |||||||||||||||||||||||||||||||||||||
| 3298 | if ( | ||||||||||||||||||||||||||||||||||||
| 3299 | #if defined(AI_NUMERICSERV0x0400) | ||||||||||||||||||||||||||||||||||||
| 3300 | (hints->ai_flags & AI_NUMERICSERV0x0400) || | ||||||||||||||||||||||||||||||||||||
| 3301 | #endif | ||||||||||||||||||||||||||||||||||||
| 3302 | isdigit(static_cast<unsigned char>(serv[0]))) | ||||||||||||||||||||||||||||||||||||
| 3303 | { | ||||||||||||||||||||||||||||||||||||
| 3304 | int port = htons(atoi(serv))__bswap_16 (atoi(serv)); | ||||||||||||||||||||||||||||||||||||
| 3305 | if (hints->ai_socktype) | ||||||||||||||||||||||||||||||||||||
| 3306 | { | ||||||||||||||||||||||||||||||||||||
| 3307 | // Caller specifies socket type. | ||||||||||||||||||||||||||||||||||||
| 3308 | int rc = gai_port(aihead, port, hints->ai_socktype); | ||||||||||||||||||||||||||||||||||||
| 3309 | if (rc < 0) | ||||||||||||||||||||||||||||||||||||
| 3310 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||||
| 3311 | num_found += rc; | ||||||||||||||||||||||||||||||||||||
| 3312 | } | ||||||||||||||||||||||||||||||||||||
| 3313 | else | ||||||||||||||||||||||||||||||||||||
| 3314 | { | ||||||||||||||||||||||||||||||||||||
| 3315 | // Caller does not specify socket type. | ||||||||||||||||||||||||||||||||||||
| 3316 | int rc = gai_port(aihead, port, SOCK_STREAMSOCK_STREAM); | ||||||||||||||||||||||||||||||||||||
| 3317 | if (rc < 0) | ||||||||||||||||||||||||||||||||||||
| 3318 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||||
| 3319 | num_found += rc; | ||||||||||||||||||||||||||||||||||||
| 3320 | rc = gai_port(aihead, port, SOCK_DGRAMSOCK_DGRAM); | ||||||||||||||||||||||||||||||||||||
| 3321 | if (rc < 0) | ||||||||||||||||||||||||||||||||||||
| 3322 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||||
| 3323 | num_found += rc; | ||||||||||||||||||||||||||||||||||||
| 3324 | } | ||||||||||||||||||||||||||||||||||||
| 3325 | } | ||||||||||||||||||||||||||||||||||||
| 3326 | else | ||||||||||||||||||||||||||||||||||||
| 3327 | { | ||||||||||||||||||||||||||||||||||||
| 3328 | // Try service name with TCP first, then UDP. | ||||||||||||||||||||||||||||||||||||
| 3329 | if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAMSOCK_STREAM) | ||||||||||||||||||||||||||||||||||||
| 3330 | { | ||||||||||||||||||||||||||||||||||||
| 3331 | servent* sptr = getservbyname(serv, "tcp"); | ||||||||||||||||||||||||||||||||||||
| 3332 | if (sptr != 0) | ||||||||||||||||||||||||||||||||||||
| 3333 | { | ||||||||||||||||||||||||||||||||||||
| 3334 | int rc = gai_port(aihead, sptr->s_port, SOCK_STREAMSOCK_STREAM); | ||||||||||||||||||||||||||||||||||||
| 3335 | if (rc < 0) | ||||||||||||||||||||||||||||||||||||
| 3336 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||||
| 3337 | num_found += rc; | ||||||||||||||||||||||||||||||||||||
| 3338 | } | ||||||||||||||||||||||||||||||||||||
| 3339 | } | ||||||||||||||||||||||||||||||||||||
| 3340 | if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAMSOCK_DGRAM) | ||||||||||||||||||||||||||||||||||||
| 3341 | { | ||||||||||||||||||||||||||||||||||||
| 3342 | servent* sptr = getservbyname(serv, "udp"); | ||||||||||||||||||||||||||||||||||||
| 3343 | if (sptr != 0) | ||||||||||||||||||||||||||||||||||||
| 3344 | { | ||||||||||||||||||||||||||||||||||||
| 3345 | int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAMSOCK_DGRAM); | ||||||||||||||||||||||||||||||||||||
| 3346 | if (rc < 0) | ||||||||||||||||||||||||||||||||||||
| 3347 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||||
| 3348 | num_found += rc; | ||||||||||||||||||||||||||||||||||||
| 3349 | } | ||||||||||||||||||||||||||||||||||||
| 3350 | } | ||||||||||||||||||||||||||||||||||||
| 3351 | } | ||||||||||||||||||||||||||||||||||||
| 3352 | |||||||||||||||||||||||||||||||||||||
| 3353 | if (num_found == 0) | ||||||||||||||||||||||||||||||||||||
| 3354 | { | ||||||||||||||||||||||||||||||||||||
| 3355 | if (hints->ai_socktype == 0) | ||||||||||||||||||||||||||||||||||||
| 3356 | { | ||||||||||||||||||||||||||||||||||||
| 3357 | // All calls to getservbyname() failed. | ||||||||||||||||||||||||||||||||||||
| 3358 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||||
| 3359 | } | ||||||||||||||||||||||||||||||||||||
| 3360 | else | ||||||||||||||||||||||||||||||||||||
| 3361 | { | ||||||||||||||||||||||||||||||||||||
| 3362 | // Service not supported for socket type. | ||||||||||||||||||||||||||||||||||||
| 3363 | return EAI_SERVICE-8; | ||||||||||||||||||||||||||||||||||||
| 3364 | } | ||||||||||||||||||||||||||||||||||||
| 3365 | } | ||||||||||||||||||||||||||||||||||||
| 3366 | |||||||||||||||||||||||||||||||||||||
| 3367 | return 0; | ||||||||||||||||||||||||||||||||||||
| 3368 | } | ||||||||||||||||||||||||||||||||||||
| 3369 | |||||||||||||||||||||||||||||||||||||
| 3370 | inline int gai_echeck(const char* host, const char* service, | ||||||||||||||||||||||||||||||||||||
| 3371 | int flags, int family, int socktype, int protocol) | ||||||||||||||||||||||||||||||||||||
| 3372 | { | ||||||||||||||||||||||||||||||||||||
| 3373 | (void)(flags); | ||||||||||||||||||||||||||||||||||||
| 3374 | (void)(protocol); | ||||||||||||||||||||||||||||||||||||
| 3375 | |||||||||||||||||||||||||||||||||||||
| 3376 | // Host or service must be specified. | ||||||||||||||||||||||||||||||||||||
| 3377 | if (host == 0 || host[0] == '\0') | ||||||||||||||||||||||||||||||||||||
| 3378 | if (service == 0 || service[0] == '\0') | ||||||||||||||||||||||||||||||||||||
| 3379 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||||
| 3380 | |||||||||||||||||||||||||||||||||||||
| 3381 | // Check combination of family and socket type. | ||||||||||||||||||||||||||||||||||||
| 3382 | switch (family) | ||||||||||||||||||||||||||||||||||||
| 3383 | { | ||||||||||||||||||||||||||||||||||||
| 3384 | case BOOST_ASIO_OS_DEF(AF_UNSPEC)0: | ||||||||||||||||||||||||||||||||||||
| 3385 | break; | ||||||||||||||||||||||||||||||||||||
| 3386 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||||
| 3387 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||||
| 3388 | if (service != 0 && service[0] != '\0') | ||||||||||||||||||||||||||||||||||||
| 3389 | if (socktype != 0 && socktype != SOCK_STREAMSOCK_STREAM && socktype != SOCK_DGRAMSOCK_DGRAM) | ||||||||||||||||||||||||||||||||||||
| 3390 | return EAI_SOCKTYPE-7; | ||||||||||||||||||||||||||||||||||||
| 3391 | break; | ||||||||||||||||||||||||||||||||||||
| 3392 | default: | ||||||||||||||||||||||||||||||||||||
| 3393 | return EAI_FAMILY-6; | ||||||||||||||||||||||||||||||||||||
| 3394 | } | ||||||||||||||||||||||||||||||||||||
| 3395 | |||||||||||||||||||||||||||||||||||||
| 3396 | return 0; | ||||||||||||||||||||||||||||||||||||
| 3397 | } | ||||||||||||||||||||||||||||||||||||
| 3398 | |||||||||||||||||||||||||||||||||||||
| 3399 | inline void freeaddrinfo_emulation(addrinfo_type* aihead) | ||||||||||||||||||||||||||||||||||||
| 3400 | { | ||||||||||||||||||||||||||||||||||||
| 3401 | addrinfo_type* ai = aihead; | ||||||||||||||||||||||||||||||||||||
| 3402 | while (ai) | ||||||||||||||||||||||||||||||||||||
| 3403 | { | ||||||||||||||||||||||||||||||||||||
| 3404 | gai_free(ai->ai_addr); | ||||||||||||||||||||||||||||||||||||
| 3405 | gai_free(ai->ai_canonname); | ||||||||||||||||||||||||||||||||||||
| 3406 | addrinfo_type* ainext = ai->ai_next; | ||||||||||||||||||||||||||||||||||||
| 3407 | gai_free(ai); | ||||||||||||||||||||||||||||||||||||
| 3408 | ai = ainext; | ||||||||||||||||||||||||||||||||||||
| 3409 | } | ||||||||||||||||||||||||||||||||||||
| 3410 | } | ||||||||||||||||||||||||||||||||||||
| 3411 | |||||||||||||||||||||||||||||||||||||
| 3412 | inline int getaddrinfo_emulation(const char* host, const char* service, | ||||||||||||||||||||||||||||||||||||
| 3413 | const addrinfo_type* hintsp, addrinfo_type** result) | ||||||||||||||||||||||||||||||||||||
| 3414 | { | ||||||||||||||||||||||||||||||||||||
| 3415 | // Set up linked list of addrinfo structures. | ||||||||||||||||||||||||||||||||||||
| 3416 | addrinfo_type* aihead = 0; | ||||||||||||||||||||||||||||||||||||
| 3417 | addrinfo_type** ainext = &aihead; | ||||||||||||||||||||||||||||||||||||
| 3418 | char* canon = 0; | ||||||||||||||||||||||||||||||||||||
| 3419 | |||||||||||||||||||||||||||||||||||||
| 3420 | // Supply default hints if not specified by caller. | ||||||||||||||||||||||||||||||||||||
| 3421 | addrinfo_type hints = addrinfo_type(); | ||||||||||||||||||||||||||||||||||||
| 3422 | hints.ai_family = BOOST_ASIO_OS_DEF(AF_UNSPEC)0; | ||||||||||||||||||||||||||||||||||||
| 3423 | if (hintsp) | ||||||||||||||||||||||||||||||||||||
| 3424 | hints = *hintsp; | ||||||||||||||||||||||||||||||||||||
| 3425 | |||||||||||||||||||||||||||||||||||||
| 3426 | // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED | ||||||||||||||||||||||||||||||||||||
| 3427 | // and AI_ALL flags. | ||||||||||||||||||||||||||||||||||||
| 3428 | #if defined(AI_V4MAPPED0x0008) | ||||||||||||||||||||||||||||||||||||
| 3429 | if (hints.ai_family != BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||||
| 3430 | hints.ai_flags &= ~AI_V4MAPPED0x0008; | ||||||||||||||||||||||||||||||||||||
| 3431 | #endif | ||||||||||||||||||||||||||||||||||||
| 3432 | #if defined(AI_ALL0x0010) | ||||||||||||||||||||||||||||||||||||
| 3433 | if (hints.ai_family != BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||||
| 3434 | hints.ai_flags &= ~AI_ALL0x0010; | ||||||||||||||||||||||||||||||||||||
| 3435 | #endif | ||||||||||||||||||||||||||||||||||||
| 3436 | |||||||||||||||||||||||||||||||||||||
| 3437 | // Basic error checking. | ||||||||||||||||||||||||||||||||||||
| 3438 | int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family, | ||||||||||||||||||||||||||||||||||||
| 3439 | hints.ai_socktype, hints.ai_protocol); | ||||||||||||||||||||||||||||||||||||
| 3440 | if (rc != 0) | ||||||||||||||||||||||||||||||||||||
| 3441 | { | ||||||||||||||||||||||||||||||||||||
| 3442 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||||
| 3443 | return rc; | ||||||||||||||||||||||||||||||||||||
| 3444 | } | ||||||||||||||||||||||||||||||||||||
| 3445 | |||||||||||||||||||||||||||||||||||||
| 3446 | gai_search search[2]; | ||||||||||||||||||||||||||||||||||||
| 3447 | int search_count = gai_nsearch(host, &hints, search); | ||||||||||||||||||||||||||||||||||||
| 3448 | for (gai_search* sptr = search; sptr < search + search_count; ++sptr) | ||||||||||||||||||||||||||||||||||||
| 3449 | { | ||||||||||||||||||||||||||||||||||||
| 3450 | // Check for IPv4 dotted decimal string. | ||||||||||||||||||||||||||||||||||||
| 3451 | in4_addr_type inaddr; | ||||||||||||||||||||||||||||||||||||
| 3452 | boost::system::error_code ec; | ||||||||||||||||||||||||||||||||||||
| 3453 | if (socket_ops::inet_pton(BOOST_ASIO_OS_DEF(AF_INET)2, | ||||||||||||||||||||||||||||||||||||
| 3454 | sptr->host, &inaddr, 0, ec) == 1) | ||||||||||||||||||||||||||||||||||||
| 3455 | { | ||||||||||||||||||||||||||||||||||||
| 3456 | if (hints.ai_family != BOOST_ASIO_OS_DEF(AF_UNSPEC)0 | ||||||||||||||||||||||||||||||||||||
| 3457 | && hints.ai_family != BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||||
| 3458 | { | ||||||||||||||||||||||||||||||||||||
| 3459 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||||
| 3460 | gai_free(canon); | ||||||||||||||||||||||||||||||||||||
| 3461 | return EAI_FAMILY-6; | ||||||||||||||||||||||||||||||||||||
| 3462 | } | ||||||||||||||||||||||||||||||||||||
| 3463 | if (sptr->family == BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||||
| 3464 | { | ||||||||||||||||||||||||||||||||||||
| 3465 | rc = gai_aistruct(&ainext, &hints, &inaddr, BOOST_ASIO_OS_DEF(AF_INET)2); | ||||||||||||||||||||||||||||||||||||
| 3466 | if (rc != 0) | ||||||||||||||||||||||||||||||||||||
| 3467 | { | ||||||||||||||||||||||||||||||||||||
| 3468 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||||
| 3469 | gai_free(canon); | ||||||||||||||||||||||||||||||||||||
| 3470 | return rc; | ||||||||||||||||||||||||||||||||||||
| 3471 | } | ||||||||||||||||||||||||||||||||||||
| 3472 | } | ||||||||||||||||||||||||||||||||||||
| 3473 | continue; | ||||||||||||||||||||||||||||||||||||
| 3474 | } | ||||||||||||||||||||||||||||||||||||
| 3475 | |||||||||||||||||||||||||||||||||||||
| 3476 | // Check for IPv6 hex string. | ||||||||||||||||||||||||||||||||||||
| 3477 | in6_addr_type in6addr; | ||||||||||||||||||||||||||||||||||||
| 3478 | if (socket_ops::inet_pton(BOOST_ASIO_OS_DEF(AF_INET6)10, | ||||||||||||||||||||||||||||||||||||
| 3479 | sptr->host, &in6addr, 0, ec) == 1) | ||||||||||||||||||||||||||||||||||||
| 3480 | { | ||||||||||||||||||||||||||||||||||||
| 3481 | if (hints.ai_family != BOOST_ASIO_OS_DEF(AF_UNSPEC)0 | ||||||||||||||||||||||||||||||||||||
| 3482 | && hints.ai_family != BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||||
| 3483 | { | ||||||||||||||||||||||||||||||||||||
| 3484 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||||
| 3485 | gai_free(canon); | ||||||||||||||||||||||||||||||||||||
| 3486 | return EAI_FAMILY-6; | ||||||||||||||||||||||||||||||||||||
| 3487 | } | ||||||||||||||||||||||||||||||||||||
| 3488 | if (sptr->family == BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||||
| 3489 | { | ||||||||||||||||||||||||||||||||||||
| 3490 | rc = gai_aistruct(&ainext, &hints, &in6addr, | ||||||||||||||||||||||||||||||||||||
| 3491 | BOOST_ASIO_OS_DEF(AF_INET6)10); | ||||||||||||||||||||||||||||||||||||
| 3492 | if (rc != 0) | ||||||||||||||||||||||||||||||||||||
| 3493 | { | ||||||||||||||||||||||||||||||||||||
| 3494 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||||
| 3495 | gai_free(canon); | ||||||||||||||||||||||||||||||||||||
| 3496 | return rc; | ||||||||||||||||||||||||||||||||||||
| 3497 | } | ||||||||||||||||||||||||||||||||||||
| 3498 | } | ||||||||||||||||||||||||||||||||||||
| 3499 | continue; | ||||||||||||||||||||||||||||||||||||
| 3500 | } | ||||||||||||||||||||||||||||||||||||
| 3501 | |||||||||||||||||||||||||||||||||||||
| 3502 | // Look up hostname. | ||||||||||||||||||||||||||||||||||||
| 3503 | hostent hent; | ||||||||||||||||||||||||||||||||||||
| 3504 | char hbuf[8192] = ""; | ||||||||||||||||||||||||||||||||||||
| 3505 | hostent* hptr = socket_ops::gethostbyname(sptr->host, | ||||||||||||||||||||||||||||||||||||
| 3506 | sptr->family, &hent, hbuf, sizeof(hbuf), hints.ai_flags, ec); | ||||||||||||||||||||||||||||||||||||
| 3507 | if (hptr == 0) | ||||||||||||||||||||||||||||||||||||
| 3508 | { | ||||||||||||||||||||||||||||||||||||
| 3509 | if (search_count == 2) | ||||||||||||||||||||||||||||||||||||
| 3510 | { | ||||||||||||||||||||||||||||||||||||
| 3511 | // Failure is OK if there are multiple searches. | ||||||||||||||||||||||||||||||||||||
| 3512 | continue; | ||||||||||||||||||||||||||||||||||||
| 3513 | } | ||||||||||||||||||||||||||||||||||||
| 3514 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||||
| 3515 | gai_free(canon); | ||||||||||||||||||||||||||||||||||||
| 3516 | if (ec == boost::asio::error::host_not_found) | ||||||||||||||||||||||||||||||||||||
| 3517 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||||
| 3518 | if (ec == boost::asio::error::host_not_found_try_again) | ||||||||||||||||||||||||||||||||||||
| 3519 | return EAI_AGAIN-3; | ||||||||||||||||||||||||||||||||||||
| 3520 | if (ec == boost::asio::error::no_recovery) | ||||||||||||||||||||||||||||||||||||
| 3521 | return EAI_FAIL-4; | ||||||||||||||||||||||||||||||||||||
| 3522 | if (ec == boost::asio::error::no_data) | ||||||||||||||||||||||||||||||||||||
| 3523 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||||
| 3524 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||||
| 3525 | } | ||||||||||||||||||||||||||||||||||||
| 3526 | |||||||||||||||||||||||||||||||||||||
| 3527 | // Check for address family mismatch if one was specified. | ||||||||||||||||||||||||||||||||||||
| 3528 | if (hints.ai_family != BOOST_ASIO_OS_DEF(AF_UNSPEC)0 | ||||||||||||||||||||||||||||||||||||
| 3529 | && hints.ai_family != hptr->h_addrtype) | ||||||||||||||||||||||||||||||||||||
| 3530 | { | ||||||||||||||||||||||||||||||||||||
| 3531 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||||
| 3532 | gai_free(canon); | ||||||||||||||||||||||||||||||||||||
| 3533 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||||
| 3534 | return EAI_FAMILY-6; | ||||||||||||||||||||||||||||||||||||
| 3535 | } | ||||||||||||||||||||||||||||||||||||
| 3536 | |||||||||||||||||||||||||||||||||||||
| 3537 | // Save canonical name first time. | ||||||||||||||||||||||||||||||||||||
| 3538 | if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0] | ||||||||||||||||||||||||||||||||||||
| 3539 | && (hints.ai_flags & AI_CANONNAME0x0002) && canon == 0) | ||||||||||||||||||||||||||||||||||||
| 3540 | { | ||||||||||||||||||||||||||||||||||||
| 3541 | std::size_t canon_len = strlen(hptr->h_name) + 1; | ||||||||||||||||||||||||||||||||||||
| 3542 | canon = gai_alloc<char>(canon_len); | ||||||||||||||||||||||||||||||||||||
| 3543 | if (canon == 0) | ||||||||||||||||||||||||||||||||||||
| 3544 | { | ||||||||||||||||||||||||||||||||||||
| 3545 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||||
| 3546 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||||
| 3547 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||||
| 3548 | } | ||||||||||||||||||||||||||||||||||||
| 3549 | gai_strcpy(canon, hptr->h_name, canon_len); | ||||||||||||||||||||||||||||||||||||
| 3550 | } | ||||||||||||||||||||||||||||||||||||
| 3551 | |||||||||||||||||||||||||||||||||||||
| 3552 | // Create an addrinfo structure for each returned address. | ||||||||||||||||||||||||||||||||||||
| 3553 | for (char** ap = hptr->h_addr_list; *ap; ++ap) | ||||||||||||||||||||||||||||||||||||
| 3554 | { | ||||||||||||||||||||||||||||||||||||
| 3555 | rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype); | ||||||||||||||||||||||||||||||||||||
| 3556 | if (rc != 0) | ||||||||||||||||||||||||||||||||||||
| 3557 | { | ||||||||||||||||||||||||||||||||||||
| 3558 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||||
| 3559 | gai_free(canon); | ||||||||||||||||||||||||||||||||||||
| 3560 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||||
| 3561 | return EAI_FAMILY-6; | ||||||||||||||||||||||||||||||||||||
| 3562 | } | ||||||||||||||||||||||||||||||||||||
| 3563 | } | ||||||||||||||||||||||||||||||||||||
| 3564 | |||||||||||||||||||||||||||||||||||||
| 3565 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||||
| 3566 | } | ||||||||||||||||||||||||||||||||||||
| 3567 | |||||||||||||||||||||||||||||||||||||
| 3568 | // Check if we found anything. | ||||||||||||||||||||||||||||||||||||
| 3569 | if (aihead == 0) | ||||||||||||||||||||||||||||||||||||
| 3570 | { | ||||||||||||||||||||||||||||||||||||
| 3571 | gai_free(canon); | ||||||||||||||||||||||||||||||||||||
| 3572 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||||
| 3573 | } | ||||||||||||||||||||||||||||||||||||
| 3574 | |||||||||||||||||||||||||||||||||||||
| 3575 | // Return canonical name in first entry. | ||||||||||||||||||||||||||||||||||||
| 3576 | if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME0x0002)) | ||||||||||||||||||||||||||||||||||||
| 3577 | { | ||||||||||||||||||||||||||||||||||||
| 3578 | if (canon) | ||||||||||||||||||||||||||||||||||||
| 3579 | { | ||||||||||||||||||||||||||||||||||||
| 3580 | aihead->ai_canonname = canon; | ||||||||||||||||||||||||||||||||||||
| 3581 | canon = 0; | ||||||||||||||||||||||||||||||||||||
| 3582 | } | ||||||||||||||||||||||||||||||||||||
| 3583 | else | ||||||||||||||||||||||||||||||||||||
| 3584 | { | ||||||||||||||||||||||||||||||||||||
| 3585 | std::size_t canonname_len = strlen(search[0].host) + 1; | ||||||||||||||||||||||||||||||||||||
| 3586 | aihead->ai_canonname = gai_alloc<char>(canonname_len); | ||||||||||||||||||||||||||||||||||||
| 3587 | if (aihead->ai_canonname == 0) | ||||||||||||||||||||||||||||||||||||
| 3588 | { | ||||||||||||||||||||||||||||||||||||
| 3589 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||||
| 3590 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||||
| 3591 | } | ||||||||||||||||||||||||||||||||||||
| 3592 | gai_strcpy(aihead->ai_canonname, search[0].host, canonname_len); | ||||||||||||||||||||||||||||||||||||
| 3593 | } | ||||||||||||||||||||||||||||||||||||
| 3594 | } | ||||||||||||||||||||||||||||||||||||
| 3595 | gai_free(canon); | ||||||||||||||||||||||||||||||||||||
| 3596 | |||||||||||||||||||||||||||||||||||||
| 3597 | // Process the service name. | ||||||||||||||||||||||||||||||||||||
| 3598 | if (service != 0 && service[0] != '\0') | ||||||||||||||||||||||||||||||||||||
| 3599 | { | ||||||||||||||||||||||||||||||||||||
| 3600 | rc = gai_serv(aihead, &hints, service); | ||||||||||||||||||||||||||||||||||||
| 3601 | if (rc != 0) | ||||||||||||||||||||||||||||||||||||
| 3602 | { | ||||||||||||||||||||||||||||||||||||
| 3603 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||||
| 3604 | return rc; | ||||||||||||||||||||||||||||||||||||
| 3605 | } | ||||||||||||||||||||||||||||||||||||
| 3606 | } | ||||||||||||||||||||||||||||||||||||
| 3607 | |||||||||||||||||||||||||||||||||||||
| 3608 | // Return result to caller. | ||||||||||||||||||||||||||||||||||||
| 3609 | *result = aihead; | ||||||||||||||||||||||||||||||||||||
| 3610 | return 0; | ||||||||||||||||||||||||||||||||||||
| 3611 | } | ||||||||||||||||||||||||||||||||||||
| 3612 | |||||||||||||||||||||||||||||||||||||
| 3613 | inline boost::system::error_code getnameinfo_emulation( | ||||||||||||||||||||||||||||||||||||
| 3614 | const socket_addr_type* sa, std::size_t salen, char* host, | ||||||||||||||||||||||||||||||||||||
| 3615 | std::size_t hostlen, char* serv, std::size_t servlen, int flags, | ||||||||||||||||||||||||||||||||||||
| 3616 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 3617 | { | ||||||||||||||||||||||||||||||||||||
| 3618 | using namespace std; | ||||||||||||||||||||||||||||||||||||
| 3619 | |||||||||||||||||||||||||||||||||||||
| 3620 | const char* addr; | ||||||||||||||||||||||||||||||||||||
| 3621 | size_t addr_len; | ||||||||||||||||||||||||||||||||||||
| 3622 | unsigned short port; | ||||||||||||||||||||||||||||||||||||
| 3623 | switch (sa->sa_family) | ||||||||||||||||||||||||||||||||||||
| 3624 | { | ||||||||||||||||||||||||||||||||||||
| 3625 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||||
| 3626 | if (salen != sizeof(sockaddr_in4_type)) | ||||||||||||||||||||||||||||||||||||
| 3627 | { | ||||||||||||||||||||||||||||||||||||
| 3628 | return ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 3629 | } | ||||||||||||||||||||||||||||||||||||
| 3630 | addr = reinterpret_cast<const char*>( | ||||||||||||||||||||||||||||||||||||
| 3631 | &reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_addr); | ||||||||||||||||||||||||||||||||||||
| 3632 | addr_len = sizeof(in4_addr_type); | ||||||||||||||||||||||||||||||||||||
| 3633 | port = reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_port; | ||||||||||||||||||||||||||||||||||||
| 3634 | break; | ||||||||||||||||||||||||||||||||||||
| 3635 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||||
| 3636 | if (salen != sizeof(sockaddr_in6_type)) | ||||||||||||||||||||||||||||||||||||
| 3637 | { | ||||||||||||||||||||||||||||||||||||
| 3638 | return ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 3639 | } | ||||||||||||||||||||||||||||||||||||
| 3640 | addr = reinterpret_cast<const char*>( | ||||||||||||||||||||||||||||||||||||
| 3641 | &reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_addr); | ||||||||||||||||||||||||||||||||||||
| 3642 | addr_len = sizeof(in6_addr_type); | ||||||||||||||||||||||||||||||||||||
| 3643 | port = reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_port; | ||||||||||||||||||||||||||||||||||||
| 3644 | break; | ||||||||||||||||||||||||||||||||||||
| 3645 | default: | ||||||||||||||||||||||||||||||||||||
| 3646 | return ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||||
| 3647 | } | ||||||||||||||||||||||||||||||||||||
| 3648 | |||||||||||||||||||||||||||||||||||||
| 3649 | if (host && hostlen > 0) | ||||||||||||||||||||||||||||||||||||
| 3650 | { | ||||||||||||||||||||||||||||||||||||
| 3651 | if (flags & NI_NUMERICHOST1) | ||||||||||||||||||||||||||||||||||||
| 3652 | { | ||||||||||||||||||||||||||||||||||||
| 3653 | if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0) | ||||||||||||||||||||||||||||||||||||
| 3654 | { | ||||||||||||||||||||||||||||||||||||
| 3655 | return ec; | ||||||||||||||||||||||||||||||||||||
| 3656 | } | ||||||||||||||||||||||||||||||||||||
| 3657 | } | ||||||||||||||||||||||||||||||||||||
| 3658 | else | ||||||||||||||||||||||||||||||||||||
| 3659 | { | ||||||||||||||||||||||||||||||||||||
| 3660 | hostent hent; | ||||||||||||||||||||||||||||||||||||
| 3661 | char hbuf[8192] = ""; | ||||||||||||||||||||||||||||||||||||
| 3662 | hostent* hptr = socket_ops::gethostbyaddr(addr, | ||||||||||||||||||||||||||||||||||||
| 3663 | static_cast<int>(addr_len), sa->sa_family, | ||||||||||||||||||||||||||||||||||||
| 3664 | &hent, hbuf, sizeof(hbuf), ec); | ||||||||||||||||||||||||||||||||||||
| 3665 | if (hptr && hptr->h_name && hptr->h_name[0] != '\0') | ||||||||||||||||||||||||||||||||||||
| 3666 | { | ||||||||||||||||||||||||||||||||||||
| 3667 | if (flags & NI_NOFQDN4) | ||||||||||||||||||||||||||||||||||||
| 3668 | { | ||||||||||||||||||||||||||||||||||||
| 3669 | char* dot = strchr(hptr->h_name, '.'); | ||||||||||||||||||||||||||||||||||||
| 3670 | if (dot) | ||||||||||||||||||||||||||||||||||||
| 3671 | { | ||||||||||||||||||||||||||||||||||||
| 3672 | *dot = 0; | ||||||||||||||||||||||||||||||||||||
| 3673 | } | ||||||||||||||||||||||||||||||||||||
| 3674 | } | ||||||||||||||||||||||||||||||||||||
| 3675 | gai_strcpy(host, hptr->h_name, hostlen); | ||||||||||||||||||||||||||||||||||||
| 3676 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||||
| 3677 | } | ||||||||||||||||||||||||||||||||||||
| 3678 | else | ||||||||||||||||||||||||||||||||||||
| 3679 | { | ||||||||||||||||||||||||||||||||||||
| 3680 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||||
| 3681 | if (flags & NI_NAMEREQD8) | ||||||||||||||||||||||||||||||||||||
| 3682 | { | ||||||||||||||||||||||||||||||||||||
| 3683 | return ec = boost::asio::error::host_not_found; | ||||||||||||||||||||||||||||||||||||
| 3684 | } | ||||||||||||||||||||||||||||||||||||
| 3685 | if (socket_ops::inet_ntop(sa->sa_family, | ||||||||||||||||||||||||||||||||||||
| 3686 | addr, host, hostlen, 0, ec) == 0) | ||||||||||||||||||||||||||||||||||||
| 3687 | { | ||||||||||||||||||||||||||||||||||||
| 3688 | return ec; | ||||||||||||||||||||||||||||||||||||
| 3689 | } | ||||||||||||||||||||||||||||||||||||
| 3690 | } | ||||||||||||||||||||||||||||||||||||
| 3691 | } | ||||||||||||||||||||||||||||||||||||
| 3692 | } | ||||||||||||||||||||||||||||||||||||
| 3693 | |||||||||||||||||||||||||||||||||||||
| 3694 | if (serv && servlen > 0) | ||||||||||||||||||||||||||||||||||||
| 3695 | { | ||||||||||||||||||||||||||||||||||||
| 3696 | if (flags & NI_NUMERICSERV2) | ||||||||||||||||||||||||||||||||||||
| 3697 | { | ||||||||||||||||||||||||||||||||||||
| 3698 | if (servlen < 6) | ||||||||||||||||||||||||||||||||||||
| 3699 | { | ||||||||||||||||||||||||||||||||||||
| 3700 | return ec = boost::asio::error::no_buffer_space; | ||||||||||||||||||||||||||||||||||||
| 3701 | } | ||||||||||||||||||||||||||||||||||||
| 3702 | #if defined(BOOST_ASIO_HAS_SNPRINTF) | ||||||||||||||||||||||||||||||||||||
| 3703 | snprintf(serv, servlen, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||||
| 3704 | #elif defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 3705 | sprintf_s(serv, servlen, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||||
| 3706 | #else // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 3707 | sprintf(serv, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||||
| 3708 | #endif // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 3709 | } | ||||||||||||||||||||||||||||||||||||
| 3710 | else | ||||||||||||||||||||||||||||||||||||
| 3711 | { | ||||||||||||||||||||||||||||||||||||
| 3712 | #if defined(BOOST_ASIO_HAS_PTHREADS1) | ||||||||||||||||||||||||||||||||||||
| 3713 | static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER{ { 0, 0, 0, 0, PTHREAD_MUTEX_TIMED_NP, 0, 0, { __null, __null } } }; | ||||||||||||||||||||||||||||||||||||
| 3714 | ::pthread_mutex_lock(&mutex); | ||||||||||||||||||||||||||||||||||||
| 3715 | #endif // defined(BOOST_ASIO_HAS_PTHREADS) | ||||||||||||||||||||||||||||||||||||
| 3716 | servent* sptr = ::getservbyport(port, (flags & NI_DGRAM16) ? "udp" : 0); | ||||||||||||||||||||||||||||||||||||
| 3717 | if (sptr && sptr->s_name && sptr->s_name[0] != '\0') | ||||||||||||||||||||||||||||||||||||
| 3718 | { | ||||||||||||||||||||||||||||||||||||
| 3719 | gai_strcpy(serv, sptr->s_name, servlen); | ||||||||||||||||||||||||||||||||||||
| 3720 | } | ||||||||||||||||||||||||||||||||||||
| 3721 | else | ||||||||||||||||||||||||||||||||||||
| 3722 | { | ||||||||||||||||||||||||||||||||||||
| 3723 | if (servlen < 6) | ||||||||||||||||||||||||||||||||||||
| 3724 | { | ||||||||||||||||||||||||||||||||||||
| 3725 | return ec = boost::asio::error::no_buffer_space; | ||||||||||||||||||||||||||||||||||||
| 3726 | } | ||||||||||||||||||||||||||||||||||||
| 3727 | #if defined(BOOST_ASIO_HAS_SNPRINTF) | ||||||||||||||||||||||||||||||||||||
| 3728 | snprintf(serv, servlen, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||||
| 3729 | #elif defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 3730 | sprintf_s(serv, servlen, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||||
| 3731 | #else // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 3732 | sprintf(serv, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||||
| 3733 | #endif // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||||
| 3734 | } | ||||||||||||||||||||||||||||||||||||
| 3735 | #if defined(BOOST_ASIO_HAS_PTHREADS1) | ||||||||||||||||||||||||||||||||||||
| 3736 | ::pthread_mutex_unlock(&mutex); | ||||||||||||||||||||||||||||||||||||
| 3737 | #endif // defined(BOOST_ASIO_HAS_PTHREADS) | ||||||||||||||||||||||||||||||||||||
| 3738 | } | ||||||||||||||||||||||||||||||||||||
| 3739 | } | ||||||||||||||||||||||||||||||||||||
| 3740 | |||||||||||||||||||||||||||||||||||||
| 3741 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||||
| 3742 | return ec; | ||||||||||||||||||||||||||||||||||||
| 3743 | } | ||||||||||||||||||||||||||||||||||||
| 3744 | |||||||||||||||||||||||||||||||||||||
| 3745 | #endif // !defined(BOOST_ASIO_HAS_GETADDRINFO) | ||||||||||||||||||||||||||||||||||||
| 3746 | |||||||||||||||||||||||||||||||||||||
| 3747 | inline boost::system::error_code translate_addrinfo_error(int error) | ||||||||||||||||||||||||||||||||||||
| 3748 | { | ||||||||||||||||||||||||||||||||||||
| 3749 | switch (error) | ||||||||||||||||||||||||||||||||||||
| 3750 | { | ||||||||||||||||||||||||||||||||||||
| 3751 | case 0: | ||||||||||||||||||||||||||||||||||||
| 3752 | return boost::system::error_code(); | ||||||||||||||||||||||||||||||||||||
| 3753 | case EAI_AGAIN-3: | ||||||||||||||||||||||||||||||||||||
| 3754 | return boost::asio::error::host_not_found_try_again; | ||||||||||||||||||||||||||||||||||||
| 3755 | case EAI_BADFLAGS-1: | ||||||||||||||||||||||||||||||||||||
| 3756 | return boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||||
| 3757 | case EAI_FAIL-4: | ||||||||||||||||||||||||||||||||||||
| 3758 | return boost::asio::error::no_recovery; | ||||||||||||||||||||||||||||||||||||
| 3759 | case EAI_FAMILY-6: | ||||||||||||||||||||||||||||||||||||
| 3760 | return boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||||
| 3761 | case EAI_MEMORY-10: | ||||||||||||||||||||||||||||||||||||
| 3762 | return boost::asio::error::no_memory; | ||||||||||||||||||||||||||||||||||||
| 3763 | case EAI_NONAME-2: | ||||||||||||||||||||||||||||||||||||
| 3764 | #if defined(EAI_ADDRFAMILY-9) | ||||||||||||||||||||||||||||||||||||
| 3765 | case EAI_ADDRFAMILY-9: | ||||||||||||||||||||||||||||||||||||
| 3766 | #endif | ||||||||||||||||||||||||||||||||||||
| 3767 | #if defined(EAI_NODATA-5) && (EAI_NODATA-5 != EAI_NONAME-2) | ||||||||||||||||||||||||||||||||||||
| 3768 | case EAI_NODATA-5: | ||||||||||||||||||||||||||||||||||||
| 3769 | #endif | ||||||||||||||||||||||||||||||||||||
| 3770 | return boost::asio::error::host_not_found; | ||||||||||||||||||||||||||||||||||||
| 3771 | case EAI_SERVICE-8: | ||||||||||||||||||||||||||||||||||||
| 3772 | return boost::asio::error::service_not_found; | ||||||||||||||||||||||||||||||||||||
| 3773 | case EAI_SOCKTYPE-7: | ||||||||||||||||||||||||||||||||||||
| 3774 | return boost::asio::error::socket_type_not_supported; | ||||||||||||||||||||||||||||||||||||
| 3775 | default: // Possibly the non-portable EAI_SYSTEM. | ||||||||||||||||||||||||||||||||||||
| 3776 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 3777 | return boost::system::error_code( | ||||||||||||||||||||||||||||||||||||
| 3778 | WSAGetLastError(), boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||||
| 3779 | #else | ||||||||||||||||||||||||||||||||||||
| 3780 | return boost::system::error_code( | ||||||||||||||||||||||||||||||||||||
| 3781 | errno(*__errno_location ()), boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||||
| 3782 | #endif | ||||||||||||||||||||||||||||||||||||
| 3783 | } | ||||||||||||||||||||||||||||||||||||
| 3784 | } | ||||||||||||||||||||||||||||||||||||
| 3785 | |||||||||||||||||||||||||||||||||||||
| 3786 | boost::system::error_code getaddrinfo(const char* host, | ||||||||||||||||||||||||||||||||||||
| 3787 | const char* service, const addrinfo_type& hints, | ||||||||||||||||||||||||||||||||||||
| 3788 | addrinfo_type** result, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 3789 | { | ||||||||||||||||||||||||||||||||||||
| 3790 | host = (host && *host) ? host : 0; | ||||||||||||||||||||||||||||||||||||
| 3791 | service = (service && *service) ? service : 0; | ||||||||||||||||||||||||||||||||||||
| 3792 | clear_last_error(); | ||||||||||||||||||||||||||||||||||||
| 3793 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 3794 | # if defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||||
| 3795 | // Building for Windows XP, Windows Server 2003, or later. | ||||||||||||||||||||||||||||||||||||
| 3796 | int error = ::getaddrinfo(host, service, &hints, result); | ||||||||||||||||||||||||||||||||||||
| 3797 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||||
| 3798 | # else | ||||||||||||||||||||||||||||||||||||
| 3799 | // Building for Windows 2000 or earlier. | ||||||||||||||||||||||||||||||||||||
| 3800 | typedef int (WSAAPI *gai_t)(const char*, | ||||||||||||||||||||||||||||||||||||
| 3801 | const char*, const addrinfo_type*, addrinfo_type**); | ||||||||||||||||||||||||||||||||||||
| 3802 | if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) | ||||||||||||||||||||||||||||||||||||
| 3803 | { | ||||||||||||||||||||||||||||||||||||
| 3804 | if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) | ||||||||||||||||||||||||||||||||||||
| 3805 | { | ||||||||||||||||||||||||||||||||||||
| 3806 | int error = gai(host, service, &hints, result); | ||||||||||||||||||||||||||||||||||||
| 3807 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||||
| 3808 | } | ||||||||||||||||||||||||||||||||||||
| 3809 | } | ||||||||||||||||||||||||||||||||||||
| 3810 | int error = getaddrinfo_emulation(host, service, &hints, result); | ||||||||||||||||||||||||||||||||||||
| 3811 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||||
| 3812 | # endif | ||||||||||||||||||||||||||||||||||||
| 3813 | #elif !defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||||
| 3814 | int error = getaddrinfo_emulation(host, service, &hints, result); | ||||||||||||||||||||||||||||||||||||
| 3815 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||||
| 3816 | #else | ||||||||||||||||||||||||||||||||||||
| 3817 | int error = ::getaddrinfo(host, service, &hints, result); | ||||||||||||||||||||||||||||||||||||
| 3818 | #if defined(__MACH__) && defined(__APPLE__) | ||||||||||||||||||||||||||||||||||||
| 3819 | using namespace std; // For isdigit and atoi. | ||||||||||||||||||||||||||||||||||||
| 3820 | if (error == 0 && service && isdigit(static_cast<unsigned char>(service[0]))) | ||||||||||||||||||||||||||||||||||||
| 3821 | { | ||||||||||||||||||||||||||||||||||||
| 3822 | u_short_type port = host_to_network_short(atoi(service)); | ||||||||||||||||||||||||||||||||||||
| 3823 | for (addrinfo_type* ai = *result; ai; ai = ai->ai_next) | ||||||||||||||||||||||||||||||||||||
| 3824 | { | ||||||||||||||||||||||||||||||||||||
| 3825 | switch (ai->ai_family) | ||||||||||||||||||||||||||||||||||||
| 3826 | { | ||||||||||||||||||||||||||||||||||||
| 3827 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||||
| 3828 | { | ||||||||||||||||||||||||||||||||||||
| 3829 | sockaddr_in4_type* sinptr = | ||||||||||||||||||||||||||||||||||||
| 3830 | reinterpret_cast<sockaddr_in4_type*>(ai->ai_addr); | ||||||||||||||||||||||||||||||||||||
| 3831 | if (sinptr->sin_port == 0) | ||||||||||||||||||||||||||||||||||||
| 3832 | sinptr->sin_port = port; | ||||||||||||||||||||||||||||||||||||
| 3833 | break; | ||||||||||||||||||||||||||||||||||||
| 3834 | } | ||||||||||||||||||||||||||||||||||||
| 3835 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||||
| 3836 | { | ||||||||||||||||||||||||||||||||||||
| 3837 | sockaddr_in6_type* sin6ptr = | ||||||||||||||||||||||||||||||||||||
| 3838 | reinterpret_cast<sockaddr_in6_type*>(ai->ai_addr); | ||||||||||||||||||||||||||||||||||||
| 3839 | if (sin6ptr->sin6_port == 0) | ||||||||||||||||||||||||||||||||||||
| 3840 | sin6ptr->sin6_port = port; | ||||||||||||||||||||||||||||||||||||
| 3841 | break; | ||||||||||||||||||||||||||||||||||||
| 3842 | } | ||||||||||||||||||||||||||||||||||||
| 3843 | default: | ||||||||||||||||||||||||||||||||||||
| 3844 | break; | ||||||||||||||||||||||||||||||||||||
| 3845 | } | ||||||||||||||||||||||||||||||||||||
| 3846 | } | ||||||||||||||||||||||||||||||||||||
| 3847 | } | ||||||||||||||||||||||||||||||||||||
| 3848 | #endif | ||||||||||||||||||||||||||||||||||||
| 3849 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||||
| 3850 | #endif | ||||||||||||||||||||||||||||||||||||
| 3851 | } | ||||||||||||||||||||||||||||||||||||
| 3852 | |||||||||||||||||||||||||||||||||||||
| 3853 | boost::system::error_code background_getaddrinfo( | ||||||||||||||||||||||||||||||||||||
| 3854 | const weak_cancel_token_type& cancel_token, const char* host, | ||||||||||||||||||||||||||||||||||||
| 3855 | const char* service, const addrinfo_type& hints, | ||||||||||||||||||||||||||||||||||||
| 3856 | addrinfo_type** result, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 3857 | { | ||||||||||||||||||||||||||||||||||||
| 3858 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||||
| 3859 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||||
| 3860 | else | ||||||||||||||||||||||||||||||||||||
| 3861 | socket_ops::getaddrinfo(host, service, hints, result, ec); | ||||||||||||||||||||||||||||||||||||
| 3862 | return ec; | ||||||||||||||||||||||||||||||||||||
| 3863 | } | ||||||||||||||||||||||||||||||||||||
| 3864 | |||||||||||||||||||||||||||||||||||||
| 3865 | void freeaddrinfo(addrinfo_type* ai) | ||||||||||||||||||||||||||||||||||||
| 3866 | { | ||||||||||||||||||||||||||||||||||||
| 3867 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 3868 | # if defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||||
| 3869 | // Building for Windows XP, Windows Server 2003, or later. | ||||||||||||||||||||||||||||||||||||
| 3870 | ::freeaddrinfo(ai); | ||||||||||||||||||||||||||||||||||||
| 3871 | # else | ||||||||||||||||||||||||||||||||||||
| 3872 | // Building for Windows 2000 or earlier. | ||||||||||||||||||||||||||||||||||||
| 3873 | typedef int (WSAAPI *fai_t)(addrinfo_type*); | ||||||||||||||||||||||||||||||||||||
| 3874 | if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) | ||||||||||||||||||||||||||||||||||||
| 3875 | { | ||||||||||||||||||||||||||||||||||||
| 3876 | if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) | ||||||||||||||||||||||||||||||||||||
| 3877 | { | ||||||||||||||||||||||||||||||||||||
| 3878 | fai(ai); | ||||||||||||||||||||||||||||||||||||
| 3879 | return; | ||||||||||||||||||||||||||||||||||||
| 3880 | } | ||||||||||||||||||||||||||||||||||||
| 3881 | } | ||||||||||||||||||||||||||||||||||||
| 3882 | freeaddrinfo_emulation(ai); | ||||||||||||||||||||||||||||||||||||
| 3883 | # endif | ||||||||||||||||||||||||||||||||||||
| 3884 | #elif !defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||||
| 3885 | freeaddrinfo_emulation(ai); | ||||||||||||||||||||||||||||||||||||
| 3886 | #else | ||||||||||||||||||||||||||||||||||||
| 3887 | ::freeaddrinfo(ai); | ||||||||||||||||||||||||||||||||||||
| 3888 | #endif | ||||||||||||||||||||||||||||||||||||
| 3889 | } | ||||||||||||||||||||||||||||||||||||
| 3890 | |||||||||||||||||||||||||||||||||||||
| 3891 | boost::system::error_code getnameinfo(const void* addr, | ||||||||||||||||||||||||||||||||||||
| 3892 | std::size_t addrlen, char* host, std::size_t hostlen, | ||||||||||||||||||||||||||||||||||||
| 3893 | char* serv, std::size_t servlen, int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 3894 | { | ||||||||||||||||||||||||||||||||||||
| 3895 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||||
| 3896 | # if defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||||
| 3897 | // Building for Windows XP, Windows Server 2003, or later. | ||||||||||||||||||||||||||||||||||||
| 3898 | clear_last_error(); | ||||||||||||||||||||||||||||||||||||
| 3899 | int error = ::getnameinfo(static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||||
| 3900 | static_cast<socklen_t>(addrlen), host, static_cast<DWORD>(hostlen), | ||||||||||||||||||||||||||||||||||||
| 3901 | serv, static_cast<DWORD>(servlen), flags); | ||||||||||||||||||||||||||||||||||||
| 3902 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||||
| 3903 | # else | ||||||||||||||||||||||||||||||||||||
| 3904 | // Building for Windows 2000 or earlier. | ||||||||||||||||||||||||||||||||||||
| 3905 | typedef int (WSAAPI *gni_t)(const socket_addr_type*, | ||||||||||||||||||||||||||||||||||||
| 3906 | int, char*, DWORD, char*, DWORD, int); | ||||||||||||||||||||||||||||||||||||
| 3907 | if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) | ||||||||||||||||||||||||||||||||||||
| 3908 | { | ||||||||||||||||||||||||||||||||||||
| 3909 | if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo")) | ||||||||||||||||||||||||||||||||||||
| 3910 | { | ||||||||||||||||||||||||||||||||||||
| 3911 | clear_last_error(); | ||||||||||||||||||||||||||||||||||||
| 3912 | int error = gni(static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||||
| 3913 | static_cast<int>(addrlen), host, static_cast<DWORD>(hostlen), | ||||||||||||||||||||||||||||||||||||
| 3914 | serv, static_cast<DWORD>(servlen), flags); | ||||||||||||||||||||||||||||||||||||
| 3915 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||||
| 3916 | } | ||||||||||||||||||||||||||||||||||||
| 3917 | } | ||||||||||||||||||||||||||||||||||||
| 3918 | clear_last_error(); | ||||||||||||||||||||||||||||||||||||
| 3919 | return getnameinfo_emulation(static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||||
| 3920 | addrlen, host, hostlen, serv, servlen, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 3921 | # endif | ||||||||||||||||||||||||||||||||||||
| 3922 | #elif !defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||||
| 3923 | using namespace std; // For memcpy. | ||||||||||||||||||||||||||||||||||||
| 3924 | sockaddr_storage_type tmp_addr; | ||||||||||||||||||||||||||||||||||||
| 3925 | memcpy(&tmp_addr, addr, addrlen); | ||||||||||||||||||||||||||||||||||||
| 3926 | addr = &tmp_addr; | ||||||||||||||||||||||||||||||||||||
| 3927 | clear_last_error(); | ||||||||||||||||||||||||||||||||||||
| 3928 | return getnameinfo_emulation(static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||||
| 3929 | addrlen, host, hostlen, serv, servlen, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 3930 | #else | ||||||||||||||||||||||||||||||||||||
| 3931 | clear_last_error(); | ||||||||||||||||||||||||||||||||||||
| 3932 | int error = ::getnameinfo(static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||||
| 3933 | addrlen, host, hostlen, serv, servlen, flags); | ||||||||||||||||||||||||||||||||||||
| 3934 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||||
| 3935 | #endif | ||||||||||||||||||||||||||||||||||||
| 3936 | } | ||||||||||||||||||||||||||||||||||||
| 3937 | |||||||||||||||||||||||||||||||||||||
| 3938 | boost::system::error_code sync_getnameinfo(const void* addr, | ||||||||||||||||||||||||||||||||||||
| 3939 | std::size_t addrlen, char* host, std::size_t hostlen, char* serv, | ||||||||||||||||||||||||||||||||||||
| 3940 | std::size_t servlen, int sock_type, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 3941 | { | ||||||||||||||||||||||||||||||||||||
| 3942 | // First try resolving with the service name. If that fails try resolving | ||||||||||||||||||||||||||||||||||||
| 3943 | // but allow the service to be returned as a number. | ||||||||||||||||||||||||||||||||||||
| 3944 | int flags = (sock_type == SOCK_DGRAMSOCK_DGRAM) ? NI_DGRAM16 : 0; | ||||||||||||||||||||||||||||||||||||
| 3945 | socket_ops::getnameinfo(addr, addrlen, host, | ||||||||||||||||||||||||||||||||||||
| 3946 | hostlen, serv, servlen, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 3947 | if (ec) | ||||||||||||||||||||||||||||||||||||
| 3948 | { | ||||||||||||||||||||||||||||||||||||
| 3949 | socket_ops::getnameinfo(addr, addrlen, host, hostlen, | ||||||||||||||||||||||||||||||||||||
| 3950 | serv, servlen, flags | NI_NUMERICSERV2, ec); | ||||||||||||||||||||||||||||||||||||
| 3951 | } | ||||||||||||||||||||||||||||||||||||
| 3952 | |||||||||||||||||||||||||||||||||||||
| 3953 | return ec; | ||||||||||||||||||||||||||||||||||||
| 3954 | } | ||||||||||||||||||||||||||||||||||||
| 3955 | |||||||||||||||||||||||||||||||||||||
| 3956 | boost::system::error_code background_getnameinfo( | ||||||||||||||||||||||||||||||||||||
| 3957 | const weak_cancel_token_type& cancel_token, | ||||||||||||||||||||||||||||||||||||
| 3958 | const void* addr, std::size_t addrlen, | ||||||||||||||||||||||||||||||||||||
| 3959 | char* host, std::size_t hostlen, char* serv, | ||||||||||||||||||||||||||||||||||||
| 3960 | std::size_t servlen, int sock_type, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||||
| 3961 | { | ||||||||||||||||||||||||||||||||||||
| 3962 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||||
| 3963 | { | ||||||||||||||||||||||||||||||||||||
| 3964 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||||
| 3965 | } | ||||||||||||||||||||||||||||||||||||
| 3966 | else | ||||||||||||||||||||||||||||||||||||
| 3967 | { | ||||||||||||||||||||||||||||||||||||
| 3968 | // First try resolving with the service name. If that fails try resolving | ||||||||||||||||||||||||||||||||||||
| 3969 | // but allow the service to be returned as a number. | ||||||||||||||||||||||||||||||||||||
| 3970 | int flags = (sock_type == SOCK_DGRAMSOCK_DGRAM) ? NI_DGRAM16 : 0; | ||||||||||||||||||||||||||||||||||||
| 3971 | socket_ops::getnameinfo(addr, addrlen, host, | ||||||||||||||||||||||||||||||||||||
| 3972 | hostlen, serv, servlen, flags, ec); | ||||||||||||||||||||||||||||||||||||
| 3973 | if (ec) | ||||||||||||||||||||||||||||||||||||
| 3974 | { | ||||||||||||||||||||||||||||||||||||
| 3975 | socket_ops::getnameinfo(addr, addrlen, host, hostlen, | ||||||||||||||||||||||||||||||||||||
| 3976 | serv, servlen, flags | NI_NUMERICSERV2, ec); | ||||||||||||||||||||||||||||||||||||
| 3977 | } | ||||||||||||||||||||||||||||||||||||
| 3978 | } | ||||||||||||||||||||||||||||||||||||
| 3979 | |||||||||||||||||||||||||||||||||||||
| 3980 | return ec; | ||||||||||||||||||||||||||||||||||||
| 3981 | } | ||||||||||||||||||||||||||||||||||||
| 3982 | |||||||||||||||||||||||||||||||||||||
| 3983 | #endif // !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 3984 | |||||||||||||||||||||||||||||||||||||
| 3985 | u_long_type network_to_host_long(u_long_type value) | ||||||||||||||||||||||||||||||||||||
| 3986 | { | ||||||||||||||||||||||||||||||||||||
| 3987 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 3988 | unsigned char* value_p = reinterpret_cast<unsigned char*>(&value); | ||||||||||||||||||||||||||||||||||||
| 3989 | u_long_type result = (static_cast<u_long_type>(value_p[0]) << 24) | ||||||||||||||||||||||||||||||||||||
| 3990 | | (static_cast<u_long_type>(value_p[1]) << 16) | ||||||||||||||||||||||||||||||||||||
| 3991 | | (static_cast<u_long_type>(value_p[2]) << 8) | ||||||||||||||||||||||||||||||||||||
| 3992 | | static_cast<u_long_type>(value_p[3]); | ||||||||||||||||||||||||||||||||||||
| 3993 | return result; | ||||||||||||||||||||||||||||||||||||
| 3994 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 3995 | return ntohl(value)__bswap_32 (value); | ||||||||||||||||||||||||||||||||||||
| 3996 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 3997 | } | ||||||||||||||||||||||||||||||||||||
| 3998 | |||||||||||||||||||||||||||||||||||||
| 3999 | u_long_type host_to_network_long(u_long_type value) | ||||||||||||||||||||||||||||||||||||
| 4000 | { | ||||||||||||||||||||||||||||||||||||
| 4001 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 4002 | u_long_type result; | ||||||||||||||||||||||||||||||||||||
| 4003 | unsigned char* result_p = reinterpret_cast<unsigned char*>(&result); | ||||||||||||||||||||||||||||||||||||
| 4004 | result_p[0] = static_cast<unsigned char>((value >> 24) & 0xFF); | ||||||||||||||||||||||||||||||||||||
| 4005 | result_p[1] = static_cast<unsigned char>((value >> 16) & 0xFF); | ||||||||||||||||||||||||||||||||||||
| 4006 | result_p[2] = static_cast<unsigned char>((value >> 8) & 0xFF); | ||||||||||||||||||||||||||||||||||||
| 4007 | result_p[3] = static_cast<unsigned char>(value & 0xFF); | ||||||||||||||||||||||||||||||||||||
| 4008 | return result; | ||||||||||||||||||||||||||||||||||||
| 4009 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 4010 | return htonl(value)__bswap_32 (value); | ||||||||||||||||||||||||||||||||||||
| 4011 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 4012 | } | ||||||||||||||||||||||||||||||||||||
| 4013 | |||||||||||||||||||||||||||||||||||||
| 4014 | u_short_type network_to_host_short(u_short_type value) | ||||||||||||||||||||||||||||||||||||
| 4015 | { | ||||||||||||||||||||||||||||||||||||
| 4016 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 4017 | unsigned char* value_p = reinterpret_cast<unsigned char*>(&value); | ||||||||||||||||||||||||||||||||||||
| 4018 | u_short_type result = (static_cast<u_short_type>(value_p[0]) << 8) | ||||||||||||||||||||||||||||||||||||
| 4019 | | static_cast<u_short_type>(value_p[1]); | ||||||||||||||||||||||||||||||||||||
| 4020 | return result; | ||||||||||||||||||||||||||||||||||||
| 4021 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 4022 | return ntohs(value)__bswap_16 (value); | ||||||||||||||||||||||||||||||||||||
| 4023 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 4024 | } | ||||||||||||||||||||||||||||||||||||
| 4025 | |||||||||||||||||||||||||||||||||||||
| 4026 | u_short_type host_to_network_short(u_short_type value) | ||||||||||||||||||||||||||||||||||||
| 4027 | { | ||||||||||||||||||||||||||||||||||||
| 4028 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 4029 | u_short_type result; | ||||||||||||||||||||||||||||||||||||
| 4030 | unsigned char* result_p = reinterpret_cast<unsigned char*>(&result); | ||||||||||||||||||||||||||||||||||||
| 4031 | result_p[0] = static_cast<unsigned char>((value >> 8) & 0xFF); | ||||||||||||||||||||||||||||||||||||
| 4032 | result_p[1] = static_cast<unsigned char>(value & 0xFF); | ||||||||||||||||||||||||||||||||||||
| 4033 | return result; | ||||||||||||||||||||||||||||||||||||
| 4034 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 4035 | return htons(value)__bswap_16 (value); | ||||||||||||||||||||||||||||||||||||
| 4036 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||||
| 4037 | } | ||||||||||||||||||||||||||||||||||||
| 4038 | |||||||||||||||||||||||||||||||||||||
| 4039 | } // namespace socket_ops | ||||||||||||||||||||||||||||||||||||
| 4040 | } // namespace detail | ||||||||||||||||||||||||||||||||||||
| 4041 | } // namespace asio | ||||||||||||||||||||||||||||||||||||
| 4042 | } // namespace boost | ||||||||||||||||||||||||||||||||||||
| 4043 | |||||||||||||||||||||||||||||||||||||
| 4044 | #include <boost/asio/detail/pop_options.hpp> | ||||||||||||||||||||||||||||||||||||
| 4045 | |||||||||||||||||||||||||||||||||||||
| 4046 | #endif // BOOST_ASIO_DETAIL_SOCKET_OPS_IPP |