| File: | usr/include/boost/asio/detail/impl/socket_ops.ipp |
| Warning: | line 811, 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(), 0)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0", mt_listener_mgr_->getThreadCount(), 0))) ; 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(), 1)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("responses.size()" , "1", responses.size(), 1))) ; 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, num_clients / clients_per_thread.size())switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("it.second" , "num_clients / clients_per_thread.size()", it.second, num_clients / clients_per_thread.size()))) ; else ::testing::internal::AssertHelper (::testing::TestPartResult::kNonFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 533, gtest_ar.failure_message()) = ::testing::Message() | |||
| 534 | << "thread-id: " << it.first | |||
| 535 | << ", clients: " << it.second << std::endl; | |||
| 536 | } | |||
| 537 | } | |||
| 538 | ||||
| 539 | /// @brief Pauses and resumes a MtTcpListener while it processes command | |||
| 540 | /// requests. | |||
| 541 | /// | |||
| 542 | /// This function command will create a MtTcpListenerMgr | |||
| 543 | /// with the given number of threads, initiates the given | |||
| 544 | /// number of clients, each requesting the "thread" command, | |||
| 545 | /// and then iteratively runs the test's IOService until all | |||
| 546 | /// the clients have received their responses or an error occurs. | |||
| 547 | /// It will pause and resume the listener at intervals governed | |||
| 548 | /// by the given number of pauses. | |||
| 549 | /// | |||
| 550 | /// @param num_threads - the number of threads the MtTcpListener | |||
| 551 | /// should use. Must be greater than 0. | |||
| 552 | /// @param num_clients - the number of clients that should issue the | |||
| 553 | /// thread command. Each client is used to carry out a single thread | |||
| 554 | /// command request. Must be greater than 0. | |||
| 555 | /// @param num_pauses Desired number of times the listener should be | |||
| 556 | /// paused during the test. Must be greater than 0. | |||
| 557 | void workPauseAndResume(size_t num_threads, size_t num_clients, | |||
| 558 | size_t num_pauses) { | |||
| 559 | // First we makes sure the parameter rules apply. | |||
| 560 | 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" , 560, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "num_threads", "false", "true") .c_str()) = ::testing::Message (); | |||
| 561 | 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" , 561, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "num_clients", "false", "true") .c_str()) = ::testing::Message (); | |||
| 562 | 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" , 562, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "num_pauses", "false", "true") .c_str()) = ::testing::Message (); | |||
| 563 | num_threads_ = num_threads; | |||
| 564 | num_clients_ = num_clients; | |||
| 565 | ||||
| 566 | // Create an MtTcpListenerMgr with prescribed number of threads and the | |||
| 567 | // simple handler. | |||
| 568 | createMtTcpListenerMgr(num_threads, | |||
| 569 | std::bind(&MtTcpListenerMgrTest::simpleCommandHandler, | |||
| 570 | this, ph::_1)); | |||
| 571 | ||||
| 572 | 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" , 572, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_", "false", "true") .c_str()) = ::testing:: Message(); | |||
| 573 | ||||
| 574 | // Start it and verify it is running. | |||
| 575 | 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" , 575, "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" , 575, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 576 | 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" , 576, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 577 | 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" , 577, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 578 | ||||
| 579 | // Initiate the prescribed number of command requests. | |||
| 580 | num_in_progress_ = 0; | |||
| 581 | while (clients_.size() < num_clients) { | |||
| 582 | 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" , 582, "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" , 582, "Failed") = ::testing::Message() << "startThreadCommand(\"I am done\")" << " threw non-std::exception"; } }; | |||
| 583 | } | |||
| 584 | ||||
| 585 | // Now we run the client-side IOService until all requests are done, | |||
| 586 | // errors occur or the test times out. We'll pause and resume the | |||
| 587 | // number of times given by num_pauses. | |||
| 588 | size_t num_done = 0; | |||
| 589 | size_t total_requests = clients_.size(); | |||
| 590 | while (num_done < total_requests) { | |||
| 591 | // Calculate how many more requests to process before we pause again. | |||
| 592 | // We divide the number of outstanding requests by the number of pauses | |||
| 593 | // and stop after we've done at least that many more requests. | |||
| 594 | size_t request_limit = (pause_cnt_ < num_pauses ? | |||
| 595 | (num_done + ((total_requests - num_done) / num_pauses)) | |||
| 596 | : total_requests); | |||
| 597 | ||||
| 598 | // Run test IOService until we hit the limit. | |||
| 599 | runIOService(request_limit); | |||
| 600 | ||||
| 601 | // If we've done all our pauses we should be through. | |||
| 602 | if (pause_cnt_ == num_pauses) { | |||
| 603 | break; | |||
| 604 | } | |||
| 605 | ||||
| 606 | // Pause the client. | |||
| 607 | 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_607 ; } catch (...) { gtest_msg.value = "it throws."; goto gtest_label_testnothrow_607 ; } } else gtest_label_testnothrow_607 : return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 607, ("Expected: " "mt_listener_mgr_->pause()" " doesn't throw an exception.\n" " Actual: " + gtest_msg.value) .c_str()) = ::testing::Message (); | |||
| 608 | 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" , 608, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isPaused()", "false", "true") .c_str( )) = ::testing::Message(); | |||
| 609 | ++pause_cnt_; | |||
| 610 | ||||
| 611 | // Check our progress. | |||
| 612 | num_done = 0; | |||
| 613 | for (auto const& client : clients_) { | |||
| 614 | if (client->receiveDone()) { | |||
| 615 | ++num_done; | |||
| 616 | } | |||
| 617 | } | |||
| 618 | ||||
| 619 | // We should completed at least as many as our | |||
| 620 | // target limit. | |||
| 621 | 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" , 621, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 622 | ||||
| 623 | // Resume the listener. | |||
| 624 | 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_624 ; } catch (...) { gtest_msg.value = "it throws."; goto gtest_label_testnothrow_624 ; } } else gtest_label_testnothrow_624 : return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 624, ("Expected: " "mt_listener_mgr_->resume()" " doesn't throw an exception.\n" " Actual: " + gtest_msg.value) .c_str()) = ::testing::Message (); | |||
| 625 | 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" , 625, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 626 | } | |||
| 627 | ||||
| 628 | // Stop the listener and then verify it has stopped. | |||
| 629 | 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" , 629, "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" , 629, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw non-std::exception"; } }; | |||
| 630 | 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" , 630, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 631 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0", mt_listener_mgr_->getThreadCount(), 0))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 631, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 632 | ||||
| 633 | // Iterate over the clients, checking their outcomes. | |||
| 634 | size_t total_responses = 0; | |||
| 635 | for (auto const& client : clients_) { | |||
| 636 | // Client should have completed its receive successfully. | |||
| 637 | 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" , 637, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "client->receiveDone()", "false", "true") .c_str()) = :: testing::Message(); | |||
| 638 | ||||
| 639 | // Now we walk the element tree to get the response data. It should look | |||
| 640 | // this: | |||
| 641 | // | |||
| 642 | // { | |||
| 643 | // "arguments": { "client-ptr": "xxxxx", | |||
| 644 | // "sign-off": "good bye", | |||
| 645 | // "thread-id": "zzzzz" }, | |||
| 646 | // "result": 0 | |||
| 647 | // } | |||
| 648 | // | |||
| 649 | // We expect one response. | |||
| 650 | auto responses = client->getResponses(); | |||
| 651 | ASSERT_EQ(responses.size(), 1)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("responses.size()" , "1", responses.size(), 1))) ; else return ::testing::internal ::AssertHelper(::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 651, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 652 | ||||
| 653 | // First we turn it into an Element tree. | |||
| 654 | ConstElementPtr answer; | |||
| 655 | 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" , 655, "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" , 655, "Failed") = ::testing::Message() << "answer = Element::fromJSON(responses.front())" << " threw non-std::exception"; } }; | |||
| 656 | ||||
| 657 | // Answer should be a map containing "arguments" and "results". | |||
| 658 | 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" , 658, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 659 | ||||
| 660 | // "result" should be 0. | |||
| 661 | ConstElementPtr result = answer->get("result"); | |||
| 662 | 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" , 662, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "result", "false", "true") .c_str()) = ::testing::Message(); | |||
| 663 | 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" , 663, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 664 | 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" , 664, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 665 | ||||
| 666 | // "arguments" is a map containing "client-ptr" and "thread-id". | |||
| 667 | ConstElementPtr arguments = answer->get("arguments"); | |||
| 668 | 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" , 668, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "arguments", "false", "true") .c_str()) = ::testing::Message (); | |||
| 669 | 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" , 669, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 670 | ||||
| 671 | // "client-ptr" is a string. | |||
| 672 | ConstElementPtr client_ptr = arguments->get("client-ptr"); | |||
| 673 | 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" , 673, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "client_ptr", "false", "true") .c_str()) = ::testing::Message (); | |||
| 674 | 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" , 674, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 675 | ||||
| 676 | // "thread-id" is a string. | |||
| 677 | ConstElementPtr thread_id = arguments->get("thread-id"); | |||
| 678 | 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" , 678, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "thread_id", "false", "true") .c_str()) = ::testing::Message (); | |||
| 679 | 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" , 679, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 680 | std::string thread_id_str = thread_id->stringValue(); | |||
| 681 | ||||
| 682 | // Make sure the response received was for this client. | |||
| 683 | std::stringstream ss; | |||
| 684 | ss << client; | |||
| 685 | 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" , 685, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 686 | ||||
| 687 | ++total_responses; | |||
| 688 | } | |||
| 689 | ||||
| 690 | // We should have responses for all our clients. | |||
| 691 | 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" , 691, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 692 | ||||
| 693 | // We should have had the expected number of pauses. | |||
| 694 | if (!num_pauses) { | |||
| 695 | ASSERT_EQ(pause_cnt_, 0)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("pause_cnt_" , "0", pause_cnt_, 0))) ; else return ::testing::internal::AssertHelper (::testing::TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 695, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 696 | } else { | |||
| 697 | // We allow a range on pauses of +-1. | |||
| 698 | 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" , 699, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "(num_pauses - 1) <= pause_cnt_ && (pause_cnt_ <= (num_pauses + 1))" , "false", "true") .c_str()) = ::testing::Message() | |||
| 699 | (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" , 699, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "(num_pauses - 1) <= pause_cnt_ && (pause_cnt_ <= (num_pauses + 1))" , "false", "true") .c_str()) = ::testing::Message() | |||
| 700 | << " num_pauses: " << num_pauses | |||
| 701 | << ", pause_cnt_" << pause_cnt_; | |||
| 702 | } | |||
| 703 | } | |||
| 704 | ||||
| 705 | /// @brief MtTcpListenerMgr instance under test. | |||
| 706 | MtTcpListenerMgrPtr mt_listener_mgr_; | |||
| 707 | ||||
| 708 | /// @brief IO service used in drive the test and test clients. | |||
| 709 | IOServicePtr io_service_; | |||
| 710 | ||||
| 711 | /// @brief Asynchronous timer service to detect timeouts. | |||
| 712 | IntervalTimer test_timer_; | |||
| 713 | ||||
| 714 | /// @brief Asynchronous timer for running IO service for a specified amount | |||
| 715 | /// of time. | |||
| 716 | IntervalTimer run_io_service_timer_; | |||
| 717 | ||||
| 718 | /// @brief List of client connections. | |||
| 719 | std::list<TcpTestClientPtr> clients_; | |||
| 720 | ||||
| 721 | /// @brief Number of threads the listener should use for the test. | |||
| 722 | size_t num_threads_; | |||
| 723 | ||||
| 724 | /// @brief Number of client requests to make during the test. | |||
| 725 | size_t num_clients_; | |||
| 726 | ||||
| 727 | /// @brief Number of requests currently in progress. | |||
| 728 | size_t num_in_progress_; | |||
| 729 | ||||
| 730 | /// @brief Number of requests that have finished. | |||
| 731 | size_t num_finished_; | |||
| 732 | ||||
| 733 | /// @brief Chunk size of requests that need to be processed in parallel. | |||
| 734 | /// | |||
| 735 | /// This can either be the number of threads (if the number of requests is | |||
| 736 | /// greater than the number of threads) or the number of requests (if the | |||
| 737 | /// number of threads is greater than the number of requests). | |||
| 738 | size_t chunk_size_; | |||
| 739 | ||||
| 740 | /// @brief Mutex used to lock during thread coordination. | |||
| 741 | std::mutex mutex_; | |||
| 742 | ||||
| 743 | /// @brief Condition variable used to coordinate threads. | |||
| 744 | std::condition_variable cv_; | |||
| 745 | ||||
| 746 | /// @brief Number of times client has been paused during the test. | |||
| 747 | size_t pause_cnt_; | |||
| 748 | ||||
| 749 | /// @brief Number of clients that have completed their assignment or | |||
| 750 | /// failed | |||
| 751 | size_t clients_done_; | |||
| 752 | ||||
| 753 | /// @brief Response Handler passed down to each connection. | |||
| 754 | TcpTestConnection::ResponseHandler response_handler_; | |||
| 755 | }; | |||
| 756 | ||||
| 757 | /// Verifies the construction, starting, stopping, pausing, resuming, | |||
| 758 | /// and destruction of MtTcpListener. | |||
| 759 | 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" , 759), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 759), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 759), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_basics_Test >); void MtTcpListenerMgrTest_basics_Test::TestBody() { | |||
| 760 | // Make sure multi-threading is off. | |||
| 761 | MultiThreadingMgr::instance().setMode(false); | |||
| 762 | IOAddress address(SERVER_ADDRESS); | |||
| 763 | uint16_t port = SERVER_PORT; | |||
| 764 | ||||
| 765 | // Make sure we can create one. | |||
| 766 | 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" , 770, "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" , 770, "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"; } } | |||
| 767 | 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" , 770, "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" , 770, "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 | 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" , 770, "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" , 770, "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 | 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" , 770, "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" , 770, "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 | 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" , 770, "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" , 770, "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 | ||||
| 772 | 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" , 772, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_", "false", "true") .c_str()) = ::testing:: Message(); | |||
| 773 | ||||
| 774 | // Verify the getters do what we expect. | |||
| 775 | 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" , 775, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 776 | 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" , 776, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 777 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 1)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "1", mt_listener_mgr_->getThreadPoolSize(), 1))) ; 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_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" , 778, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getTlsContext()", "true", "false") .c_str ()) = ::testing::Message(); | |||
| 779 | ||||
| 780 | // It should not have an IOService, should not be listening and | |||
| 781 | // should have no threads. | |||
| 782 | 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" , 782, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "true", "false" ) .c_str()) = ::testing::Message(); | |||
| 783 | 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" , 783, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 784 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0", mt_listener_mgr_->getThreadCount(), 0))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 784, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 785 | ||||
| 786 | // Verify that we cannot start it when multi-threading is disabled. | |||
| 787 | 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" , 787, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "MultiThreadingMgr::instance().getMode()", "true", "false") .c_str()) = ::testing::Message(); | |||
| 788 | 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" , 790, "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" , 790, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 790, "Failed") = ::testing::Message() << "wrong exception type thrown, expected: " << "InvalidOperation"; } } | |||
| 789 | "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" , 790, "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" , 790, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 790, "Failed") = ::testing::Message() << "wrong exception type thrown, expected: " << "InvalidOperation"; } } | |||
| 790 | " 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" , 790, "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" , 790, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 790, "Failed") = ::testing::Message() << "wrong exception type thrown, expected: " << "InvalidOperation"; } }; | |||
| 791 | ||||
| 792 | // It should still not be listening and have no threads. | |||
| 793 | 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" , 793, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 794 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0", mt_listener_mgr_->getThreadCount(), 0))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 794, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 795 | ||||
| 796 | // Enable multi-threading. | |||
| 797 | MultiThreadingMgr::instance().setMode(true); | |||
| 798 | ||||
| 799 | // Make sure we can start it and it's listening with 1 thread. | |||
| 800 | 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" , 800, "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" , 800, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 801 | 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" , 801, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 802 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 1)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "1", mt_listener_mgr_->getThreadCount(), 1))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 802, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 803 | 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" , 803, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 804 | 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" , 804, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "true" , "false") .c_str()) = ::testing::Message(); | |||
| 805 | ||||
| 806 | // Trying to start it again should fail. | |||
| 807 | 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" , 808, "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" , 808, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 808, "Failed") = ::testing::Message() << "wrong exception type thrown, expected: " << "InvalidOperation"; } } | |||
| 808 | "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" , 808, "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" , 808, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 808, "Failed") = ::testing::Message() << "wrong exception type thrown, expected: " << "InvalidOperation"; } }; | |||
| 809 | ||||
| 810 | // Stop it and verify we're no longer listening. | |||
| 811 | 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" , 811, "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" , 811, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw non-std::exception"; } }; | |||
| 812 | 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" , 812, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 813 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0", mt_listener_mgr_->getThreadCount(), 0))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 813, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 814 | 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" , 814, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "true", "false" ) .c_str()) = ::testing::Message(); | |||
| 815 | ||||
| 816 | // Make sure we can call stop again without problems. | |||
| 817 | 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" , 817, "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" , 817, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw non-std::exception"; } }; | |||
| 818 | ||||
| 819 | // We should be able to restart it. | |||
| 820 | 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" , 820, "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" , 820, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 821 | 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" , 821, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 822 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 1)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "1", mt_listener_mgr_->getThreadCount(), 1))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 822, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 823 | 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" , 823, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 824 | 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" , 824, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "true" , "false") .c_str()) = ::testing::Message(); | |||
| 825 | ||||
| 826 | // Destroying it should also stop it. | |||
| 827 | // If the test timeouts we know it didn't! | |||
| 828 | 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" , 828, "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" , 828, "Failed") = ::testing::Message() << "mt_listener_mgr_.reset()" << " threw non-std::exception"; } }; | |||
| 829 | ||||
| 830 | // Verify we can construct with more than one thread. | |||
| 831 | 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" , 835, "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" , 835, "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"; } } | |||
| 832 | 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" , 835, "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" , 835, "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 | 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" , 835, "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" , 835, "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 | 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" , 835, "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" , 835, "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 | 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" , 835, "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" , 835, "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 | ||||
| 837 | 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" , 837, "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" , 837, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 838 | 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" , 838, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 839 | 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" , 839, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 840 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 4)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "4", mt_listener_mgr_->getThreadCount(), 4))) ; 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_->getThreadPoolSize(), 4)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "4", mt_listener_mgr_->getThreadPoolSize(), 4))) ; 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 | 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" , 842, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 843 | 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" , 843, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 844 | 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" , 844, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "true" , "false") .c_str()) = ::testing::Message(); | |||
| 845 | ||||
| 846 | // Verify we can pause it. We should still be listening, threads intact, | |||
| 847 | // IOService stopped, state set to PAUSED. | |||
| 848 | 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" , 848, "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" , 848, "Failed") = ::testing::Message() << "mt_listener_mgr_->pause()" << " threw non-std::exception"; } }; | |||
| 849 | 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" , 849, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isPaused()", "false", "true") .c_str( )) = ::testing::Message(); | |||
| 850 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 4)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "4", mt_listener_mgr_->getThreadCount(), 4))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 850, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 851 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 4)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "4", mt_listener_mgr_->getThreadPoolSize(), 4))) ; 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 | 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" , 852, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 853 | 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" , 853, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "false" , "true") .c_str()) = ::testing::Message(); | |||
| 854 | ||||
| 855 | // Verify we can resume it. | |||
| 856 | 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" , 856, "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" , 856, "Failed") = ::testing::Message() << "mt_listener_mgr_->resume()" << " threw non-std::exception"; } }; | |||
| 857 | 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" , 857, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 858 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 4)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "4", mt_listener_mgr_->getThreadCount(), 4))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 858, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 859 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 4)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "4", mt_listener_mgr_->getThreadPoolSize(), 4))) ; 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 | 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" , 860, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 861 | 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" , 861, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "true" , "false") .c_str()) = ::testing::Message(); | |||
| 862 | ||||
| 863 | // Stop it and verify we're no longer listening. | |||
| 864 | 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" , 864, "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" , 864, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw non-std::exception"; } }; | |||
| 865 | 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" , 865, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 866 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0", mt_listener_mgr_->getThreadCount(), 0))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 866, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 867 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 4)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "4", mt_listener_mgr_->getThreadPoolSize(), 4))) ; 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 | 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" , 868, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "true", "false" ) .c_str()) = ::testing::Message(); | |||
| 869 | 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" , 869, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 870 | } | |||
| 871 | ||||
| 872 | // Now we'll run some permutations of the number of listener threads | |||
| 873 | // and the number of client requests. | |||
| 874 | ||||
| 875 | // One thread, one client. | |||
| 876 | 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" , 876), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 876), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 876), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_oneByOne_Test >); void MtTcpListenerMgrTest_oneByOne_Test::TestBody() { | |||
| 877 | size_t num_threads = 1; | |||
| 878 | size_t num_clients = 1; | |||
| 879 | threadListenAndRespond(num_threads, num_clients); | |||
| 880 | } | |||
| 881 | ||||
| 882 | // One thread, four clients. | |||
| 883 | 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" , 883), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 883), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 883), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_oneByFour_Test >); void MtTcpListenerMgrTest_oneByFour_Test::TestBody() { | |||
| 884 | size_t num_threads = 1; | |||
| 885 | size_t num_clients = 4; | |||
| 886 | threadListenAndRespond(num_threads, num_clients); | |||
| 887 | } | |||
| 888 | ||||
| 889 | // Four threads, one clients. | |||
| 890 | 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" , 890), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 890), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 890), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_fourByOne_Test >); void MtTcpListenerMgrTest_fourByOne_Test::TestBody() { | |||
| 891 | size_t num_threads = 4; | |||
| 892 | size_t num_clients = 1; | |||
| 893 | threadListenAndRespond(num_threads, num_clients); | |||
| 894 | } | |||
| 895 | ||||
| 896 | // Four threads, four clients. | |||
| 897 | 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" , 897), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 897), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 897), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_fourByFour_Test >); void MtTcpListenerMgrTest_fourByFour_Test::TestBody() { | |||
| 898 | size_t num_threads = 4; | |||
| 899 | size_t num_clients = 4; | |||
| 900 | threadListenAndRespond(num_threads, num_clients); | |||
| 901 | } | |||
| 902 | ||||
| 903 | // Four threads, eight clients. | |||
| 904 | 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" , 904), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 904), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 904), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_fourByEight_Test >); void MtTcpListenerMgrTest_fourByEight_Test::TestBody() { | |||
| 905 | size_t num_threads = 4; | |||
| 906 | size_t num_clients = 8; | |||
| 907 | threadListenAndRespond(num_threads, num_clients); | |||
| 908 | } | |||
| 909 | ||||
| 910 | // Six threads, eighteen clients. | |||
| 911 | 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" , 911), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 911), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 911), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_sixByEighteen_Test >); void MtTcpListenerMgrTest_sixByEighteen_Test::TestBody () { | |||
| 912 | size_t num_threads = 6; | |||
| 913 | size_t num_clients = 18; | |||
| 914 | threadListenAndRespond(num_threads, num_clients); | |||
| 915 | } | |||
| 916 | ||||
| 917 | // Pauses and resumes the listener while it is processing | |||
| 918 | // requests. | |||
| 919 | 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" , 919), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 919), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 919), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_pauseAndResume_Test >); void MtTcpListenerMgrTest_pauseAndResume_Test::TestBody () { | |||
| 920 | size_t num_threads = 6; | |||
| 921 | size_t num_clients = 18; | |||
| 922 | size_t num_pauses = 3; | |||
| 923 | workPauseAndResume(num_threads, num_clients, num_pauses); | |||
| 924 | } | |||
| 925 | ||||
| 926 | // Check if a TLS listener can be created. | |||
| 927 | 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" , 927), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 927), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 927), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_tls_Test >); void MtTcpListenerMgrTest_tls_Test::TestBody() { | |||
| 928 | IOAddress address(SERVER_ADDRESS); | |||
| 929 | uint16_t port = SERVER_PORT; | |||
| 930 | TlsContextPtr context; | |||
| 931 | configServer(context); | |||
| 932 | ||||
| 933 | // Make sure we can create the listener. | |||
| 934 | 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" , 940, "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" , 940, "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"; } } | |||
| 935 | 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" , 940, "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" , 940, "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 | 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" , 940, "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" , 940, "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 | 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" , 940, "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" , 940, "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 | 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" , 940, "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" , 940, "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 | 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" , 940, "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" , 940, "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 | ){ 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" , 940, "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" , 940, "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 | ||||
| 942 | 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" , 942, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 943 | 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" , 943, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 944 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 1)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "1", mt_listener_mgr_->getThreadPoolSize(), 1))) ; 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_->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" , 945, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 946 | 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" , 946, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 947 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0", mt_listener_mgr_->getThreadCount(), 0))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 947, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 948 | ||||
| 949 | // Make sure we can start it and it's listening with 1 thread. | |||
| 950 | 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" , 950, "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" , 950, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 951 | 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" , 951, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 952 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 1)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "1", mt_listener_mgr_->getThreadCount(), 1))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 952, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 953 | 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" , 953, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "false", "true" ) .c_str()) = ::testing::Message(); | |||
| 954 | 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" , 954, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()->stopped()", "true" , "false") .c_str()) = ::testing::Message(); | |||
| 955 | ||||
| 956 | // Stop it. | |||
| 957 | 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" , 957, "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" , 957, "Failed") = ::testing::Message() << "mt_listener_mgr_->stop()" << " threw non-std::exception"; } }; | |||
| 958 | 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" , 958, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 959 | EXPECT_EQ(mt_listener_mgr_->getThreadCount(), 0)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadCount()" , "0", mt_listener_mgr_->getThreadCount(), 0))) ; else ::testing ::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure , "../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 959, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 960 | EXPECT_EQ(mt_listener_mgr_->getThreadPoolSize(), 1)switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("mt_listener_mgr_->getThreadPoolSize()" , "1", mt_listener_mgr_->getThreadPoolSize(), 1))) ; 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 | 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" , 961, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->getThreadIOService()", "true", "false" ) .c_str()) = ::testing::Message(); | |||
| 962 | 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" , 962, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isStopped()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 963 | } | |||
| 964 | ||||
| 965 | /// Verifies that idle timeout can be passed down to the internal listener. | |||
| 966 | 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" , 966), (::testing::internal::GetTypeId<MtTcpListenerMgrTest >()), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetSetUpCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 966), ::testing::internal::SuiteApiResolver< MtTcpListenerMgrTest >::GetTearDownCaseOrSuite("../../../src/lib/tcp/tests/mt_tcp_listener_mgr_unittests.cc" , 966), new ::testing::internal::TestFactoryImpl<MtTcpListenerMgrTest_idleTimeout_Test >); void MtTcpListenerMgrTest_idleTimeout_Test::TestBody() { | |||
| 967 | // Create an MtTcpListenerMgr. | |||
| 968 | createMtTcpListenerMgr(1, std::bind(&MtTcpListenerMgrTest::synchronizedCommandHandler, | |||
| 969 | this, ph::_1)); | |||
| 970 | // Verify the default timeout value. | |||
| 971 | 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" , 971, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 972 | ||||
| 973 | // Set a new timeout value. | |||
| 974 | mt_listener_mgr_->setIdleTimeout(200); | |||
| 975 | 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" , 975, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 976 | ||||
| 977 | // Start the listener, which should instantiate the internal listener. | |||
| 978 | 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" , 978, "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" , 978, "Failed") = ::testing::Message() << "mt_listener_mgr_->start()" << " threw non-std::exception"; } }; | |||
| 979 | 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" , 979, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "mt_listener_mgr_->isRunning()", "false", "true") .c_str ()) = ::testing::Message(); | |||
| 980 | ||||
| 981 | // Verify the internal listener's timeout value. | |||
| 982 | auto tcp_listener = mt_listener_mgr_->getTcpListener(); | |||
| 983 | 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" , 983, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_ , "tcp_listener", "false", "true") .c_str()) = ::testing::Message (); | |||
| 984 | 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" , 984, gtest_ar.failure_message()) = ::testing::Message(); | |||
| 985 | } | |||
| 986 | ||||
| 987 | } // 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 | 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-2023 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 | typename constraint< |
| 121 | is_convertible<ExecutionContext&, execution_context&>::value |
| 122 | >::type = 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 | typename constraint< |
| 160 | is_convertible<ExecutionContext&, execution_context&>::value, |
| 161 | defaulted_constraint |
| 162 | >::type = 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 | typename constraint< |
| 206 | is_convertible<ExecutionContext&, execution_context&>::value |
| 207 | >::type = 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 | typename constraint< |
| 251 | is_convertible<ExecutionContext&, execution_context&>::value |
| 252 | >::type = 0) |
| 253 | : basic_socket<Protocol, Executor>(context, protocol, native_socket) |
| 254 | { |
| 255 | } |
| 256 | |
| 257 | #if defined(BOOST_ASIO_HAS_MOVE1) || defined(GENERATING_DOCUMENTATION) |
| 258 | /// Move-construct a basic_stream_socket from another. |
| 259 | /** |
| 260 | * This constructor moves a stream socket from one object to another. |
| 261 | * |
| 262 | * @param other The other basic_stream_socket object from which the move |
| 263 | * will occur. |
| 264 | * |
| 265 | * @note Following the move, the moved-from object is in the same state as if |
| 266 | * constructed using the @c basic_stream_socket(const executor_type&) |
| 267 | * constructor. |
| 268 | */ |
| 269 | basic_stream_socket(basic_stream_socket&& other) BOOST_ASIO_NOEXCEPTnoexcept |
| 270 | : basic_socket<Protocol, Executor>(std::move(other)) |
| 271 | { |
| 272 | } |
| 273 | |
| 274 | /// Move-assign a basic_stream_socket from another. |
| 275 | /** |
| 276 | * This assignment operator moves a stream socket from one object to another. |
| 277 | * |
| 278 | * @param other The other basic_stream_socket object from which the move |
| 279 | * will occur. |
| 280 | * |
| 281 | * @note Following the move, the moved-from object is in the same state as if |
| 282 | * constructed using the @c basic_stream_socket(const executor_type&) |
| 283 | * constructor. |
| 284 | */ |
| 285 | basic_stream_socket& operator=(basic_stream_socket&& other) |
| 286 | { |
| 287 | basic_socket<Protocol, Executor>::operator=(std::move(other)); |
| 288 | return *this; |
| 289 | } |
| 290 | |
| 291 | /// Move-construct a basic_stream_socket from a socket of another protocol |
| 292 | /// type. |
| 293 | /** |
| 294 | * This constructor moves a stream socket from one object to another. |
| 295 | * |
| 296 | * @param other The other basic_stream_socket object from which the move |
| 297 | * will occur. |
| 298 | * |
| 299 | * @note Following the move, the moved-from object is in the same state as if |
| 300 | * constructed using the @c basic_stream_socket(const executor_type&) |
| 301 | * constructor. |
| 302 | */ |
| 303 | template <typename Protocol1, typename Executor1> |
| 304 | basic_stream_socket(basic_stream_socket<Protocol1, Executor1>&& other, |
| 305 | typename constraint< |
| 306 | is_convertible<Protocol1, Protocol>::value |
| 307 | && is_convertible<Executor1, Executor>::value |
| 308 | >::type = 0) |
| 309 | : basic_socket<Protocol, Executor>(std::move(other)) |
| 310 | { |
| 311 | } |
| 312 | |
| 313 | /// Move-assign a basic_stream_socket from a socket of another protocol type. |
| 314 | /** |
| 315 | * This assignment operator moves a stream socket from one object to another. |
| 316 | * |
| 317 | * @param other The other basic_stream_socket object from which the move |
| 318 | * will occur. |
| 319 | * |
| 320 | * @note Following the move, the moved-from object is in the same state as if |
| 321 | * constructed using the @c basic_stream_socket(const executor_type&) |
| 322 | * constructor. |
| 323 | */ |
| 324 | template <typename Protocol1, typename Executor1> |
| 325 | typename constraint< |
| 326 | is_convertible<Protocol1, Protocol>::value |
| 327 | && is_convertible<Executor1, Executor>::value, |
| 328 | basic_stream_socket& |
| 329 | >::type operator=(basic_stream_socket<Protocol1, Executor1>&& other) |
| 330 | { |
| 331 | basic_socket<Protocol, Executor>::operator=(std::move(other)); |
| 332 | return *this; |
| 333 | } |
| 334 | #endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) |
| 335 | |
| 336 | /// Destroys the socket. |
| 337 | /** |
| 338 | * This function destroys the socket, cancelling any outstanding asynchronous |
| 339 | * operations associated with the socket as if by calling @c cancel. |
| 340 | */ |
| 341 | ~basic_stream_socket() |
| 342 | { |
| 343 | } |
| 344 | |
| 345 | /// Send some data on the socket. |
| 346 | /** |
| 347 | * This function is used to send data on the stream socket. The function |
| 348 | * call will block until one or more bytes of the data has been sent |
| 349 | * successfully, or an until error occurs. |
| 350 | * |
| 351 | * @param buffers One or more data buffers to be sent on the socket. |
| 352 | * |
| 353 | * @returns The number of bytes sent. |
| 354 | * |
| 355 | * @throws boost::system::system_error Thrown on failure. |
| 356 | * |
| 357 | * @note The send operation may not transmit all of the data to the peer. |
| 358 | * Consider using the @ref write function if you need to ensure that all data |
| 359 | * is written before the blocking operation completes. |
| 360 | * |
| 361 | * @par Example |
| 362 | * To send a single data buffer use the @ref buffer function as follows: |
| 363 | * @code |
| 364 | * socket.send(boost::asio::buffer(data, size)); |
| 365 | * @endcode |
| 366 | * See the @ref buffer documentation for information on sending multiple |
| 367 | * buffers in one go, and how to use it with arrays, boost::array or |
| 368 | * std::vector. |
| 369 | */ |
| 370 | template <typename ConstBufferSequence> |
| 371 | std::size_t send(const ConstBufferSequence& buffers) |
| 372 | { |
| 373 | boost::system::error_code ec; |
| 374 | std::size_t s = this->impl_.get_service().send( |
| 375 | this->impl_.get_implementation(), buffers, 0, ec); |
| 376 | boost::asio::detail::throw_error(ec, "send"); |
| 377 | return s; |
| 378 | } |
| 379 | |
| 380 | /// Send some data on the socket. |
| 381 | /** |
| 382 | * This function is used to send data on the stream socket. The function |
| 383 | * call will block until one or more bytes of the data has been sent |
| 384 | * successfully, or an until error occurs. |
| 385 | * |
| 386 | * @param buffers One or more data buffers to be sent on the socket. |
| 387 | * |
| 388 | * @param flags Flags specifying how the send call is to be made. |
| 389 | * |
| 390 | * @returns The number of bytes sent. |
| 391 | * |
| 392 | * @throws boost::system::system_error Thrown on failure. |
| 393 | * |
| 394 | * @note The send operation may not transmit all of the data to the peer. |
| 395 | * Consider using the @ref write function if you need to ensure that all data |
| 396 | * is written before the blocking operation completes. |
| 397 | * |
| 398 | * @par Example |
| 399 | * To send a single data buffer use the @ref buffer function as follows: |
| 400 | * @code |
| 401 | * socket.send(boost::asio::buffer(data, size), 0); |
| 402 | * @endcode |
| 403 | * See the @ref buffer documentation for information on sending multiple |
| 404 | * buffers in one go, and how to use it with arrays, boost::array or |
| 405 | * std::vector. |
| 406 | */ |
| 407 | template <typename ConstBufferSequence> |
| 408 | std::size_t send(const ConstBufferSequence& buffers, |
| 409 | socket_base::message_flags flags) |
| 410 | { |
| 411 | boost::system::error_code ec; |
| 412 | std::size_t s = this->impl_.get_service().send( |
| 413 | this->impl_.get_implementation(), buffers, flags, ec); |
| 414 | boost::asio::detail::throw_error(ec, "send"); |
| 415 | return s; |
| 416 | } |
| 417 | |
| 418 | /// Send some data on the socket. |
| 419 | /** |
| 420 | * This function is used to send data on the stream socket. The function |
| 421 | * call will block until one or more bytes of the data has been sent |
| 422 | * successfully, or an until error occurs. |
| 423 | * |
| 424 | * @param buffers One or more data buffers to be sent on the socket. |
| 425 | * |
| 426 | * @param flags Flags specifying how the send call is to be made. |
| 427 | * |
| 428 | * @param ec Set to indicate what error occurred, if any. |
| 429 | * |
| 430 | * @returns The number of bytes sent. Returns 0 if an error occurred. |
| 431 | * |
| 432 | * @note The send operation may not transmit all of the data to the peer. |
| 433 | * Consider using the @ref write function if you need to ensure that all data |
| 434 | * is written before the blocking operation completes. |
| 435 | */ |
| 436 | template <typename ConstBufferSequence> |
| 437 | std::size_t send(const ConstBufferSequence& buffers, |
| 438 | socket_base::message_flags flags, boost::system::error_code& ec) |
| 439 | { |
| 440 | return this->impl_.get_service().send( |
| 441 | this->impl_.get_implementation(), buffers, flags, ec); |
| 442 | } |
| 443 | |
| 444 | /// Start an asynchronous send. |
| 445 | /** |
| 446 | * This function is used to asynchronously send data on the stream socket. |
| 447 | * It is an initiating function for an @ref asynchronous_operation, and always |
| 448 | * returns immediately. |
| 449 | * |
| 450 | * @param buffers One or more data buffers to be sent on the socket. Although |
| 451 | * the buffers object may be copied as necessary, ownership of the underlying |
| 452 | * memory blocks is retained by the caller, which must guarantee that they |
| 453 | * remain valid until the completion handler is called. |
| 454 | * |
| 455 | * @param token The @ref completion_token that will be used to produce a |
| 456 | * completion handler, which will be called when the send completes. |
| 457 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 458 | * @ref yield_context, or a function object with the correct completion |
| 459 | * signature. The function signature of the completion handler must be: |
| 460 | * @code void handler( |
| 461 | * const boost::system::error_code& error, // Result of operation. |
| 462 | * std::size_t bytes_transferred // Number of bytes sent. |
| 463 | * ); @endcode |
| 464 | * Regardless of whether the asynchronous operation completes immediately or |
| 465 | * not, the completion handler will not be invoked from within this function. |
| 466 | * On immediate completion, invocation of the handler will be performed in a |
| 467 | * manner equivalent to using boost::asio::post(). |
| 468 | * |
| 469 | * @par Completion Signature |
| 470 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 471 | * |
| 472 | * @note The send operation may not transmit all of the data to the peer. |
| 473 | * Consider using the @ref async_write function if you need to ensure that all |
| 474 | * data is written before the asynchronous operation completes. |
| 475 | * |
| 476 | * @par Example |
| 477 | * To send a single data buffer use the @ref buffer function as follows: |
| 478 | * @code |
| 479 | * socket.async_send(boost::asio::buffer(data, size), handler); |
| 480 | * @endcode |
| 481 | * See the @ref buffer documentation for information on sending multiple |
| 482 | * buffers in one go, and how to use it with arrays, boost::array or |
| 483 | * std::vector. |
| 484 | * |
| 485 | * @par Per-Operation Cancellation |
| 486 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 487 | * cancellation for the following boost::asio::cancellation_type values: |
| 488 | * |
| 489 | * @li @c cancellation_type::terminal |
| 490 | * |
| 491 | * @li @c cancellation_type::partial |
| 492 | * |
| 493 | * @li @c cancellation_type::total |
| 494 | */ |
| 495 | template <typename ConstBufferSequence, |
| 496 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 497 | std::size_t))typename WriteToken |
| 498 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type> |
| 499 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(WriteToken,auto |
| 500 | void (boost::system::error_code, std::size_t))auto |
| 501 | async_send(const ConstBufferSequence& buffers, |
| 502 | BOOST_ASIO_MOVE_ARG(WriteToken)WriteToken&& token |
| 503 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type()) |
| 504 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( |
| 505 | async_initiate<WriteToken, |
| 506 | void (boost::system::error_code, std::size_t)>( |
| 507 | declval<initiate_async_send>(), token, |
| 508 | buffers, socket_base::message_flags(0)))) |
| 509 | { |
| 510 | return async_initiate<WriteToken, |
| 511 | void (boost::system::error_code, std::size_t)>( |
| 512 | initiate_async_send(this), token, |
| 513 | buffers, socket_base::message_flags(0)); |
| 514 | } |
| 515 | |
| 516 | /// Start an asynchronous send. |
| 517 | /** |
| 518 | * This function is used to asynchronously send data on the stream socket. |
| 519 | * It is an initiating function for an @ref asynchronous_operation, and always |
| 520 | * returns immediately. |
| 521 | * |
| 522 | * @param buffers One or more data buffers to be sent on the socket. Although |
| 523 | * the buffers object may be copied as necessary, ownership of the underlying |
| 524 | * memory blocks is retained by the caller, which must guarantee that they |
| 525 | * remain valid until the completion handler is called. |
| 526 | * |
| 527 | * @param flags Flags specifying how the send call is to be made. |
| 528 | * |
| 529 | * @param token The @ref completion_token that will be used to produce a |
| 530 | * completion handler, which will be called when the send completes. |
| 531 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 532 | * @ref yield_context, or a function object with the correct completion |
| 533 | * signature. The function signature of the completion handler must be: |
| 534 | * @code void handler( |
| 535 | * const boost::system::error_code& error, // Result of operation. |
| 536 | * std::size_t bytes_transferred // Number of bytes sent. |
| 537 | * ); @endcode |
| 538 | * Regardless of whether the asynchronous operation completes immediately or |
| 539 | * not, the completion handler will not be invoked from within this function. |
| 540 | * On immediate completion, invocation of the handler will be performed in a |
| 541 | * manner equivalent to using boost::asio::post(). |
| 542 | * |
| 543 | * @par Completion Signature |
| 544 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 545 | * |
| 546 | * @note The send operation may not transmit all of the data to the peer. |
| 547 | * Consider using the @ref async_write function if you need to ensure that all |
| 548 | * data is written before the asynchronous operation completes. |
| 549 | * |
| 550 | * @par Example |
| 551 | * To send a single data buffer use the @ref buffer function as follows: |
| 552 | * @code |
| 553 | * socket.async_send(boost::asio::buffer(data, size), 0, handler); |
| 554 | * @endcode |
| 555 | * See the @ref buffer documentation for information on sending multiple |
| 556 | * buffers in one go, and how to use it with arrays, boost::array or |
| 557 | * std::vector. |
| 558 | * |
| 559 | * @par Per-Operation Cancellation |
| 560 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 561 | * cancellation for the following boost::asio::cancellation_type values: |
| 562 | * |
| 563 | * @li @c cancellation_type::terminal |
| 564 | * |
| 565 | * @li @c cancellation_type::partial |
| 566 | * |
| 567 | * @li @c cancellation_type::total |
| 568 | */ |
| 569 | template <typename ConstBufferSequence, |
| 570 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 571 | std::size_t))typename WriteToken |
| 572 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type> |
| 573 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(WriteToken,auto |
| 574 | void (boost::system::error_code, std::size_t))auto |
| 575 | async_send(const ConstBufferSequence& buffers, |
| 576 | socket_base::message_flags flags, |
| 577 | BOOST_ASIO_MOVE_ARG(WriteToken)WriteToken&& token |
| 578 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type()) |
| 579 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( |
| 580 | async_initiate<WriteToken, |
| 581 | void (boost::system::error_code, std::size_t)>( |
| 582 | declval<initiate_async_send>(), token, buffers, flags))) |
| 583 | { |
| 584 | return async_initiate<WriteToken, |
| 585 | void (boost::system::error_code, std::size_t)>( |
| 586 | initiate_async_send(this), token, buffers, flags); |
| 587 | } |
| 588 | |
| 589 | /// Receive some data on the socket. |
| 590 | /** |
| 591 | * This function is used to receive data on the stream socket. The function |
| 592 | * call will block until one or more bytes of data has been received |
| 593 | * successfully, or until an error occurs. |
| 594 | * |
| 595 | * @param buffers One or more buffers into which the data will be received. |
| 596 | * |
| 597 | * @returns The number of bytes received. |
| 598 | * |
| 599 | * @throws boost::system::system_error Thrown on failure. An error code of |
| 600 | * boost::asio::error::eof indicates that the connection was closed by the |
| 601 | * peer. |
| 602 | * |
| 603 | * @note The receive operation may not receive all of the requested number of |
| 604 | * bytes. Consider using the @ref read function if you need to ensure that the |
| 605 | * requested amount of data is read before the blocking operation completes. |
| 606 | * |
| 607 | * @par Example |
| 608 | * To receive into a single data buffer use the @ref buffer function as |
| 609 | * follows: |
| 610 | * @code |
| 611 | * socket.receive(boost::asio::buffer(data, size)); |
| 612 | * @endcode |
| 613 | * See the @ref buffer documentation for information on receiving into |
| 614 | * multiple buffers in one go, and how to use it with arrays, boost::array or |
| 615 | * std::vector. |
| 616 | */ |
| 617 | template <typename MutableBufferSequence> |
| 618 | std::size_t receive(const MutableBufferSequence& buffers) |
| 619 | { |
| 620 | boost::system::error_code ec; |
| 621 | std::size_t s = this->impl_.get_service().receive( |
| 622 | this->impl_.get_implementation(), buffers, 0, ec); |
| 623 | boost::asio::detail::throw_error(ec, "receive"); |
| 624 | return s; |
| 625 | } |
| 626 | |
| 627 | /// Receive some data on the socket. |
| 628 | /** |
| 629 | * This function is used to receive data on the stream socket. The function |
| 630 | * call will block until one or more bytes of data has been received |
| 631 | * successfully, or until an error occurs. |
| 632 | * |
| 633 | * @param buffers One or more buffers into which the data will be received. |
| 634 | * |
| 635 | * @param flags Flags specifying how the receive call is to be made. |
| 636 | * |
| 637 | * @returns The number of bytes received. |
| 638 | * |
| 639 | * @throws boost::system::system_error Thrown on failure. An error code of |
| 640 | * boost::asio::error::eof indicates that the connection was closed by the |
| 641 | * peer. |
| 642 | * |
| 643 | * @note The receive operation may not receive all of the requested number of |
| 644 | * bytes. Consider using the @ref read function if you need to ensure that the |
| 645 | * requested amount of data is read before the blocking operation completes. |
| 646 | * |
| 647 | * @par Example |
| 648 | * To receive into a single data buffer use the @ref buffer function as |
| 649 | * follows: |
| 650 | * @code |
| 651 | * socket.receive(boost::asio::buffer(data, size), 0); |
| 652 | * @endcode |
| 653 | * See the @ref buffer documentation for information on receiving into |
| 654 | * multiple buffers in one go, and how to use it with arrays, boost::array or |
| 655 | * std::vector. |
| 656 | */ |
| 657 | template <typename MutableBufferSequence> |
| 658 | std::size_t receive(const MutableBufferSequence& buffers, |
| 659 | socket_base::message_flags flags) |
| 660 | { |
| 661 | boost::system::error_code ec; |
| 662 | std::size_t s = this->impl_.get_service().receive( |
| 663 | this->impl_.get_implementation(), buffers, flags, ec); |
| 664 | boost::asio::detail::throw_error(ec, "receive"); |
| 665 | return s; |
| 666 | } |
| 667 | |
| 668 | /// Receive some data on a connected socket. |
| 669 | /** |
| 670 | * This function is used to receive data on the stream socket. The function |
| 671 | * call will block until one or more bytes of data has been received |
| 672 | * successfully, or until an error occurs. |
| 673 | * |
| 674 | * @param buffers One or more buffers into which the data will be received. |
| 675 | * |
| 676 | * @param flags Flags specifying how the receive call is to be made. |
| 677 | * |
| 678 | * @param ec Set to indicate what error occurred, if any. |
| 679 | * |
| 680 | * @returns The number of bytes received. Returns 0 if an error occurred. |
| 681 | * |
| 682 | * @note The receive operation may not receive all of the requested number of |
| 683 | * bytes. Consider using the @ref read function if you need to ensure that the |
| 684 | * requested amount of data is read before the blocking operation completes. |
| 685 | */ |
| 686 | template <typename MutableBufferSequence> |
| 687 | std::size_t receive(const MutableBufferSequence& buffers, |
| 688 | socket_base::message_flags flags, boost::system::error_code& ec) |
| 689 | { |
| 690 | return this->impl_.get_service().receive( |
| 691 | this->impl_.get_implementation(), buffers, flags, ec); |
| 692 | } |
| 693 | |
| 694 | /// Start an asynchronous receive. |
| 695 | /** |
| 696 | * This function is used to asynchronously receive data from the stream |
| 697 | * socket. It is an initiating function for an @ref asynchronous_operation, |
| 698 | * and always returns immediately. |
| 699 | * |
| 700 | * @param buffers One or more buffers into which the data will be received. |
| 701 | * Although the buffers object may be copied as necessary, ownership of the |
| 702 | * underlying memory blocks is retained by the caller, which must guarantee |
| 703 | * that they remain valid until the completion handler is called. |
| 704 | * |
| 705 | * @param token The @ref completion_token that will be used to produce a |
| 706 | * completion handler, which will be called when the receive completes. |
| 707 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 708 | * @ref yield_context, or a function object with the correct completion |
| 709 | * signature. The function signature of the completion handler must be: |
| 710 | * @code void handler( |
| 711 | * const boost::system::error_code& error, // Result of operation. |
| 712 | * std::size_t bytes_transferred // Number of bytes received. |
| 713 | * ); @endcode |
| 714 | * Regardless of whether the asynchronous operation completes immediately or |
| 715 | * not, the completion handler will not be invoked from within this function. |
| 716 | * On immediate completion, invocation of the handler will be performed in a |
| 717 | * manner equivalent to using boost::asio::post(). |
| 718 | * |
| 719 | * @par Completion Signature |
| 720 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 721 | * |
| 722 | * @note The receive operation may not receive all of the requested number of |
| 723 | * bytes. Consider using the @ref async_read function if you need to ensure |
| 724 | * that the requested amount of data is received before the asynchronous |
| 725 | * operation completes. |
| 726 | * |
| 727 | * @par Example |
| 728 | * To receive into a single data buffer use the @ref buffer function as |
| 729 | * follows: |
| 730 | * @code |
| 731 | * socket.async_receive(boost::asio::buffer(data, size), handler); |
| 732 | * @endcode |
| 733 | * See the @ref buffer documentation for information on receiving into |
| 734 | * multiple buffers in one go, and how to use it with arrays, boost::array or |
| 735 | * std::vector. |
| 736 | * |
| 737 | * @par Per-Operation Cancellation |
| 738 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 739 | * cancellation for the following boost::asio::cancellation_type values: |
| 740 | * |
| 741 | * @li @c cancellation_type::terminal |
| 742 | * |
| 743 | * @li @c cancellation_type::partial |
| 744 | * |
| 745 | * @li @c cancellation_type::total |
| 746 | */ |
| 747 | template <typename MutableBufferSequence, |
| 748 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 749 | std::size_t))typename ReadToken |
| 750 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type> |
| 751 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(ReadToken,auto |
| 752 | void (boost::system::error_code, std::size_t))auto |
| 753 | async_receive(const MutableBufferSequence& buffers, |
| 754 | BOOST_ASIO_MOVE_ARG(ReadToken)ReadToken&& token |
| 755 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type()) |
| 756 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( |
| 757 | async_initiate<ReadToken, |
| 758 | void (boost::system::error_code, std::size_t)>( |
| 759 | declval<initiate_async_receive>(), token, |
| 760 | buffers, socket_base::message_flags(0)))) |
| 761 | { |
| 762 | return async_initiate<ReadToken, |
| 763 | void (boost::system::error_code, std::size_t)>( |
| 764 | initiate_async_receive(this), token, |
| 765 | buffers, socket_base::message_flags(0)); |
| 766 | } |
| 767 | |
| 768 | /// Start an asynchronous receive. |
| 769 | /** |
| 770 | * This function is used to asynchronously receive data from the stream |
| 771 | * socket. It is an initiating function for an @ref asynchronous_operation, |
| 772 | * and always returns immediately. |
| 773 | * |
| 774 | * @param buffers One or more buffers into which the data will be received. |
| 775 | * Although the buffers object may be copied as necessary, ownership of the |
| 776 | * underlying memory blocks is retained by the caller, which must guarantee |
| 777 | * that they remain valid until the completion handler is called. |
| 778 | * |
| 779 | * @param flags Flags specifying how the receive call is to be made. |
| 780 | * |
| 781 | * @param token The @ref completion_token that will be used to produce a |
| 782 | * completion handler, which will be called when the receive completes. |
| 783 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 784 | * @ref yield_context, or a function object with the correct completion |
| 785 | * signature. The function signature of the completion handler must be: |
| 786 | * @code void handler( |
| 787 | * const boost::system::error_code& error, // Result of operation. |
| 788 | * std::size_t bytes_transferred // Number of bytes received. |
| 789 | * ); @endcode |
| 790 | * Regardless of whether the asynchronous operation completes immediately or |
| 791 | * not, the completion handler will not be invoked from within this function. |
| 792 | * On immediate completion, invocation of the handler will be performed in a |
| 793 | * manner equivalent to using boost::asio::post(). |
| 794 | * |
| 795 | * @par Completion Signature |
| 796 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 797 | * |
| 798 | * @note The receive operation may not receive all of the requested number of |
| 799 | * bytes. Consider using the @ref async_read function if you need to ensure |
| 800 | * that the requested amount of data is received before the asynchronous |
| 801 | * operation completes. |
| 802 | * |
| 803 | * @par Example |
| 804 | * To receive into a single data buffer use the @ref buffer function as |
| 805 | * follows: |
| 806 | * @code |
| 807 | * socket.async_receive(boost::asio::buffer(data, size), 0, handler); |
| 808 | * @endcode |
| 809 | * See the @ref buffer documentation for information on receiving into |
| 810 | * multiple buffers in one go, and how to use it with arrays, boost::array or |
| 811 | * std::vector. |
| 812 | * |
| 813 | * @par Per-Operation Cancellation |
| 814 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 815 | * cancellation for the following boost::asio::cancellation_type values: |
| 816 | * |
| 817 | * @li @c cancellation_type::terminal |
| 818 | * |
| 819 | * @li @c cancellation_type::partial |
| 820 | * |
| 821 | * @li @c cancellation_type::total |
| 822 | */ |
| 823 | template <typename MutableBufferSequence, |
| 824 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 825 | std::size_t))typename ReadToken |
| 826 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type> |
| 827 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(ReadToken,auto |
| 828 | void (boost::system::error_code, std::size_t))auto |
| 829 | async_receive(const MutableBufferSequence& buffers, |
| 830 | socket_base::message_flags flags, |
| 831 | BOOST_ASIO_MOVE_ARG(ReadToken)ReadToken&& token |
| 832 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type()) |
| 833 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( |
| 834 | async_initiate<ReadToken, |
| 835 | void (boost::system::error_code, std::size_t)>( |
| 836 | declval<initiate_async_receive>(), token, buffers, flags))) |
| 837 | { |
| 838 | return async_initiate<ReadToken, |
| 839 | void (boost::system::error_code, std::size_t)>( |
| 840 | initiate_async_receive(this), token, buffers, flags); |
| 841 | } |
| 842 | |
| 843 | /// Write some data to the socket. |
| 844 | /** |
| 845 | * This function is used to write data to the stream socket. The function call |
| 846 | * will block until one or more bytes of the data has been written |
| 847 | * successfully, or until an error occurs. |
| 848 | * |
| 849 | * @param buffers One or more data buffers to be written to the socket. |
| 850 | * |
| 851 | * @returns The number of bytes written. |
| 852 | * |
| 853 | * @throws boost::system::system_error Thrown on failure. An error code of |
| 854 | * boost::asio::error::eof indicates that the connection was closed by the |
| 855 | * peer. |
| 856 | * |
| 857 | * @note The write_some operation may not transmit all of the data to the |
| 858 | * peer. Consider using the @ref write function if you need to ensure that |
| 859 | * all data is written before the blocking operation completes. |
| 860 | * |
| 861 | * @par Example |
| 862 | * To write a single data buffer use the @ref buffer function as follows: |
| 863 | * @code |
| 864 | * socket.write_some(boost::asio::buffer(data, size)); |
| 865 | * @endcode |
| 866 | * See the @ref buffer documentation for information on writing multiple |
| 867 | * buffers in one go, and how to use it with arrays, boost::array or |
| 868 | * std::vector. |
| 869 | */ |
| 870 | template <typename ConstBufferSequence> |
| 871 | std::size_t write_some(const ConstBufferSequence& buffers) |
| 872 | { |
| 873 | boost::system::error_code ec; |
| 874 | std::size_t s = this->impl_.get_service().send( |
| 875 | this->impl_.get_implementation(), buffers, 0, ec); |
| 876 | boost::asio::detail::throw_error(ec, "write_some"); |
| 877 | return s; |
| 878 | } |
| 879 | |
| 880 | /// Write some data to the socket. |
| 881 | /** |
| 882 | * This function is used to write data to the stream socket. The function call |
| 883 | * will block until one or more bytes of the data has been written |
| 884 | * successfully, or until an error occurs. |
| 885 | * |
| 886 | * @param buffers One or more data buffers to be written to the socket. |
| 887 | * |
| 888 | * @param ec Set to indicate what error occurred, if any. |
| 889 | * |
| 890 | * @returns The number of bytes written. Returns 0 if an error occurred. |
| 891 | * |
| 892 | * @note The write_some operation may not transmit all of the data to the |
| 893 | * peer. Consider using the @ref write function if you need to ensure that |
| 894 | * all data is written before the blocking operation completes. |
| 895 | */ |
| 896 | template <typename ConstBufferSequence> |
| 897 | std::size_t write_some(const ConstBufferSequence& buffers, |
| 898 | boost::system::error_code& ec) |
| 899 | { |
| 900 | return this->impl_.get_service().send( |
| 901 | this->impl_.get_implementation(), buffers, 0, ec); |
| 902 | } |
| 903 | |
| 904 | /// Start an asynchronous write. |
| 905 | /** |
| 906 | * This function is used to asynchronously write data to the stream socket. |
| 907 | * It is an initiating function for an @ref asynchronous_operation, and always |
| 908 | * returns immediately. |
| 909 | * |
| 910 | * @param buffers One or more data buffers to be written to the socket. |
| 911 | * Although the buffers object may be copied as necessary, ownership of the |
| 912 | * underlying memory blocks is retained by the caller, which must guarantee |
| 913 | * that they remain valid until the completion handler is called. |
| 914 | * |
| 915 | * @param token The @ref completion_token that will be used to produce a |
| 916 | * completion handler, which will be called when the write completes. |
| 917 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 918 | * @ref yield_context, or a function object with the correct completion |
| 919 | * signature. The function signature of the completion handler must be: |
| 920 | * @code void handler( |
| 921 | * const boost::system::error_code& error, // Result of operation. |
| 922 | * std::size_t bytes_transferred // Number of bytes written. |
| 923 | * ); @endcode |
| 924 | * Regardless of whether the asynchronous operation completes immediately or |
| 925 | * not, the completion handler will not be invoked from within this function. |
| 926 | * On immediate completion, invocation of the handler will be performed in a |
| 927 | * manner equivalent to using boost::asio::post(). |
| 928 | * |
| 929 | * @par Completion Signature |
| 930 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 931 | * |
| 932 | * @note The write operation may not transmit all of the data to the peer. |
| 933 | * Consider using the @ref async_write function if you need to ensure that all |
| 934 | * data is written before the asynchronous operation completes. |
| 935 | * |
| 936 | * @par Example |
| 937 | * To write a single data buffer use the @ref buffer function as follows: |
| 938 | * @code |
| 939 | * socket.async_write_some(boost::asio::buffer(data, size), handler); |
| 940 | * @endcode |
| 941 | * See the @ref buffer documentation for information on writing multiple |
| 942 | * buffers in one go, and how to use it with arrays, boost::array or |
| 943 | * std::vector. |
| 944 | * |
| 945 | * @par Per-Operation Cancellation |
| 946 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 947 | * cancellation for the following boost::asio::cancellation_type values: |
| 948 | * |
| 949 | * @li @c cancellation_type::terminal |
| 950 | * |
| 951 | * @li @c cancellation_type::partial |
| 952 | * |
| 953 | * @li @c cancellation_type::total |
| 954 | */ |
| 955 | template <typename ConstBufferSequence, |
| 956 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 957 | std::size_t))typename WriteToken |
| 958 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type> |
| 959 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(WriteToken,auto |
| 960 | void (boost::system::error_code, std::size_t))auto |
| 961 | async_write_some(const ConstBufferSequence& buffers, |
| 962 | BOOST_ASIO_MOVE_ARG(WriteToken)WriteToken&& token |
| 963 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type()) |
| 964 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( |
| 965 | async_initiate<WriteToken, |
| 966 | void (boost::system::error_code, std::size_t)>( |
| 967 | declval<initiate_async_send>(), token, |
| 968 | buffers, socket_base::message_flags(0)))) |
| 969 | { |
| 970 | return async_initiate<WriteToken, |
| 971 | void (boost::system::error_code, std::size_t)>( |
| 972 | initiate_async_send(this), token, |
| 973 | buffers, socket_base::message_flags(0)); |
| 974 | } |
| 975 | |
| 976 | /// Read some data from the socket. |
| 977 | /** |
| 978 | * This function is used to read data from the stream socket. The function |
| 979 | * call will block until one or more bytes of data has been read successfully, |
| 980 | * or until an error occurs. |
| 981 | * |
| 982 | * @param buffers One or more buffers into which the data will be read. |
| 983 | * |
| 984 | * @returns The number of bytes read. |
| 985 | * |
| 986 | * @throws boost::system::system_error Thrown on failure. An error code of |
| 987 | * boost::asio::error::eof indicates that the connection was closed by the |
| 988 | * peer. |
| 989 | * |
| 990 | * @note The read_some operation may not read all of the requested number of |
| 991 | * bytes. Consider using the @ref read function if you need to ensure that |
| 992 | * the requested amount of data is read before the blocking operation |
| 993 | * completes. |
| 994 | * |
| 995 | * @par Example |
| 996 | * To read into a single data buffer use the @ref buffer function as follows: |
| 997 | * @code |
| 998 | * socket.read_some(boost::asio::buffer(data, size)); |
| 999 | * @endcode |
| 1000 | * See the @ref buffer documentation for information on reading into multiple |
| 1001 | * buffers in one go, and how to use it with arrays, boost::array or |
| 1002 | * std::vector. |
| 1003 | */ |
| 1004 | template <typename MutableBufferSequence> |
| 1005 | std::size_t read_some(const MutableBufferSequence& buffers) |
| 1006 | { |
| 1007 | boost::system::error_code ec; |
| 1008 | std::size_t s = this->impl_.get_service().receive( |
| 1009 | this->impl_.get_implementation(), buffers, 0, ec); |
| 1010 | boost::asio::detail::throw_error(ec, "read_some"); |
| 1011 | return s; |
| 1012 | } |
| 1013 | |
| 1014 | /// Read some data from the socket. |
| 1015 | /** |
| 1016 | * This function is used to read data from the stream socket. The function |
| 1017 | * call will block until one or more bytes of data has been read successfully, |
| 1018 | * or until an error occurs. |
| 1019 | * |
| 1020 | * @param buffers One or more buffers into which the data will be read. |
| 1021 | * |
| 1022 | * @param ec Set to indicate what error occurred, if any. |
| 1023 | * |
| 1024 | * @returns The number of bytes read. Returns 0 if an error occurred. |
| 1025 | * |
| 1026 | * @note The read_some operation may not read all of the requested number of |
| 1027 | * bytes. Consider using the @ref read function if you need to ensure that |
| 1028 | * the requested amount of data is read before the blocking operation |
| 1029 | * completes. |
| 1030 | */ |
| 1031 | template <typename MutableBufferSequence> |
| 1032 | std::size_t read_some(const MutableBufferSequence& buffers, |
| 1033 | boost::system::error_code& ec) |
| 1034 | { |
| 1035 | return this->impl_.get_service().receive( |
| 1036 | this->impl_.get_implementation(), buffers, 0, ec); |
| 1037 | } |
| 1038 | |
| 1039 | /// Start an asynchronous read. |
| 1040 | /** |
| 1041 | * This function is used to asynchronously read data from the stream socket. |
| 1042 | * socket. It is an initiating function for an @ref asynchronous_operation, |
| 1043 | * and always returns immediately. |
| 1044 | * |
| 1045 | * @param buffers One or more buffers into which the data will be read. |
| 1046 | * Although the buffers object may be copied as necessary, ownership of the |
| 1047 | * underlying memory blocks is retained by the caller, which must guarantee |
| 1048 | * that they remain valid until the completion handler is called. |
| 1049 | * |
| 1050 | * @param token The @ref completion_token that will be used to produce a |
| 1051 | * completion handler, which will be called when the read completes. |
| 1052 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 1053 | * @ref yield_context, or a function object with the correct completion |
| 1054 | * signature. The function signature of the completion handler must be: |
| 1055 | * @code void handler( |
| 1056 | * const boost::system::error_code& error, // Result of operation. |
| 1057 | * std::size_t bytes_transferred // Number of bytes read. |
| 1058 | * ); @endcode |
| 1059 | * Regardless of whether the asynchronous operation completes immediately or |
| 1060 | * not, the completion handler will not be invoked from within this function. |
| 1061 | * On immediate completion, invocation of the handler will be performed in a |
| 1062 | * manner equivalent to using boost::asio::post(). |
| 1063 | * |
| 1064 | * @par Completion Signature |
| 1065 | * @code void(boost::system::error_code, std::size_t) @endcode |
| 1066 | * |
| 1067 | * @note The read operation may not read all of the requested number of bytes. |
| 1068 | * Consider using the @ref async_read function if you need to ensure that the |
| 1069 | * requested amount of data is read before the asynchronous operation |
| 1070 | * completes. |
| 1071 | * |
| 1072 | * @par Example |
| 1073 | * To read into a single data buffer use the @ref buffer function as follows: |
| 1074 | * @code |
| 1075 | * socket.async_read_some(boost::asio::buffer(data, size), handler); |
| 1076 | * @endcode |
| 1077 | * See the @ref buffer documentation for information on reading into multiple |
| 1078 | * buffers in one go, and how to use it with arrays, boost::array or |
| 1079 | * std::vector. |
| 1080 | * |
| 1081 | * @par Per-Operation Cancellation |
| 1082 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 1083 | * cancellation for the following boost::asio::cancellation_type values: |
| 1084 | * |
| 1085 | * @li @c cancellation_type::terminal |
| 1086 | * |
| 1087 | * @li @c cancellation_type::partial |
| 1088 | * |
| 1089 | * @li @c cancellation_type::total |
| 1090 | */ |
| 1091 | template <typename MutableBufferSequence, |
| 1092 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,typename |
| 1093 | std::size_t))typename ReadToken |
| 1094 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type> |
| 1095 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(ReadToken,auto |
| 1096 | void (boost::system::error_code, std::size_t))auto |
| 1097 | async_read_some(const MutableBufferSequence& buffers, |
| 1098 | BOOST_ASIO_MOVE_ARG(ReadToken)ReadToken&& token |
| 1099 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type()) |
| 1100 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( |
| 1101 | async_initiate<ReadToken, |
| 1102 | void (boost::system::error_code, std::size_t)>( |
| 1103 | declval<initiate_async_receive>(), token, |
| 1104 | buffers, socket_base::message_flags(0)))) |
| 1105 | { |
| 1106 | return async_initiate<ReadToken, |
| 1107 | void (boost::system::error_code, std::size_t)>( |
| 1108 | initiate_async_receive(this), token, |
| 1109 | buffers, socket_base::message_flags(0)); |
| 1110 | } |
| 1111 | |
| 1112 | private: |
| 1113 | // Disallow copying and assignment. |
| 1114 | basic_stream_socket(const basic_stream_socket&) BOOST_ASIO_DELETED= delete; |
| 1115 | basic_stream_socket& operator=(const basic_stream_socket&) BOOST_ASIO_DELETED= delete; |
| 1116 | |
| 1117 | class initiate_async_send |
| 1118 | { |
| 1119 | public: |
| 1120 | typedef Executor executor_type; |
| 1121 | |
| 1122 | explicit initiate_async_send(basic_stream_socket* self) |
| 1123 | : self_(self) |
| 1124 | { |
| 1125 | } |
| 1126 | |
| 1127 | const executor_type& get_executor() const BOOST_ASIO_NOEXCEPTnoexcept |
| 1128 | { |
| 1129 | return self_->get_executor(); |
| 1130 | } |
| 1131 | |
| 1132 | template <typename WriteHandler, typename ConstBufferSequence> |
| 1133 | void operator()(BOOST_ASIO_MOVE_ARG(WriteHandler)WriteHandler&& handler, |
| 1134 | const ConstBufferSequence& buffers, |
| 1135 | socket_base::message_flags flags) const |
| 1136 | { |
| 1137 | // If you get an error on the following line it means that your handler |
| 1138 | // does not meet the documented type requirements for a WriteHandler. |
| 1139 | 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; |
| 1140 | |
| 1141 | detail::non_const_lvalue<WriteHandler> handler2(handler); |
| 1142 | self_->impl_.get_service().async_send( |
| 1143 | self_->impl_.get_implementation(), buffers, flags, |
| 1144 | handler2.value, self_->impl_.get_executor()); |
| 1145 | } |
| 1146 | |
| 1147 | private: |
| 1148 | basic_stream_socket* self_; |
| 1149 | }; |
| 1150 | |
| 1151 | class initiate_async_receive |
| 1152 | { |
| 1153 | public: |
| 1154 | typedef Executor executor_type; |
| 1155 | |
| 1156 | explicit initiate_async_receive(basic_stream_socket* self) |
| 1157 | : self_(self) |
| 1158 | { |
| 1159 | } |
| 1160 | |
| 1161 | const executor_type& get_executor() const BOOST_ASIO_NOEXCEPTnoexcept |
| 1162 | { |
| 1163 | return self_->get_executor(); |
| 1164 | } |
| 1165 | |
| 1166 | template <typename ReadHandler, typename MutableBufferSequence> |
| 1167 | void operator()(BOOST_ASIO_MOVE_ARG(ReadHandler)ReadHandler&& handler, |
| 1168 | const MutableBufferSequence& buffers, |
| 1169 | socket_base::message_flags flags) const |
| 1170 | { |
| 1171 | // If you get an error on the following line it means that your handler |
| 1172 | // does not meet the documented type requirements for a ReadHandler. |
| 1173 | 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; |
| 1174 | |
| 1175 | detail::non_const_lvalue<ReadHandler> handler2(handler); |
| 1176 | self_->impl_.get_service().async_receive( |
| 1177 | self_->impl_.get_implementation(), buffers, flags, |
| 1178 | handler2.value, self_->impl_.get_executor()); |
| 1179 | } |
| 1180 | |
| 1181 | private: |
| 1182 | basic_stream_socket* self_; |
| 1183 | }; |
| 1184 | }; |
| 1185 | |
| 1186 | } // namespace asio |
| 1187 | } // namespace boost |
| 1188 | |
| 1189 | #include <boost/asio/detail/pop_options.hpp> |
| 1190 | |
| 1191 | #endif // BOOST_ASIO_BASIC_STREAM_SOCKET_HPP |
| 1 | // |
| 2 | // basic_socket.hpp |
| 3 | // ~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2023 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 <boost/asio/any_io_executor.hpp> |
| 19 | #include <boost/asio/detail/config.hpp> |
| 20 | #include <boost/asio/async_result.hpp> |
| 21 | #include <boost/asio/detail/handler_type_requirements.hpp> |
| 22 | #include <boost/asio/detail/io_object_impl.hpp> |
| 23 | #include <boost/asio/detail/non_const_lvalue.hpp> |
| 24 | #include <boost/asio/detail/throw_error.hpp> |
| 25 | #include <boost/asio/detail/type_traits.hpp> |
| 26 | #include <boost/asio/error.hpp> |
| 27 | #include <boost/asio/execution_context.hpp> |
| 28 | #include <boost/asio/post.hpp> |
| 29 | #include <boost/asio/socket_base.hpp> |
| 30 | |
| 31 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) |
| 32 | # include <boost/asio/detail/null_socket_service.hpp> |
| 33 | #elif defined(BOOST_ASIO_HAS_IOCP) |
| 34 | # include <boost/asio/detail/win_iocp_socket_service.hpp> |
| 35 | #elif defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 36 | # include <boost/asio/detail/io_uring_socket_service.hpp> |
| 37 | #else |
| 38 | # include <boost/asio/detail/reactive_socket_service.hpp> |
| 39 | #endif |
| 40 | |
| 41 | #if defined(BOOST_ASIO_HAS_MOVE1) |
| 42 | # include <utility> |
| 43 | #endif // defined(BOOST_ASIO_HAS_MOVE) |
| 44 | |
| 45 | #include <boost/asio/detail/push_options.hpp> |
| 46 | |
| 47 | namespace boost { |
| 48 | namespace asio { |
| 49 | |
| 50 | #if !defined(BOOST_ASIO_BASIC_SOCKET_FWD_DECL) |
| 51 | #define BOOST_ASIO_BASIC_SOCKET_FWD_DECL |
| 52 | |
| 53 | // Forward declaration with defaulted arguments. |
| 54 | template <typename Protocol, typename Executor = any_io_executor> |
| 55 | class basic_socket; |
| 56 | |
| 57 | #endif // !defined(BOOST_ASIO_BASIC_SOCKET_FWD_DECL) |
| 58 | |
| 59 | /// Provides socket functionality. |
| 60 | /** |
| 61 | * The basic_socket class template provides functionality that is common to both |
| 62 | * stream-oriented and datagram-oriented sockets. |
| 63 | * |
| 64 | * @par Thread Safety |
| 65 | * @e Distinct @e objects: Safe.@n |
| 66 | * @e Shared @e objects: Unsafe. |
| 67 | */ |
| 68 | template <typename Protocol, typename Executor> |
| 69 | class basic_socket |
| 70 | : public socket_base |
| 71 | { |
| 72 | private: |
| 73 | class initiate_async_connect; |
| 74 | class initiate_async_wait; |
| 75 | |
| 76 | public: |
| 77 | /// The type of the executor associated with the object. |
| 78 | typedef Executor executor_type; |
| 79 | |
| 80 | /// Rebinds the socket type to another executor. |
| 81 | template <typename Executor1> |
| 82 | struct rebind_executor |
| 83 | { |
| 84 | /// The socket type when rebound to the specified executor. |
| 85 | typedef basic_socket<Protocol, Executor1> other; |
| 86 | }; |
| 87 | |
| 88 | /// The native representation of a socket. |
| 89 | #if defined(GENERATING_DOCUMENTATION) |
| 90 | typedef implementation_defined native_handle_type; |
| 91 | #elif defined(BOOST_ASIO_WINDOWS_RUNTIME) |
| 92 | typedef typename detail::null_socket_service< |
| 93 | Protocol>::native_handle_type native_handle_type; |
| 94 | #elif defined(BOOST_ASIO_HAS_IOCP) |
| 95 | typedef typename detail::win_iocp_socket_service< |
| 96 | Protocol>::native_handle_type native_handle_type; |
| 97 | #elif defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 98 | typedef typename detail::io_uring_socket_service< |
| 99 | Protocol>::native_handle_type native_handle_type; |
| 100 | #else |
| 101 | typedef typename detail::reactive_socket_service< |
| 102 | Protocol>::native_handle_type native_handle_type; |
| 103 | #endif |
| 104 | |
| 105 | /// The protocol type. |
| 106 | typedef Protocol protocol_type; |
| 107 | |
| 108 | /// The endpoint type. |
| 109 | typedef typename Protocol::endpoint endpoint_type; |
| 110 | |
| 111 | #if !defined(BOOST_ASIO_NO_EXTENSIONS) |
| 112 | /// A basic_socket is always the lowest layer. |
| 113 | typedef basic_socket<Protocol, Executor> lowest_layer_type; |
| 114 | #endif // !defined(BOOST_ASIO_NO_EXTENSIONS) |
| 115 | |
| 116 | /// Construct a basic_socket without opening it. |
| 117 | /** |
| 118 | * This constructor creates a socket without opening it. |
| 119 | * |
| 120 | * @param ex The I/O executor that the socket will use, by default, to |
| 121 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 122 | */ |
| 123 | explicit basic_socket(const executor_type& ex) |
| 124 | : impl_(0, ex) |
| 125 | { |
| 126 | } |
| 127 | |
| 128 | /// Construct a basic_socket without opening it. |
| 129 | /** |
| 130 | * This constructor creates a socket without opening it. |
| 131 | * |
| 132 | * @param context An execution context which provides the I/O executor that |
| 133 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 134 | * operations performed on the socket. |
| 135 | */ |
| 136 | template <typename ExecutionContext> |
| 137 | explicit basic_socket(ExecutionContext& context, |
| 138 | typename constraint< |
| 139 | is_convertible<ExecutionContext&, execution_context&>::value |
| 140 | >::type = 0) |
| 141 | : impl_(0, 0, context) |
| 142 | { |
| 143 | } |
| 144 | |
| 145 | /// Construct and open a basic_socket. |
| 146 | /** |
| 147 | * This constructor creates and opens a socket. |
| 148 | * |
| 149 | * @param ex The I/O executor that the socket will use, by default, to |
| 150 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 151 | * |
| 152 | * @param protocol An object specifying protocol parameters to be used. |
| 153 | * |
| 154 | * @throws boost::system::system_error Thrown on failure. |
| 155 | */ |
| 156 | basic_socket(const executor_type& ex, const protocol_type& protocol) |
| 157 | : impl_(0, ex) |
| 158 | { |
| 159 | boost::system::error_code ec; |
| 160 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 161 | boost::asio::detail::throw_error(ec, "open"); |
| 162 | } |
| 163 | |
| 164 | /// Construct and open a basic_socket. |
| 165 | /** |
| 166 | * This constructor creates and opens a socket. |
| 167 | * |
| 168 | * @param context An execution context which provides the I/O executor that |
| 169 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 170 | * operations performed on the socket. |
| 171 | * |
| 172 | * @param protocol An object specifying protocol parameters to be used. |
| 173 | * |
| 174 | * @throws boost::system::system_error Thrown on failure. |
| 175 | */ |
| 176 | template <typename ExecutionContext> |
| 177 | basic_socket(ExecutionContext& context, const protocol_type& protocol, |
| 178 | typename constraint< |
| 179 | is_convertible<ExecutionContext&, execution_context&>::value, |
| 180 | defaulted_constraint |
| 181 | >::type = defaulted_constraint()) |
| 182 | : impl_(0, 0, context) |
| 183 | { |
| 184 | boost::system::error_code ec; |
| 185 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 186 | boost::asio::detail::throw_error(ec, "open"); |
| 187 | } |
| 188 | |
| 189 | /// Construct a basic_socket, opening it and binding it to the given local |
| 190 | /// endpoint. |
| 191 | /** |
| 192 | * This constructor creates a socket and automatically opens it bound to the |
| 193 | * specified endpoint on the local machine. The protocol used is the protocol |
| 194 | * associated with the given endpoint. |
| 195 | * |
| 196 | * @param ex The I/O executor that the socket will use, by default, to |
| 197 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 198 | * |
| 199 | * @param endpoint An endpoint on the local machine to which the socket will |
| 200 | * be bound. |
| 201 | * |
| 202 | * @throws boost::system::system_error Thrown on failure. |
| 203 | */ |
| 204 | basic_socket(const executor_type& ex, const endpoint_type& endpoint) |
| 205 | : impl_(0, ex) |
| 206 | { |
| 207 | boost::system::error_code ec; |
| 208 | const protocol_type protocol = endpoint.protocol(); |
| 209 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 210 | boost::asio::detail::throw_error(ec, "open"); |
| 211 | impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); |
| 212 | boost::asio::detail::throw_error(ec, "bind"); |
| 213 | } |
| 214 | |
| 215 | /// Construct a basic_socket, opening it and binding it to the given local |
| 216 | /// endpoint. |
| 217 | /** |
| 218 | * This constructor creates a socket and automatically opens it bound to the |
| 219 | * specified endpoint on the local machine. The protocol used is the protocol |
| 220 | * associated with the given endpoint. |
| 221 | * |
| 222 | * @param context An execution context which provides the I/O executor that |
| 223 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 224 | * operations performed on the socket. |
| 225 | * |
| 226 | * @param endpoint An endpoint on the local machine to which the socket will |
| 227 | * be bound. |
| 228 | * |
| 229 | * @throws boost::system::system_error Thrown on failure. |
| 230 | */ |
| 231 | template <typename ExecutionContext> |
| 232 | basic_socket(ExecutionContext& context, const endpoint_type& endpoint, |
| 233 | typename constraint< |
| 234 | is_convertible<ExecutionContext&, execution_context&>::value |
| 235 | >::type = 0) |
| 236 | : impl_(0, 0, context) |
| 237 | { |
| 238 | boost::system::error_code ec; |
| 239 | const protocol_type protocol = endpoint.protocol(); |
| 240 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 241 | boost::asio::detail::throw_error(ec, "open"); |
| 242 | impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); |
| 243 | boost::asio::detail::throw_error(ec, "bind"); |
| 244 | } |
| 245 | |
| 246 | /// Construct a basic_socket on an existing native socket. |
| 247 | /** |
| 248 | * This constructor creates a socket object to hold an existing native socket. |
| 249 | * |
| 250 | * @param ex The I/O executor that the socket will use, by default, to |
| 251 | * dispatch handlers for any asynchronous operations performed on the socket. |
| 252 | * |
| 253 | * @param protocol An object specifying protocol parameters to be used. |
| 254 | * |
| 255 | * @param native_socket A native socket. |
| 256 | * |
| 257 | * @throws boost::system::system_error Thrown on failure. |
| 258 | */ |
| 259 | basic_socket(const executor_type& ex, const protocol_type& protocol, |
| 260 | const native_handle_type& native_socket) |
| 261 | : impl_(0, ex) |
| 262 | { |
| 263 | boost::system::error_code ec; |
| 264 | impl_.get_service().assign(impl_.get_implementation(), |
| 265 | protocol, native_socket, ec); |
| 266 | boost::asio::detail::throw_error(ec, "assign"); |
| 267 | } |
| 268 | |
| 269 | /// Construct a basic_socket on an existing native socket. |
| 270 | /** |
| 271 | * This constructor creates a socket object to hold an existing native socket. |
| 272 | * |
| 273 | * @param context An execution context which provides the I/O executor that |
| 274 | * the socket will use, by default, to dispatch handlers for any asynchronous |
| 275 | * operations performed on the socket. |
| 276 | * |
| 277 | * @param protocol An object specifying protocol parameters to be used. |
| 278 | * |
| 279 | * @param native_socket A native socket. |
| 280 | * |
| 281 | * @throws boost::system::system_error Thrown on failure. |
| 282 | */ |
| 283 | template <typename ExecutionContext> |
| 284 | basic_socket(ExecutionContext& context, const protocol_type& protocol, |
| 285 | const native_handle_type& native_socket, |
| 286 | typename constraint< |
| 287 | is_convertible<ExecutionContext&, execution_context&>::value |
| 288 | >::type = 0) |
| 289 | : impl_(0, 0, context) |
| 290 | { |
| 291 | boost::system::error_code ec; |
| 292 | impl_.get_service().assign(impl_.get_implementation(), |
| 293 | protocol, native_socket, ec); |
| 294 | boost::asio::detail::throw_error(ec, "assign"); |
| 295 | } |
| 296 | |
| 297 | #if defined(BOOST_ASIO_HAS_MOVE1) || defined(GENERATING_DOCUMENTATION) |
| 298 | /// Move-construct a basic_socket from another. |
| 299 | /** |
| 300 | * This constructor moves a socket from one object to another. |
| 301 | * |
| 302 | * @param other The other basic_socket object from which the move will |
| 303 | * occur. |
| 304 | * |
| 305 | * @note Following the move, the moved-from object is in the same state as if |
| 306 | * constructed using the @c basic_socket(const executor_type&) constructor. |
| 307 | */ |
| 308 | basic_socket(basic_socket&& other) BOOST_ASIO_NOEXCEPTnoexcept |
| 309 | : impl_(std::move(other.impl_)) |
| 310 | { |
| 311 | } |
| 312 | |
| 313 | /// Move-assign a basic_socket from another. |
| 314 | /** |
| 315 | * This assignment operator moves a socket from one object to another. |
| 316 | * |
| 317 | * @param other The other basic_socket object from which the move will |
| 318 | * occur. |
| 319 | * |
| 320 | * @note Following the move, the moved-from object is in the same state as if |
| 321 | * constructed using the @c basic_socket(const executor_type&) constructor. |
| 322 | */ |
| 323 | basic_socket& operator=(basic_socket&& other) |
| 324 | { |
| 325 | impl_ = std::move(other.impl_); |
| 326 | return *this; |
| 327 | } |
| 328 | |
| 329 | // All sockets have access to each other's implementations. |
| 330 | template <typename Protocol1, typename Executor1> |
| 331 | friend class basic_socket; |
| 332 | |
| 333 | /// Move-construct a basic_socket from a socket of another protocol type. |
| 334 | /** |
| 335 | * This constructor moves a socket from one object to another. |
| 336 | * |
| 337 | * @param other The other basic_socket object from which the move will |
| 338 | * occur. |
| 339 | * |
| 340 | * @note Following the move, the moved-from object is in the same state as if |
| 341 | * constructed using the @c basic_socket(const executor_type&) constructor. |
| 342 | */ |
| 343 | template <typename Protocol1, typename Executor1> |
| 344 | basic_socket(basic_socket<Protocol1, Executor1>&& other, |
| 345 | typename constraint< |
| 346 | is_convertible<Protocol1, Protocol>::value |
| 347 | && is_convertible<Executor1, Executor>::value |
| 348 | >::type = 0) |
| 349 | : impl_(std::move(other.impl_)) |
| 350 | { |
| 351 | } |
| 352 | |
| 353 | /// Move-assign a basic_socket from a socket of another protocol type. |
| 354 | /** |
| 355 | * This assignment operator moves a socket from one object to another. |
| 356 | * |
| 357 | * @param other The other basic_socket object from which the move will |
| 358 | * occur. |
| 359 | * |
| 360 | * @note Following the move, the moved-from object is in the same state as if |
| 361 | * constructed using the @c basic_socket(const executor_type&) constructor. |
| 362 | */ |
| 363 | template <typename Protocol1, typename Executor1> |
| 364 | typename constraint< |
| 365 | is_convertible<Protocol1, Protocol>::value |
| 366 | && is_convertible<Executor1, Executor>::value, |
| 367 | basic_socket& |
| 368 | >::type operator=(basic_socket<Protocol1, Executor1>&& other) |
| 369 | { |
| 370 | basic_socket tmp(std::move(other)); |
| 371 | impl_ = std::move(tmp.impl_); |
| 372 | return *this; |
| 373 | } |
| 374 | #endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) |
| 375 | |
| 376 | /// Get the executor associated with the object. |
| 377 | const executor_type& get_executor() BOOST_ASIO_NOEXCEPTnoexcept |
| 378 | { |
| 379 | return impl_.get_executor(); |
| 380 | } |
| 381 | |
| 382 | #if !defined(BOOST_ASIO_NO_EXTENSIONS) |
| 383 | /// Get a reference to the lowest layer. |
| 384 | /** |
| 385 | * This function returns a reference to the lowest layer in a stack of |
| 386 | * layers. Since a basic_socket cannot contain any further layers, it simply |
| 387 | * returns a reference to itself. |
| 388 | * |
| 389 | * @return A reference to the lowest layer in the stack of layers. Ownership |
| 390 | * is not transferred to the caller. |
| 391 | */ |
| 392 | lowest_layer_type& lowest_layer() |
| 393 | { |
| 394 | return *this; |
| 395 | } |
| 396 | |
| 397 | /// Get a const reference to the lowest layer. |
| 398 | /** |
| 399 | * This function returns a const reference to the lowest layer in a stack of |
| 400 | * layers. Since a basic_socket cannot contain any further layers, it simply |
| 401 | * returns a reference to itself. |
| 402 | * |
| 403 | * @return A const reference to the lowest layer in the stack of layers. |
| 404 | * Ownership is not transferred to the caller. |
| 405 | */ |
| 406 | const lowest_layer_type& lowest_layer() const |
| 407 | { |
| 408 | return *this; |
| 409 | } |
| 410 | #endif // !defined(BOOST_ASIO_NO_EXTENSIONS) |
| 411 | |
| 412 | /// Open the socket using the specified protocol. |
| 413 | /** |
| 414 | * This function opens the socket so that it will use the specified protocol. |
| 415 | * |
| 416 | * @param protocol An object specifying protocol parameters to be used. |
| 417 | * |
| 418 | * @throws boost::system::system_error Thrown on failure. |
| 419 | * |
| 420 | * @par Example |
| 421 | * @code |
| 422 | * boost::asio::ip::tcp::socket socket(my_context); |
| 423 | * socket.open(boost::asio::ip::tcp::v4()); |
| 424 | * @endcode |
| 425 | */ |
| 426 | void open(const protocol_type& protocol = protocol_type()) |
| 427 | { |
| 428 | boost::system::error_code ec; |
| 429 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 430 | boost::asio::detail::throw_error(ec, "open"); |
| 431 | } |
| 432 | |
| 433 | /// Open the socket using the specified protocol. |
| 434 | /** |
| 435 | * This function opens the socket so that it will use the specified protocol. |
| 436 | * |
| 437 | * @param protocol An object specifying which protocol is to be used. |
| 438 | * |
| 439 | * @param ec Set to indicate what error occurred, if any. |
| 440 | * |
| 441 | * @par Example |
| 442 | * @code |
| 443 | * boost::asio::ip::tcp::socket socket(my_context); |
| 444 | * boost::system::error_code ec; |
| 445 | * socket.open(boost::asio::ip::tcp::v4(), ec); |
| 446 | * if (ec) |
| 447 | * { |
| 448 | * // An error occurred. |
| 449 | * } |
| 450 | * @endcode |
| 451 | */ |
| 452 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code open(const protocol_type& protocol, |
| 453 | boost::system::error_code& ec) |
| 454 | { |
| 455 | impl_.get_service().open(impl_.get_implementation(), protocol, ec); |
| 456 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 457 | } |
| 458 | |
| 459 | /// Assign an existing native socket to the socket. |
| 460 | /* |
| 461 | * This function opens the socket to hold an existing native socket. |
| 462 | * |
| 463 | * @param protocol An object specifying which protocol is to be used. |
| 464 | * |
| 465 | * @param native_socket A native socket. |
| 466 | * |
| 467 | * @throws boost::system::system_error Thrown on failure. |
| 468 | */ |
| 469 | void assign(const protocol_type& protocol, |
| 470 | const native_handle_type& native_socket) |
| 471 | { |
| 472 | boost::system::error_code ec; |
| 473 | impl_.get_service().assign(impl_.get_implementation(), |
| 474 | protocol, native_socket, ec); |
| 475 | boost::asio::detail::throw_error(ec, "assign"); |
| 476 | } |
| 477 | |
| 478 | /// Assign an existing native socket to the socket. |
| 479 | /* |
| 480 | * This function opens the socket to hold an existing native socket. |
| 481 | * |
| 482 | * @param protocol An object specifying which protocol is to be used. |
| 483 | * |
| 484 | * @param native_socket A native socket. |
| 485 | * |
| 486 | * @param ec Set to indicate what error occurred, if any. |
| 487 | */ |
| 488 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code assign(const protocol_type& protocol, |
| 489 | const native_handle_type& native_socket, boost::system::error_code& ec) |
| 490 | { |
| 491 | impl_.get_service().assign(impl_.get_implementation(), |
| 492 | protocol, native_socket, ec); |
| 493 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 494 | } |
| 495 | |
| 496 | /// Determine whether the socket is open. |
| 497 | bool is_open() const |
| 498 | { |
| 499 | return impl_.get_service().is_open(impl_.get_implementation()); |
| 500 | } |
| 501 | |
| 502 | /// Close the socket. |
| 503 | /** |
| 504 | * This function is used to close the socket. Any asynchronous send, receive |
| 505 | * or connect operations will be cancelled immediately, and will complete |
| 506 | * with the boost::asio::error::operation_aborted error. |
| 507 | * |
| 508 | * @throws boost::system::system_error Thrown on failure. Note that, even if |
| 509 | * the function indicates an error, the underlying descriptor is closed. |
| 510 | * |
| 511 | * @note For portable behaviour with respect to graceful closure of a |
| 512 | * connected socket, call shutdown() before closing the socket. |
| 513 | */ |
| 514 | void close() |
| 515 | { |
| 516 | boost::system::error_code ec; |
| 517 | impl_.get_service().close(impl_.get_implementation(), ec); |
| 518 | boost::asio::detail::throw_error(ec, "close"); |
| 519 | } |
| 520 | |
| 521 | /// Close the socket. |
| 522 | /** |
| 523 | * This function is used to close the socket. Any asynchronous send, receive |
| 524 | * or connect operations will be cancelled immediately, and will complete |
| 525 | * with the boost::asio::error::operation_aborted error. |
| 526 | * |
| 527 | * @param ec Set to indicate what error occurred, if any. Note that, even if |
| 528 | * the function indicates an error, the underlying descriptor is closed. |
| 529 | * |
| 530 | * @par Example |
| 531 | * @code |
| 532 | * boost::asio::ip::tcp::socket socket(my_context); |
| 533 | * ... |
| 534 | * boost::system::error_code ec; |
| 535 | * socket.close(ec); |
| 536 | * if (ec) |
| 537 | * { |
| 538 | * // An error occurred. |
| 539 | * } |
| 540 | * @endcode |
| 541 | * |
| 542 | * @note For portable behaviour with respect to graceful closure of a |
| 543 | * connected socket, call shutdown() before closing the socket. |
| 544 | */ |
| 545 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code close(boost::system::error_code& ec) |
| 546 | { |
| 547 | impl_.get_service().close(impl_.get_implementation(), ec); |
| 548 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 549 | } |
| 550 | |
| 551 | /// Release ownership of the underlying native socket. |
| 552 | /** |
| 553 | * This function causes all outstanding asynchronous connect, send and receive |
| 554 | * operations to finish immediately, and the handlers for cancelled operations |
| 555 | * will be passed the boost::asio::error::operation_aborted error. Ownership |
| 556 | * of the native socket is then transferred to the caller. |
| 557 | * |
| 558 | * @throws boost::system::system_error Thrown on failure. |
| 559 | * |
| 560 | * @note This function is unsupported on Windows versions prior to Windows |
| 561 | * 8.1, and will fail with boost::asio::error::operation_not_supported on |
| 562 | * these platforms. |
| 563 | */ |
| 564 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ |
| 565 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) |
| 566 | __declspec(deprecated("This function always fails with " |
| 567 | "operation_not_supported when used on Windows versions " |
| 568 | "prior to Windows 8.1.")) |
| 569 | #endif |
| 570 | native_handle_type release() |
| 571 | { |
| 572 | boost::system::error_code ec; |
| 573 | native_handle_type s = impl_.get_service().release( |
| 574 | impl_.get_implementation(), ec); |
| 575 | boost::asio::detail::throw_error(ec, "release"); |
| 576 | return s; |
| 577 | } |
| 578 | |
| 579 | /// Release ownership of the underlying native socket. |
| 580 | /** |
| 581 | * This function causes all outstanding asynchronous connect, send and receive |
| 582 | * operations to finish immediately, and the handlers for cancelled operations |
| 583 | * will be passed the boost::asio::error::operation_aborted error. Ownership |
| 584 | * of the native socket is then transferred to the caller. |
| 585 | * |
| 586 | * @param ec Set to indicate what error occurred, if any. |
| 587 | * |
| 588 | * @note This function is unsupported on Windows versions prior to Windows |
| 589 | * 8.1, and will fail with boost::asio::error::operation_not_supported on |
| 590 | * these platforms. |
| 591 | */ |
| 592 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ |
| 593 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) |
| 594 | __declspec(deprecated("This function always fails with " |
| 595 | "operation_not_supported when used on Windows versions " |
| 596 | "prior to Windows 8.1.")) |
| 597 | #endif |
| 598 | native_handle_type release(boost::system::error_code& ec) |
| 599 | { |
| 600 | return impl_.get_service().release(impl_.get_implementation(), ec); |
| 601 | } |
| 602 | |
| 603 | /// Get the native socket representation. |
| 604 | /** |
| 605 | * This function may be used to obtain the underlying representation of the |
| 606 | * socket. This is intended to allow access to native socket functionality |
| 607 | * that is not otherwise provided. |
| 608 | */ |
| 609 | native_handle_type native_handle() |
| 610 | { |
| 611 | return impl_.get_service().native_handle(impl_.get_implementation()); |
| 612 | } |
| 613 | |
| 614 | /// Cancel all asynchronous operations associated with the socket. |
| 615 | /** |
| 616 | * This function causes all outstanding asynchronous connect, send and receive |
| 617 | * operations to finish immediately, and the handlers for cancelled operations |
| 618 | * will be passed the boost::asio::error::operation_aborted error. |
| 619 | * |
| 620 | * @throws boost::system::system_error Thrown on failure. |
| 621 | * |
| 622 | * @note Calls to cancel() will always fail with |
| 623 | * boost::asio::error::operation_not_supported when run on Windows XP, Windows |
| 624 | * Server 2003, and earlier versions of Windows, unless |
| 625 | * BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has |
| 626 | * two issues that should be considered before enabling its use: |
| 627 | * |
| 628 | * @li It will only cancel asynchronous operations that were initiated in the |
| 629 | * current thread. |
| 630 | * |
| 631 | * @li It can appear to complete without error, but the request to cancel the |
| 632 | * unfinished operations may be silently ignored by the operating system. |
| 633 | * Whether it works or not seems to depend on the drivers that are installed. |
| 634 | * |
| 635 | * For portable cancellation, consider using one of the following |
| 636 | * alternatives: |
| 637 | * |
| 638 | * @li Disable asio's I/O completion port backend by defining |
| 639 | * BOOST_ASIO_DISABLE_IOCP. |
| 640 | * |
| 641 | * @li Use the close() function to simultaneously cancel the outstanding |
| 642 | * operations and close the socket. |
| 643 | * |
| 644 | * When running on Windows Vista, Windows Server 2008, and later, the |
| 645 | * CancelIoEx function is always used. This function does not have the |
| 646 | * problems described above. |
| 647 | */ |
| 648 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ |
| 649 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ |
| 650 | && !defined(BOOST_ASIO_ENABLE_CANCELIO) |
| 651 | __declspec(deprecated("By default, this function always fails with " |
| 652 | "operation_not_supported when used on Windows XP, Windows Server 2003, " |
| 653 | "or earlier. Consult documentation for details.")) |
| 654 | #endif |
| 655 | void cancel() |
| 656 | { |
| 657 | boost::system::error_code ec; |
| 658 | impl_.get_service().cancel(impl_.get_implementation(), ec); |
| 659 | boost::asio::detail::throw_error(ec, "cancel"); |
| 660 | } |
| 661 | |
| 662 | /// Cancel all asynchronous operations associated with the socket. |
| 663 | /** |
| 664 | * This function causes all outstanding asynchronous connect, send and receive |
| 665 | * operations to finish immediately, and the handlers for cancelled operations |
| 666 | * will be passed the boost::asio::error::operation_aborted error. |
| 667 | * |
| 668 | * @param ec Set to indicate what error occurred, if any. |
| 669 | * |
| 670 | * @note Calls to cancel() will always fail with |
| 671 | * boost::asio::error::operation_not_supported when run on Windows XP, Windows |
| 672 | * Server 2003, and earlier versions of Windows, unless |
| 673 | * BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has |
| 674 | * two issues that should be considered before enabling its use: |
| 675 | * |
| 676 | * @li It will only cancel asynchronous operations that were initiated in the |
| 677 | * current thread. |
| 678 | * |
| 679 | * @li It can appear to complete without error, but the request to cancel the |
| 680 | * unfinished operations may be silently ignored by the operating system. |
| 681 | * Whether it works or not seems to depend on the drivers that are installed. |
| 682 | * |
| 683 | * For portable cancellation, consider using one of the following |
| 684 | * alternatives: |
| 685 | * |
| 686 | * @li Disable asio's I/O completion port backend by defining |
| 687 | * BOOST_ASIO_DISABLE_IOCP. |
| 688 | * |
| 689 | * @li Use the close() function to simultaneously cancel the outstanding |
| 690 | * operations and close the socket. |
| 691 | * |
| 692 | * When running on Windows Vista, Windows Server 2008, and later, the |
| 693 | * CancelIoEx function is always used. This function does not have the |
| 694 | * problems described above. |
| 695 | */ |
| 696 | #if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \ |
| 697 | && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600) \ |
| 698 | && !defined(BOOST_ASIO_ENABLE_CANCELIO) |
| 699 | __declspec(deprecated("By default, this function always fails with " |
| 700 | "operation_not_supported when used on Windows XP, Windows Server 2003, " |
| 701 | "or earlier. Consult documentation for details.")) |
| 702 | #endif |
| 703 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code cancel(boost::system::error_code& ec) |
| 704 | { |
| 705 | impl_.get_service().cancel(impl_.get_implementation(), ec); |
| 706 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 707 | } |
| 708 | |
| 709 | /// Determine whether the socket is at the out-of-band data mark. |
| 710 | /** |
| 711 | * This function is used to check whether the socket input is currently |
| 712 | * positioned at the out-of-band data mark. |
| 713 | * |
| 714 | * @return A bool indicating whether the socket is at the out-of-band data |
| 715 | * mark. |
| 716 | * |
| 717 | * @throws boost::system::system_error Thrown on failure. |
| 718 | */ |
| 719 | bool at_mark() const |
| 720 | { |
| 721 | boost::system::error_code ec; |
| 722 | bool b = impl_.get_service().at_mark(impl_.get_implementation(), ec); |
| 723 | boost::asio::detail::throw_error(ec, "at_mark"); |
| 724 | return b; |
| 725 | } |
| 726 | |
| 727 | /// Determine whether the socket is at the out-of-band data mark. |
| 728 | /** |
| 729 | * This function is used to check whether the socket input is currently |
| 730 | * positioned at the out-of-band data mark. |
| 731 | * |
| 732 | * @param ec Set to indicate what error occurred, if any. |
| 733 | * |
| 734 | * @return A bool indicating whether the socket is at the out-of-band data |
| 735 | * mark. |
| 736 | */ |
| 737 | bool at_mark(boost::system::error_code& ec) const |
| 738 | { |
| 739 | return impl_.get_service().at_mark(impl_.get_implementation(), ec); |
| 740 | } |
| 741 | |
| 742 | /// Determine the number of bytes available for reading. |
| 743 | /** |
| 744 | * This function is used to determine the number of bytes that may be read |
| 745 | * without blocking. |
| 746 | * |
| 747 | * @return The number of bytes that may be read without blocking, or 0 if an |
| 748 | * error occurs. |
| 749 | * |
| 750 | * @throws boost::system::system_error Thrown on failure. |
| 751 | */ |
| 752 | std::size_t available() const |
| 753 | { |
| 754 | boost::system::error_code ec; |
| 755 | std::size_t s = impl_.get_service().available( |
| 756 | impl_.get_implementation(), ec); |
| 757 | boost::asio::detail::throw_error(ec, "available"); |
| 758 | return s; |
| 759 | } |
| 760 | |
| 761 | /// Determine the number of bytes available for reading. |
| 762 | /** |
| 763 | * This function is used to determine the number of bytes that may be read |
| 764 | * without blocking. |
| 765 | * |
| 766 | * @param ec Set to indicate what error occurred, if any. |
| 767 | * |
| 768 | * @return The number of bytes that may be read without blocking, or 0 if an |
| 769 | * error occurs. |
| 770 | */ |
| 771 | std::size_t available(boost::system::error_code& ec) const |
| 772 | { |
| 773 | return impl_.get_service().available(impl_.get_implementation(), ec); |
| 774 | } |
| 775 | |
| 776 | /// Bind the socket to the given local endpoint. |
| 777 | /** |
| 778 | * This function binds the socket to the specified endpoint on the local |
| 779 | * machine. |
| 780 | * |
| 781 | * @param endpoint An endpoint on the local machine to which the socket will |
| 782 | * be bound. |
| 783 | * |
| 784 | * @throws boost::system::system_error Thrown on failure. |
| 785 | * |
| 786 | * @par Example |
| 787 | * @code |
| 788 | * boost::asio::ip::tcp::socket socket(my_context); |
| 789 | * socket.open(boost::asio::ip::tcp::v4()); |
| 790 | * socket.bind(boost::asio::ip::tcp::endpoint( |
| 791 | * boost::asio::ip::tcp::v4(), 12345)); |
| 792 | * @endcode |
| 793 | */ |
| 794 | void bind(const endpoint_type& endpoint) |
| 795 | { |
| 796 | boost::system::error_code ec; |
| 797 | impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); |
| 798 | boost::asio::detail::throw_error(ec, "bind"); |
| 799 | } |
| 800 | |
| 801 | /// Bind the socket to the given local endpoint. |
| 802 | /** |
| 803 | * This function binds the socket to the specified endpoint on the local |
| 804 | * machine. |
| 805 | * |
| 806 | * @param endpoint An endpoint on the local machine to which the socket will |
| 807 | * be bound. |
| 808 | * |
| 809 | * @param ec Set to indicate what error occurred, if any. |
| 810 | * |
| 811 | * @par Example |
| 812 | * @code |
| 813 | * boost::asio::ip::tcp::socket socket(my_context); |
| 814 | * socket.open(boost::asio::ip::tcp::v4()); |
| 815 | * boost::system::error_code ec; |
| 816 | * socket.bind(boost::asio::ip::tcp::endpoint( |
| 817 | * boost::asio::ip::tcp::v4(), 12345), ec); |
| 818 | * if (ec) |
| 819 | * { |
| 820 | * // An error occurred. |
| 821 | * } |
| 822 | * @endcode |
| 823 | */ |
| 824 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code bind(const endpoint_type& endpoint, |
| 825 | boost::system::error_code& ec) |
| 826 | { |
| 827 | impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); |
| 828 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 829 | } |
| 830 | |
| 831 | /// Connect the socket to the specified endpoint. |
| 832 | /** |
| 833 | * This function is used to connect a socket to the specified remote endpoint. |
| 834 | * The function call will block until the connection is successfully made or |
| 835 | * an error occurs. |
| 836 | * |
| 837 | * The socket is automatically opened if it is not already open. If the |
| 838 | * connect fails, and the socket was automatically opened, the socket is |
| 839 | * not returned to the closed state. |
| 840 | * |
| 841 | * @param peer_endpoint The remote endpoint to which the socket will be |
| 842 | * connected. |
| 843 | * |
| 844 | * @throws boost::system::system_error Thrown on failure. |
| 845 | * |
| 846 | * @par Example |
| 847 | * @code |
| 848 | * boost::asio::ip::tcp::socket socket(my_context); |
| 849 | * boost::asio::ip::tcp::endpoint endpoint( |
| 850 | * boost::asio::ip::address::from_string("1.2.3.4"), 12345); |
| 851 | * socket.connect(endpoint); |
| 852 | * @endcode |
| 853 | */ |
| 854 | void connect(const endpoint_type& peer_endpoint) |
| 855 | { |
| 856 | boost::system::error_code ec; |
| 857 | if (!is_open()) |
| 858 | { |
| 859 | impl_.get_service().open(impl_.get_implementation(), |
| 860 | peer_endpoint.protocol(), ec); |
| 861 | boost::asio::detail::throw_error(ec, "connect"); |
| 862 | } |
| 863 | impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); |
| 864 | boost::asio::detail::throw_error(ec, "connect"); |
| 865 | } |
| 866 | |
| 867 | /// Connect the socket to the specified endpoint. |
| 868 | /** |
| 869 | * This function is used to connect a socket to the specified remote endpoint. |
| 870 | * The function call will block until the connection is successfully made or |
| 871 | * an error occurs. |
| 872 | * |
| 873 | * The socket is automatically opened if it is not already open. If the |
| 874 | * connect fails, and the socket was automatically opened, the socket is |
| 875 | * not returned to the closed state. |
| 876 | * |
| 877 | * @param peer_endpoint The remote endpoint to which the socket will be |
| 878 | * connected. |
| 879 | * |
| 880 | * @param ec Set to indicate what error occurred, if any. |
| 881 | * |
| 882 | * @par Example |
| 883 | * @code |
| 884 | * boost::asio::ip::tcp::socket socket(my_context); |
| 885 | * boost::asio::ip::tcp::endpoint endpoint( |
| 886 | * boost::asio::ip::address::from_string("1.2.3.4"), 12345); |
| 887 | * boost::system::error_code ec; |
| 888 | * socket.connect(endpoint, ec); |
| 889 | * if (ec) |
| 890 | * { |
| 891 | * // An error occurred. |
| 892 | * } |
| 893 | * @endcode |
| 894 | */ |
| 895 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code connect(const endpoint_type& peer_endpoint, |
| 896 | boost::system::error_code& ec) |
| 897 | { |
| 898 | if (!is_open()) |
| 899 | { |
| 900 | impl_.get_service().open(impl_.get_implementation(), |
| 901 | peer_endpoint.protocol(), ec); |
| 902 | if (ec) |
| 903 | { |
| 904 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 905 | } |
| 906 | } |
| 907 | |
| 908 | impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); |
| 909 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 910 | } |
| 911 | |
| 912 | /// Start an asynchronous connect. |
| 913 | /** |
| 914 | * This function is used to asynchronously connect a socket to the specified |
| 915 | * remote endpoint. It is an initiating function for an @ref |
| 916 | * asynchronous_operation, and always returns immediately. |
| 917 | * |
| 918 | * The socket is automatically opened if it is not already open. If the |
| 919 | * connect fails, and the socket was automatically opened, the socket is |
| 920 | * not returned to the closed state. |
| 921 | * |
| 922 | * @param peer_endpoint The remote endpoint to which the socket will be |
| 923 | * connected. Copies will be made of the endpoint object as required. |
| 924 | * |
| 925 | * @param token The @ref completion_token that will be used to produce a |
| 926 | * completion handler, which will be called when the connect completes. |
| 927 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
| 928 | * @ref yield_context, or a function object with the correct completion |
| 929 | * signature. The function signature of the completion handler must be: |
| 930 | * @code void handler( |
| 931 | * const boost::system::error_code& error // Result of operation. |
| 932 | * ); @endcode |
| 933 | * Regardless of whether the asynchronous operation completes immediately or |
| 934 | * not, the completion handler will not be invoked from within this function. |
| 935 | * On immediate completion, invocation of the handler will be performed in a |
| 936 | * manner equivalent to using boost::asio::post(). |
| 937 | * |
| 938 | * @par Completion Signature |
| 939 | * @code void(boost::system::error_code) @endcode |
| 940 | * |
| 941 | * @par Example |
| 942 | * @code |
| 943 | * void connect_handler(const boost::system::error_code& error) |
| 944 | * { |
| 945 | * if (!error) |
| 946 | * { |
| 947 | * // Connect succeeded. |
| 948 | * } |
| 949 | * } |
| 950 | * |
| 951 | * ... |
| 952 | * |
| 953 | * boost::asio::ip::tcp::socket socket(my_context); |
| 954 | * boost::asio::ip::tcp::endpoint endpoint( |
| 955 | * boost::asio::ip::address::from_string("1.2.3.4"), 12345); |
| 956 | * socket.async_connect(endpoint, connect_handler); |
| 957 | * @endcode |
| 958 | * |
| 959 | * @par Per-Operation Cancellation |
| 960 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 961 | * cancellation for the following boost::asio::cancellation_type values: |
| 962 | * |
| 963 | * @li @c cancellation_type::terminal |
| 964 | * |
| 965 | * @li @c cancellation_type::partial |
| 966 | * |
| 967 | * @li @c cancellation_type::total |
| 968 | */ |
| 969 | template < |
| 970 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code))typename |
| 971 | ConnectToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type> |
| 972 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(ConnectToken,auto |
| 973 | void (boost::system::error_code))auto |
| 974 | async_connect(const endpoint_type& peer_endpoint, |
| 975 | BOOST_ASIO_MOVE_ARG(ConnectToken)ConnectToken&& token |
| 976 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type()) |
| 977 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( |
| 978 | async_initiate<ConnectToken, void (boost::system::error_code)>( |
| 979 | declval<initiate_async_connect>(), token, |
| 980 | peer_endpoint, declval<boost::system::error_code&>()))) |
| 981 | { |
| 982 | boost::system::error_code open_ec; |
| 983 | if (!is_open()) |
| 984 | { |
| 985 | const protocol_type protocol = peer_endpoint.protocol(); |
| 986 | impl_.get_service().open(impl_.get_implementation(), protocol, open_ec); |
| 987 | } |
| 988 | |
| 989 | return async_initiate<ConnectToken, void (boost::system::error_code)>( |
| 990 | initiate_async_connect(this), token, peer_endpoint, open_ec); |
| 991 | } |
| 992 | |
| 993 | /// Set an option on the socket. |
| 994 | /** |
| 995 | * This function is used to set an option on the socket. |
| 996 | * |
| 997 | * @param option The new option value to be set on the socket. |
| 998 | * |
| 999 | * @throws boost::system::system_error Thrown on failure. |
| 1000 | * |
| 1001 | * @sa SettableSocketOption @n |
| 1002 | * boost::asio::socket_base::broadcast @n |
| 1003 | * boost::asio::socket_base::do_not_route @n |
| 1004 | * boost::asio::socket_base::keep_alive @n |
| 1005 | * boost::asio::socket_base::linger @n |
| 1006 | * boost::asio::socket_base::receive_buffer_size @n |
| 1007 | * boost::asio::socket_base::receive_low_watermark @n |
| 1008 | * boost::asio::socket_base::reuse_address @n |
| 1009 | * boost::asio::socket_base::send_buffer_size @n |
| 1010 | * boost::asio::socket_base::send_low_watermark @n |
| 1011 | * boost::asio::ip::multicast::join_group @n |
| 1012 | * boost::asio::ip::multicast::leave_group @n |
| 1013 | * boost::asio::ip::multicast::enable_loopback @n |
| 1014 | * boost::asio::ip::multicast::outbound_interface @n |
| 1015 | * boost::asio::ip::multicast::hops @n |
| 1016 | * boost::asio::ip::tcp::no_delay |
| 1017 | * |
| 1018 | * @par Example |
| 1019 | * Setting the IPPROTO_TCP/TCP_NODELAY option: |
| 1020 | * @code |
| 1021 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1022 | * ... |
| 1023 | * boost::asio::ip::tcp::no_delay option(true); |
| 1024 | * socket.set_option(option); |
| 1025 | * @endcode |
| 1026 | */ |
| 1027 | template <typename SettableSocketOption> |
| 1028 | void set_option(const SettableSocketOption& option) |
| 1029 | { |
| 1030 | boost::system::error_code ec; |
| 1031 | impl_.get_service().set_option(impl_.get_implementation(), option, ec); |
| 1032 | boost::asio::detail::throw_error(ec, "set_option"); |
| 1033 | } |
| 1034 | |
| 1035 | /// Set an option on the socket. |
| 1036 | /** |
| 1037 | * This function is used to set an option on the socket. |
| 1038 | * |
| 1039 | * @param option The new option value to be set on the socket. |
| 1040 | * |
| 1041 | * @param ec Set to indicate what error occurred, if any. |
| 1042 | * |
| 1043 | * @sa SettableSocketOption @n |
| 1044 | * boost::asio::socket_base::broadcast @n |
| 1045 | * boost::asio::socket_base::do_not_route @n |
| 1046 | * boost::asio::socket_base::keep_alive @n |
| 1047 | * boost::asio::socket_base::linger @n |
| 1048 | * boost::asio::socket_base::receive_buffer_size @n |
| 1049 | * boost::asio::socket_base::receive_low_watermark @n |
| 1050 | * boost::asio::socket_base::reuse_address @n |
| 1051 | * boost::asio::socket_base::send_buffer_size @n |
| 1052 | * boost::asio::socket_base::send_low_watermark @n |
| 1053 | * boost::asio::ip::multicast::join_group @n |
| 1054 | * boost::asio::ip::multicast::leave_group @n |
| 1055 | * boost::asio::ip::multicast::enable_loopback @n |
| 1056 | * boost::asio::ip::multicast::outbound_interface @n |
| 1057 | * boost::asio::ip::multicast::hops @n |
| 1058 | * boost::asio::ip::tcp::no_delay |
| 1059 | * |
| 1060 | * @par Example |
| 1061 | * Setting the IPPROTO_TCP/TCP_NODELAY option: |
| 1062 | * @code |
| 1063 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1064 | * ... |
| 1065 | * boost::asio::ip::tcp::no_delay option(true); |
| 1066 | * boost::system::error_code ec; |
| 1067 | * socket.set_option(option, ec); |
| 1068 | * if (ec) |
| 1069 | * { |
| 1070 | * // An error occurred. |
| 1071 | * } |
| 1072 | * @endcode |
| 1073 | */ |
| 1074 | template <typename SettableSocketOption> |
| 1075 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code set_option(const SettableSocketOption& option, |
| 1076 | boost::system::error_code& ec) |
| 1077 | { |
| 1078 | impl_.get_service().set_option(impl_.get_implementation(), option, ec); |
| 1079 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1080 | } |
| 1081 | |
| 1082 | /// Get an option from the socket. |
| 1083 | /** |
| 1084 | * This function is used to get the current value of an option on the socket. |
| 1085 | * |
| 1086 | * @param option The option value to be obtained from the socket. |
| 1087 | * |
| 1088 | * @throws boost::system::system_error Thrown on failure. |
| 1089 | * |
| 1090 | * @sa GettableSocketOption @n |
| 1091 | * boost::asio::socket_base::broadcast @n |
| 1092 | * boost::asio::socket_base::do_not_route @n |
| 1093 | * boost::asio::socket_base::keep_alive @n |
| 1094 | * boost::asio::socket_base::linger @n |
| 1095 | * boost::asio::socket_base::receive_buffer_size @n |
| 1096 | * boost::asio::socket_base::receive_low_watermark @n |
| 1097 | * boost::asio::socket_base::reuse_address @n |
| 1098 | * boost::asio::socket_base::send_buffer_size @n |
| 1099 | * boost::asio::socket_base::send_low_watermark @n |
| 1100 | * boost::asio::ip::multicast::join_group @n |
| 1101 | * boost::asio::ip::multicast::leave_group @n |
| 1102 | * boost::asio::ip::multicast::enable_loopback @n |
| 1103 | * boost::asio::ip::multicast::outbound_interface @n |
| 1104 | * boost::asio::ip::multicast::hops @n |
| 1105 | * boost::asio::ip::tcp::no_delay |
| 1106 | * |
| 1107 | * @par Example |
| 1108 | * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: |
| 1109 | * @code |
| 1110 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1111 | * ... |
| 1112 | * boost::asio::ip::tcp::socket::keep_alive option; |
| 1113 | * socket.get_option(option); |
| 1114 | * bool is_set = option.value(); |
| 1115 | * @endcode |
| 1116 | */ |
| 1117 | template <typename GettableSocketOption> |
| 1118 | void get_option(GettableSocketOption& option) const |
| 1119 | { |
| 1120 | boost::system::error_code ec; |
| 1121 | impl_.get_service().get_option(impl_.get_implementation(), option, ec); |
| 1122 | boost::asio::detail::throw_error(ec, "get_option"); |
| 1123 | } |
| 1124 | |
| 1125 | /// Get an option from the socket. |
| 1126 | /** |
| 1127 | * This function is used to get the current value of an option on the socket. |
| 1128 | * |
| 1129 | * @param option The option value to be obtained from the socket. |
| 1130 | * |
| 1131 | * @param ec Set to indicate what error occurred, if any. |
| 1132 | * |
| 1133 | * @sa GettableSocketOption @n |
| 1134 | * boost::asio::socket_base::broadcast @n |
| 1135 | * boost::asio::socket_base::do_not_route @n |
| 1136 | * boost::asio::socket_base::keep_alive @n |
| 1137 | * boost::asio::socket_base::linger @n |
| 1138 | * boost::asio::socket_base::receive_buffer_size @n |
| 1139 | * boost::asio::socket_base::receive_low_watermark @n |
| 1140 | * boost::asio::socket_base::reuse_address @n |
| 1141 | * boost::asio::socket_base::send_buffer_size @n |
| 1142 | * boost::asio::socket_base::send_low_watermark @n |
| 1143 | * boost::asio::ip::multicast::join_group @n |
| 1144 | * boost::asio::ip::multicast::leave_group @n |
| 1145 | * boost::asio::ip::multicast::enable_loopback @n |
| 1146 | * boost::asio::ip::multicast::outbound_interface @n |
| 1147 | * boost::asio::ip::multicast::hops @n |
| 1148 | * boost::asio::ip::tcp::no_delay |
| 1149 | * |
| 1150 | * @par Example |
| 1151 | * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: |
| 1152 | * @code |
| 1153 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1154 | * ... |
| 1155 | * boost::asio::ip::tcp::socket::keep_alive option; |
| 1156 | * boost::system::error_code ec; |
| 1157 | * socket.get_option(option, ec); |
| 1158 | * if (ec) |
| 1159 | * { |
| 1160 | * // An error occurred. |
| 1161 | * } |
| 1162 | * bool is_set = option.value(); |
| 1163 | * @endcode |
| 1164 | */ |
| 1165 | template <typename GettableSocketOption> |
| 1166 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code get_option(GettableSocketOption& option, |
| 1167 | boost::system::error_code& ec) const |
| 1168 | { |
| 1169 | impl_.get_service().get_option(impl_.get_implementation(), option, ec); |
| 1170 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1171 | } |
| 1172 | |
| 1173 | /// Perform an IO control command on the socket. |
| 1174 | /** |
| 1175 | * This function is used to execute an IO control command on the socket. |
| 1176 | * |
| 1177 | * @param command The IO control command to be performed on the socket. |
| 1178 | * |
| 1179 | * @throws boost::system::system_error Thrown on failure. |
| 1180 | * |
| 1181 | * @sa IoControlCommand @n |
| 1182 | * boost::asio::socket_base::bytes_readable @n |
| 1183 | * boost::asio::socket_base::non_blocking_io |
| 1184 | * |
| 1185 | * @par Example |
| 1186 | * Getting the number of bytes ready to read: |
| 1187 | * @code |
| 1188 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1189 | * ... |
| 1190 | * boost::asio::ip::tcp::socket::bytes_readable command; |
| 1191 | * socket.io_control(command); |
| 1192 | * std::size_t bytes_readable = command.get(); |
| 1193 | * @endcode |
| 1194 | */ |
| 1195 | template <typename IoControlCommand> |
| 1196 | void io_control(IoControlCommand& command) |
| 1197 | { |
| 1198 | boost::system::error_code ec; |
| 1199 | impl_.get_service().io_control(impl_.get_implementation(), command, ec); |
| 1200 | boost::asio::detail::throw_error(ec, "io_control"); |
| 1201 | } |
| 1202 | |
| 1203 | /// Perform an IO control command on the socket. |
| 1204 | /** |
| 1205 | * This function is used to execute an IO control command on the socket. |
| 1206 | * |
| 1207 | * @param command The IO control command to be performed on the socket. |
| 1208 | * |
| 1209 | * @param ec Set to indicate what error occurred, if any. |
| 1210 | * |
| 1211 | * @sa IoControlCommand @n |
| 1212 | * boost::asio::socket_base::bytes_readable @n |
| 1213 | * boost::asio::socket_base::non_blocking_io |
| 1214 | * |
| 1215 | * @par Example |
| 1216 | * Getting the number of bytes ready to read: |
| 1217 | * @code |
| 1218 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1219 | * ... |
| 1220 | * boost::asio::ip::tcp::socket::bytes_readable command; |
| 1221 | * boost::system::error_code ec; |
| 1222 | * socket.io_control(command, ec); |
| 1223 | * if (ec) |
| 1224 | * { |
| 1225 | * // An error occurred. |
| 1226 | * } |
| 1227 | * std::size_t bytes_readable = command.get(); |
| 1228 | * @endcode |
| 1229 | */ |
| 1230 | template <typename IoControlCommand> |
| 1231 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code io_control(IoControlCommand& command, |
| 1232 | boost::system::error_code& ec) |
| 1233 | { |
| 1234 | impl_.get_service().io_control(impl_.get_implementation(), command, ec); |
| 1235 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1236 | } |
| 1237 | |
| 1238 | /// Gets the non-blocking mode of the socket. |
| 1239 | /** |
| 1240 | * @returns @c true if the socket's synchronous operations will fail with |
| 1241 | * boost::asio::error::would_block if they are unable to perform the requested |
| 1242 | * operation immediately. If @c false, synchronous operations will block |
| 1243 | * until complete. |
| 1244 | * |
| 1245 | * @note The non-blocking mode has no effect on the behaviour of asynchronous |
| 1246 | * operations. Asynchronous operations will never fail with the error |
| 1247 | * boost::asio::error::would_block. |
| 1248 | */ |
| 1249 | bool non_blocking() const |
| 1250 | { |
| 1251 | return impl_.get_service().non_blocking(impl_.get_implementation()); |
| 1252 | } |
| 1253 | |
| 1254 | /// Sets the non-blocking mode of the socket. |
| 1255 | /** |
| 1256 | * @param mode If @c true, the socket's synchronous operations will fail with |
| 1257 | * boost::asio::error::would_block if they are unable to perform the requested |
| 1258 | * operation immediately. If @c false, synchronous operations will block |
| 1259 | * until complete. |
| 1260 | * |
| 1261 | * @throws boost::system::system_error Thrown on failure. |
| 1262 | * |
| 1263 | * @note The non-blocking mode has no effect on the behaviour of asynchronous |
| 1264 | * operations. Asynchronous operations will never fail with the error |
| 1265 | * boost::asio::error::would_block. |
| 1266 | */ |
| 1267 | void non_blocking(bool mode) |
| 1268 | { |
| 1269 | boost::system::error_code ec; |
| 1270 | impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); |
| 1271 | boost::asio::detail::throw_error(ec, "non_blocking"); |
| 1272 | } |
| 1273 | |
| 1274 | /// Sets the non-blocking mode of the socket. |
| 1275 | /** |
| 1276 | * @param mode If @c true, the socket's synchronous operations will fail with |
| 1277 | * boost::asio::error::would_block if they are unable to perform the requested |
| 1278 | * operation immediately. If @c false, synchronous operations will block |
| 1279 | * until complete. |
| 1280 | * |
| 1281 | * @param ec Set to indicate what error occurred, if any. |
| 1282 | * |
| 1283 | * @note The non-blocking mode has no effect on the behaviour of asynchronous |
| 1284 | * operations. Asynchronous operations will never fail with the error |
| 1285 | * boost::asio::error::would_block. |
| 1286 | */ |
| 1287 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code non_blocking( |
| 1288 | bool mode, boost::system::error_code& ec) |
| 1289 | { |
| 1290 | impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); |
| 1291 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1292 | } |
| 1293 | |
| 1294 | /// Gets the non-blocking mode of the native socket implementation. |
| 1295 | /** |
| 1296 | * This function is used to retrieve the non-blocking mode of the underlying |
| 1297 | * native socket. This mode has no effect on the behaviour of the socket |
| 1298 | * object's synchronous operations. |
| 1299 | * |
| 1300 | * @returns @c true if the underlying socket is in non-blocking mode and |
| 1301 | * direct system calls may fail with boost::asio::error::would_block (or the |
| 1302 | * equivalent system error). |
| 1303 | * |
| 1304 | * @note The current non-blocking mode is cached by the socket object. |
| 1305 | * Consequently, the return value may be incorrect if the non-blocking mode |
| 1306 | * was set directly on the native socket. |
| 1307 | * |
| 1308 | * @par Example |
| 1309 | * This function is intended to allow the encapsulation of arbitrary |
| 1310 | * non-blocking system calls as asynchronous operations, in a way that is |
| 1311 | * transparent to the user of the socket object. The following example |
| 1312 | * illustrates how Linux's @c sendfile system call might be encapsulated: |
| 1313 | * @code template <typename Handler> |
| 1314 | * struct sendfile_op |
| 1315 | * { |
| 1316 | * tcp::socket& sock_; |
| 1317 | * int fd_; |
| 1318 | * Handler handler_; |
| 1319 | * off_t offset_; |
| 1320 | * std::size_t total_bytes_transferred_; |
| 1321 | * |
| 1322 | * // Function call operator meeting WriteHandler requirements. |
| 1323 | * // Used as the handler for the async_write_some operation. |
| 1324 | * void operator()(boost::system::error_code ec, std::size_t) |
| 1325 | * { |
| 1326 | * // Put the underlying socket into non-blocking mode. |
| 1327 | * if (!ec) |
| 1328 | * if (!sock_.native_non_blocking()) |
| 1329 | * sock_.native_non_blocking(true, ec); |
| 1330 | * |
| 1331 | * if (!ec) |
| 1332 | * { |
| 1333 | * for (;;) |
| 1334 | * { |
| 1335 | * // Try the system call. |
| 1336 | * errno = 0; |
| 1337 | * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); |
| 1338 | * ec = boost::system::error_code(n < 0 ? errno : 0, |
| 1339 | * boost::asio::error::get_system_category()); |
| 1340 | * total_bytes_transferred_ += ec ? 0 : n; |
| 1341 | * |
| 1342 | * // Retry operation immediately if interrupted by signal. |
| 1343 | * if (ec == boost::asio::error::interrupted) |
| 1344 | * continue; |
| 1345 | * |
| 1346 | * // Check if we need to run the operation again. |
| 1347 | * if (ec == boost::asio::error::would_block |
| 1348 | * || ec == boost::asio::error::try_again) |
| 1349 | * { |
| 1350 | * // We have to wait for the socket to become ready again. |
| 1351 | * sock_.async_wait(tcp::socket::wait_write, *this); |
| 1352 | * return; |
| 1353 | * } |
| 1354 | * |
| 1355 | * if (ec || n == 0) |
| 1356 | * { |
| 1357 | * // An error occurred, or we have reached the end of the file. |
| 1358 | * // Either way we must exit the loop so we can call the handler. |
| 1359 | * break; |
| 1360 | * } |
| 1361 | * |
| 1362 | * // Loop around to try calling sendfile again. |
| 1363 | * } |
| 1364 | * } |
| 1365 | * |
| 1366 | * // Pass result back to user's handler. |
| 1367 | * handler_(ec, total_bytes_transferred_); |
| 1368 | * } |
| 1369 | * }; |
| 1370 | * |
| 1371 | * template <typename Handler> |
| 1372 | * void async_sendfile(tcp::socket& sock, int fd, Handler h) |
| 1373 | * { |
| 1374 | * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; |
| 1375 | * sock.async_wait(tcp::socket::wait_write, op); |
| 1376 | * } @endcode |
| 1377 | */ |
| 1378 | bool native_non_blocking() const |
| 1379 | { |
| 1380 | return impl_.get_service().native_non_blocking(impl_.get_implementation()); |
| 1381 | } |
| 1382 | |
| 1383 | /// Sets the non-blocking mode of the native socket implementation. |
| 1384 | /** |
| 1385 | * This function is used to modify the non-blocking mode of the underlying |
| 1386 | * native socket. It has no effect on the behaviour of the socket object's |
| 1387 | * synchronous operations. |
| 1388 | * |
| 1389 | * @param mode If @c true, the underlying socket is put into non-blocking |
| 1390 | * mode and direct system calls may fail with boost::asio::error::would_block |
| 1391 | * (or the equivalent system error). |
| 1392 | * |
| 1393 | * @throws boost::system::system_error Thrown on failure. If the @c mode is |
| 1394 | * @c false, but the current value of @c non_blocking() is @c true, this |
| 1395 | * function fails with boost::asio::error::invalid_argument, as the |
| 1396 | * combination does not make sense. |
| 1397 | * |
| 1398 | * @par Example |
| 1399 | * This function is intended to allow the encapsulation of arbitrary |
| 1400 | * non-blocking system calls as asynchronous operations, in a way that is |
| 1401 | * transparent to the user of the socket object. The following example |
| 1402 | * illustrates how Linux's @c sendfile system call might be encapsulated: |
| 1403 | * @code template <typename Handler> |
| 1404 | * struct sendfile_op |
| 1405 | * { |
| 1406 | * tcp::socket& sock_; |
| 1407 | * int fd_; |
| 1408 | * Handler handler_; |
| 1409 | * off_t offset_; |
| 1410 | * std::size_t total_bytes_transferred_; |
| 1411 | * |
| 1412 | * // Function call operator meeting WriteHandler requirements. |
| 1413 | * // Used as the handler for the async_write_some operation. |
| 1414 | * void operator()(boost::system::error_code ec, std::size_t) |
| 1415 | * { |
| 1416 | * // Put the underlying socket into non-blocking mode. |
| 1417 | * if (!ec) |
| 1418 | * if (!sock_.native_non_blocking()) |
| 1419 | * sock_.native_non_blocking(true, ec); |
| 1420 | * |
| 1421 | * if (!ec) |
| 1422 | * { |
| 1423 | * for (;;) |
| 1424 | * { |
| 1425 | * // Try the system call. |
| 1426 | * errno = 0; |
| 1427 | * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); |
| 1428 | * ec = boost::system::error_code(n < 0 ? errno : 0, |
| 1429 | * boost::asio::error::get_system_category()); |
| 1430 | * total_bytes_transferred_ += ec ? 0 : n; |
| 1431 | * |
| 1432 | * // Retry operation immediately if interrupted by signal. |
| 1433 | * if (ec == boost::asio::error::interrupted) |
| 1434 | * continue; |
| 1435 | * |
| 1436 | * // Check if we need to run the operation again. |
| 1437 | * if (ec == boost::asio::error::would_block |
| 1438 | * || ec == boost::asio::error::try_again) |
| 1439 | * { |
| 1440 | * // We have to wait for the socket to become ready again. |
| 1441 | * sock_.async_wait(tcp::socket::wait_write, *this); |
| 1442 | * return; |
| 1443 | * } |
| 1444 | * |
| 1445 | * if (ec || n == 0) |
| 1446 | * { |
| 1447 | * // An error occurred, or we have reached the end of the file. |
| 1448 | * // Either way we must exit the loop so we can call the handler. |
| 1449 | * break; |
| 1450 | * } |
| 1451 | * |
| 1452 | * // Loop around to try calling sendfile again. |
| 1453 | * } |
| 1454 | * } |
| 1455 | * |
| 1456 | * // Pass result back to user's handler. |
| 1457 | * handler_(ec, total_bytes_transferred_); |
| 1458 | * } |
| 1459 | * }; |
| 1460 | * |
| 1461 | * template <typename Handler> |
| 1462 | * void async_sendfile(tcp::socket& sock, int fd, Handler h) |
| 1463 | * { |
| 1464 | * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; |
| 1465 | * sock.async_wait(tcp::socket::wait_write, op); |
| 1466 | * } @endcode |
| 1467 | */ |
| 1468 | void native_non_blocking(bool mode) |
| 1469 | { |
| 1470 | boost::system::error_code ec; |
| 1471 | impl_.get_service().native_non_blocking( |
| 1472 | impl_.get_implementation(), mode, ec); |
| 1473 | boost::asio::detail::throw_error(ec, "native_non_blocking"); |
| 1474 | } |
| 1475 | |
| 1476 | /// Sets the non-blocking mode of the native socket implementation. |
| 1477 | /** |
| 1478 | * This function is used to modify the non-blocking mode of the underlying |
| 1479 | * native socket. It has no effect on the behaviour of the socket object's |
| 1480 | * synchronous operations. |
| 1481 | * |
| 1482 | * @param mode If @c true, the underlying socket is put into non-blocking |
| 1483 | * mode and direct system calls may fail with boost::asio::error::would_block |
| 1484 | * (or the equivalent system error). |
| 1485 | * |
| 1486 | * @param ec Set to indicate what error occurred, if any. If the @c mode is |
| 1487 | * @c false, but the current value of @c non_blocking() is @c true, this |
| 1488 | * function fails with boost::asio::error::invalid_argument, as the |
| 1489 | * combination does not make sense. |
| 1490 | * |
| 1491 | * @par Example |
| 1492 | * This function is intended to allow the encapsulation of arbitrary |
| 1493 | * non-blocking system calls as asynchronous operations, in a way that is |
| 1494 | * transparent to the user of the socket object. The following example |
| 1495 | * illustrates how Linux's @c sendfile system call might be encapsulated: |
| 1496 | * @code template <typename Handler> |
| 1497 | * struct sendfile_op |
| 1498 | * { |
| 1499 | * tcp::socket& sock_; |
| 1500 | * int fd_; |
| 1501 | * Handler handler_; |
| 1502 | * off_t offset_; |
| 1503 | * std::size_t total_bytes_transferred_; |
| 1504 | * |
| 1505 | * // Function call operator meeting WriteHandler requirements. |
| 1506 | * // Used as the handler for the async_write_some operation. |
| 1507 | * void operator()(boost::system::error_code ec, std::size_t) |
| 1508 | * { |
| 1509 | * // Put the underlying socket into non-blocking mode. |
| 1510 | * if (!ec) |
| 1511 | * if (!sock_.native_non_blocking()) |
| 1512 | * sock_.native_non_blocking(true, ec); |
| 1513 | * |
| 1514 | * if (!ec) |
| 1515 | * { |
| 1516 | * for (;;) |
| 1517 | * { |
| 1518 | * // Try the system call. |
| 1519 | * errno = 0; |
| 1520 | * int n = ::sendfile(sock_.native_handle(), fd_, &offset_, 65536); |
| 1521 | * ec = boost::system::error_code(n < 0 ? errno : 0, |
| 1522 | * boost::asio::error::get_system_category()); |
| 1523 | * total_bytes_transferred_ += ec ? 0 : n; |
| 1524 | * |
| 1525 | * // Retry operation immediately if interrupted by signal. |
| 1526 | * if (ec == boost::asio::error::interrupted) |
| 1527 | * continue; |
| 1528 | * |
| 1529 | * // Check if we need to run the operation again. |
| 1530 | * if (ec == boost::asio::error::would_block |
| 1531 | * || ec == boost::asio::error::try_again) |
| 1532 | * { |
| 1533 | * // We have to wait for the socket to become ready again. |
| 1534 | * sock_.async_wait(tcp::socket::wait_write, *this); |
| 1535 | * return; |
| 1536 | * } |
| 1537 | * |
| 1538 | * if (ec || n == 0) |
| 1539 | * { |
| 1540 | * // An error occurred, or we have reached the end of the file. |
| 1541 | * // Either way we must exit the loop so we can call the handler. |
| 1542 | * break; |
| 1543 | * } |
| 1544 | * |
| 1545 | * // Loop around to try calling sendfile again. |
| 1546 | * } |
| 1547 | * } |
| 1548 | * |
| 1549 | * // Pass result back to user's handler. |
| 1550 | * handler_(ec, total_bytes_transferred_); |
| 1551 | * } |
| 1552 | * }; |
| 1553 | * |
| 1554 | * template <typename Handler> |
| 1555 | * void async_sendfile(tcp::socket& sock, int fd, Handler h) |
| 1556 | * { |
| 1557 | * sendfile_op<Handler> op = { sock, fd, h, 0, 0 }; |
| 1558 | * sock.async_wait(tcp::socket::wait_write, op); |
| 1559 | * } @endcode |
| 1560 | */ |
| 1561 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code native_non_blocking( |
| 1562 | bool mode, boost::system::error_code& ec) |
| 1563 | { |
| 1564 | impl_.get_service().native_non_blocking( |
| 1565 | impl_.get_implementation(), mode, ec); |
| 1566 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1567 | } |
| 1568 | |
| 1569 | /// Get the local endpoint of the socket. |
| 1570 | /** |
| 1571 | * This function is used to obtain the locally bound endpoint of the socket. |
| 1572 | * |
| 1573 | * @returns An object that represents the local endpoint of the socket. |
| 1574 | * |
| 1575 | * @throws boost::system::system_error Thrown on failure. |
| 1576 | * |
| 1577 | * @par Example |
| 1578 | * @code |
| 1579 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1580 | * ... |
| 1581 | * boost::asio::ip::tcp::endpoint endpoint = socket.local_endpoint(); |
| 1582 | * @endcode |
| 1583 | */ |
| 1584 | endpoint_type local_endpoint() const |
| 1585 | { |
| 1586 | boost::system::error_code ec; |
| 1587 | endpoint_type ep = impl_.get_service().local_endpoint( |
| 1588 | impl_.get_implementation(), ec); |
| 1589 | boost::asio::detail::throw_error(ec, "local_endpoint"); |
| 1590 | return ep; |
| 1591 | } |
| 1592 | |
| 1593 | /// Get the local endpoint of the socket. |
| 1594 | /** |
| 1595 | * This function is used to obtain the locally bound endpoint of the socket. |
| 1596 | * |
| 1597 | * @param ec Set to indicate what error occurred, if any. |
| 1598 | * |
| 1599 | * @returns An object that represents the local endpoint of the socket. |
| 1600 | * Returns a default-constructed endpoint object if an error occurred. |
| 1601 | * |
| 1602 | * @par Example |
| 1603 | * @code |
| 1604 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1605 | * ... |
| 1606 | * boost::system::error_code ec; |
| 1607 | * boost::asio::ip::tcp::endpoint endpoint = socket.local_endpoint(ec); |
| 1608 | * if (ec) |
| 1609 | * { |
| 1610 | * // An error occurred. |
| 1611 | * } |
| 1612 | * @endcode |
| 1613 | */ |
| 1614 | endpoint_type local_endpoint(boost::system::error_code& ec) const |
| 1615 | { |
| 1616 | return impl_.get_service().local_endpoint(impl_.get_implementation(), ec); |
| 1617 | } |
| 1618 | |
| 1619 | /// Get the remote endpoint of the socket. |
| 1620 | /** |
| 1621 | * This function is used to obtain the remote endpoint of the socket. |
| 1622 | * |
| 1623 | * @returns An object that represents the remote endpoint of the socket. |
| 1624 | * |
| 1625 | * @throws boost::system::system_error Thrown on failure. |
| 1626 | * |
| 1627 | * @par Example |
| 1628 | * @code |
| 1629 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1630 | * ... |
| 1631 | * boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(); |
| 1632 | * @endcode |
| 1633 | */ |
| 1634 | endpoint_type remote_endpoint() const |
| 1635 | { |
| 1636 | boost::system::error_code ec; |
| 1637 | endpoint_type ep = impl_.get_service().remote_endpoint( |
| 1638 | impl_.get_implementation(), ec); |
| 1639 | boost::asio::detail::throw_error(ec, "remote_endpoint"); |
| 1640 | return ep; |
| 1641 | } |
| 1642 | |
| 1643 | /// Get the remote endpoint of the socket. |
| 1644 | /** |
| 1645 | * This function is used to obtain the remote endpoint of the socket. |
| 1646 | * |
| 1647 | * @param ec Set to indicate what error occurred, if any. |
| 1648 | * |
| 1649 | * @returns An object that represents the remote endpoint of the socket. |
| 1650 | * Returns a default-constructed endpoint object if an error occurred. |
| 1651 | * |
| 1652 | * @par Example |
| 1653 | * @code |
| 1654 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1655 | * ... |
| 1656 | * boost::system::error_code ec; |
| 1657 | * boost::asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(ec); |
| 1658 | * if (ec) |
| 1659 | * { |
| 1660 | * // An error occurred. |
| 1661 | * } |
| 1662 | * @endcode |
| 1663 | */ |
| 1664 | endpoint_type remote_endpoint(boost::system::error_code& ec) const |
| 1665 | { |
| 1666 | return impl_.get_service().remote_endpoint(impl_.get_implementation(), ec); |
| 1667 | } |
| 1668 | |
| 1669 | /// Disable sends or receives on the socket. |
| 1670 | /** |
| 1671 | * This function is used to disable send operations, receive operations, or |
| 1672 | * both. |
| 1673 | * |
| 1674 | * @param what Determines what types of operation will no longer be allowed. |
| 1675 | * |
| 1676 | * @throws boost::system::system_error Thrown on failure. |
| 1677 | * |
| 1678 | * @par Example |
| 1679 | * Shutting down the send side of the socket: |
| 1680 | * @code |
| 1681 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1682 | * ... |
| 1683 | * socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send); |
| 1684 | * @endcode |
| 1685 | */ |
| 1686 | void shutdown(shutdown_type what) |
| 1687 | { |
| 1688 | boost::system::error_code ec; |
| 1689 | impl_.get_service().shutdown(impl_.get_implementation(), what, ec); |
| 1690 | boost::asio::detail::throw_error(ec, "shutdown"); |
| 1691 | } |
| 1692 | |
| 1693 | /// Disable sends or receives on the socket. |
| 1694 | /** |
| 1695 | * This function is used to disable send operations, receive operations, or |
| 1696 | * both. |
| 1697 | * |
| 1698 | * @param what Determines what types of operation will no longer be allowed. |
| 1699 | * |
| 1700 | * @param ec Set to indicate what error occurred, if any. |
| 1701 | * |
| 1702 | * @par Example |
| 1703 | * Shutting down the send side of the socket: |
| 1704 | * @code |
| 1705 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1706 | * ... |
| 1707 | * boost::system::error_code ec; |
| 1708 | * socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); |
| 1709 | * if (ec) |
| 1710 | * { |
| 1711 | * // An error occurred. |
| 1712 | * } |
| 1713 | * @endcode |
| 1714 | */ |
| 1715 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code shutdown(shutdown_type what, |
| 1716 | boost::system::error_code& ec) |
| 1717 | { |
| 1718 | impl_.get_service().shutdown(impl_.get_implementation(), what, ec); |
| 1719 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1720 | } |
| 1721 | |
| 1722 | /// Wait for the socket to become ready to read, ready to write, or to have |
| 1723 | /// pending error conditions. |
| 1724 | /** |
| 1725 | * This function is used to perform a blocking wait for a socket to enter |
| 1726 | * a ready to read, write or error condition state. |
| 1727 | * |
| 1728 | * @param w Specifies the desired socket state. |
| 1729 | * |
| 1730 | * @par Example |
| 1731 | * Waiting for a socket to become readable. |
| 1732 | * @code |
| 1733 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1734 | * ... |
| 1735 | * socket.wait(boost::asio::ip::tcp::socket::wait_read); |
| 1736 | * @endcode |
| 1737 | */ |
| 1738 | void wait(wait_type w) |
| 1739 | { |
| 1740 | boost::system::error_code ec; |
| 1741 | impl_.get_service().wait(impl_.get_implementation(), w, ec); |
| 1742 | boost::asio::detail::throw_error(ec, "wait"); |
| 1743 | } |
| 1744 | |
| 1745 | /// Wait for the socket to become ready to read, ready to write, or to have |
| 1746 | /// pending error conditions. |
| 1747 | /** |
| 1748 | * This function is used to perform a blocking wait for a socket to enter |
| 1749 | * a ready to read, write or error condition state. |
| 1750 | * |
| 1751 | * @param w Specifies the desired socket state. |
| 1752 | * |
| 1753 | * @param ec Set to indicate what error occurred, if any. |
| 1754 | * |
| 1755 | * @par Example |
| 1756 | * Waiting for a socket to become readable. |
| 1757 | * @code |
| 1758 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1759 | * ... |
| 1760 | * boost::system::error_code ec; |
| 1761 | * socket.wait(boost::asio::ip::tcp::socket::wait_read, ec); |
| 1762 | * @endcode |
| 1763 | */ |
| 1764 | BOOST_ASIO_SYNC_OP_VOIDboost::system::error_code wait(wait_type w, boost::system::error_code& ec) |
| 1765 | { |
| 1766 | impl_.get_service().wait(impl_.get_implementation(), w, ec); |
| 1767 | BOOST_ASIO_SYNC_OP_VOID_RETURN(ec)return ec; |
| 1768 | } |
| 1769 | |
| 1770 | /// Asynchronously wait for the socket to become ready to read, ready to |
| 1771 | /// write, or to have pending error conditions. |
| 1772 | /** |
| 1773 | * This function is used to perform an asynchronous wait for a socket to enter |
| 1774 | * a ready to read, write or error condition state. It is an initiating |
| 1775 | * function for an @ref asynchronous_operation, and always returns |
| 1776 | * immediately. |
| 1777 | * |
| 1778 | * @param w Specifies the desired socket state. |
| 1779 | * |
| 1780 | * @param token The @ref completion_token that will be used to produce a |
| 1781 | * completion handler, which will be called when the wait completes. Potential |
| 1782 | * completion tokens include @ref use_future, @ref use_awaitable, @ref |
| 1783 | * yield_context, or a function object with the correct completion signature. |
| 1784 | * The function signature of the completion handler must be: |
| 1785 | * @code void handler( |
| 1786 | * const boost::system::error_code& error // Result of operation. |
| 1787 | * ); @endcode |
| 1788 | * Regardless of whether the asynchronous operation completes immediately or |
| 1789 | * not, the completion handler will not be invoked from within this function. |
| 1790 | * On immediate completion, invocation of the handler will be performed in a |
| 1791 | * manner equivalent to using boost::asio::post(). |
| 1792 | * |
| 1793 | * @par Completion Signature |
| 1794 | * @code void(boost::system::error_code) @endcode |
| 1795 | * |
| 1796 | * @par Example |
| 1797 | * @code |
| 1798 | * void wait_handler(const boost::system::error_code& error) |
| 1799 | * { |
| 1800 | * if (!error) |
| 1801 | * { |
| 1802 | * // Wait succeeded. |
| 1803 | * } |
| 1804 | * } |
| 1805 | * |
| 1806 | * ... |
| 1807 | * |
| 1808 | * boost::asio::ip::tcp::socket socket(my_context); |
| 1809 | * ... |
| 1810 | * socket.async_wait(boost::asio::ip::tcp::socket::wait_read, wait_handler); |
| 1811 | * @endcode |
| 1812 | * |
| 1813 | * @par Per-Operation Cancellation |
| 1814 | * On POSIX or Windows operating systems, this asynchronous operation supports |
| 1815 | * cancellation for the following boost::asio::cancellation_type values: |
| 1816 | * |
| 1817 | * @li @c cancellation_type::terminal |
| 1818 | * |
| 1819 | * @li @c cancellation_type::partial |
| 1820 | * |
| 1821 | * @li @c cancellation_type::total |
| 1822 | */ |
| 1823 | template < |
| 1824 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code))typename |
| 1825 | WaitToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type> |
| 1826 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(WaitToken,auto |
| 1827 | void (boost::system::error_code))auto |
| 1828 | async_wait(wait_type w, |
| 1829 | BOOST_ASIO_MOVE_ARG(WaitToken)WaitToken&& token |
| 1830 | BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)= typename ::boost::asio::default_completion_token<executor_type >::type()) |
| 1831 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( |
| 1832 | async_initiate<WaitToken, void (boost::system::error_code)>( |
| 1833 | declval<initiate_async_wait>(), token, w))) |
| 1834 | { |
| 1835 | return async_initiate<WaitToken, void (boost::system::error_code)>( |
| 1836 | initiate_async_wait(this), token, w); |
| 1837 | } |
| 1838 | |
| 1839 | protected: |
| 1840 | /// Protected destructor to prevent deletion through this type. |
| 1841 | /** |
| 1842 | * This function destroys the socket, cancelling any outstanding asynchronous |
| 1843 | * operations associated with the socket as if by calling @c cancel. |
| 1844 | */ |
| 1845 | ~basic_socket() |
| 1846 | { |
| 1847 | } |
| 1848 | |
| 1849 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) |
| 1850 | detail::io_object_impl< |
| 1851 | detail::null_socket_service<Protocol>, Executor> impl_; |
| 1852 | #elif defined(BOOST_ASIO_HAS_IOCP) |
| 1853 | detail::io_object_impl< |
| 1854 | detail::win_iocp_socket_service<Protocol>, Executor> impl_; |
| 1855 | #elif defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 1856 | detail::io_object_impl< |
| 1857 | detail::io_uring_socket_service<Protocol>, Executor> impl_; |
| 1858 | #else |
| 1859 | detail::io_object_impl< |
| 1860 | detail::reactive_socket_service<Protocol>, Executor> impl_; |
| 1861 | #endif |
| 1862 | |
| 1863 | private: |
| 1864 | // Disallow copying and assignment. |
| 1865 | basic_socket(const basic_socket&) BOOST_ASIO_DELETED= delete; |
| 1866 | basic_socket& operator=(const basic_socket&) BOOST_ASIO_DELETED= delete; |
| 1867 | |
| 1868 | class initiate_async_connect |
| 1869 | { |
| 1870 | public: |
| 1871 | typedef Executor executor_type; |
| 1872 | |
| 1873 | explicit initiate_async_connect(basic_socket* self) |
| 1874 | : self_(self) |
| 1875 | { |
| 1876 | } |
| 1877 | |
| 1878 | const executor_type& get_executor() const BOOST_ASIO_NOEXCEPTnoexcept |
| 1879 | { |
| 1880 | return self_->get_executor(); |
| 1881 | } |
| 1882 | |
| 1883 | template <typename ConnectHandler> |
| 1884 | void operator()(BOOST_ASIO_MOVE_ARG(ConnectHandler)ConnectHandler&& handler, |
| 1885 | const endpoint_type& peer_endpoint, |
| 1886 | const boost::system::error_code& open_ec) const |
| 1887 | { |
| 1888 | // If you get an error on the following line it means that your handler |
| 1889 | // does not meet the documented type requirements for a ConnectHandler. |
| 1890 | 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; |
| 1891 | |
| 1892 | if (open_ec) |
| 1893 | { |
| 1894 | boost::asio::post(self_->impl_.get_executor(), |
| 1895 | boost::asio::detail::bind_handler( |
| 1896 | BOOST_ASIO_MOVE_CAST(ConnectHandler)static_cast<ConnectHandler&&>(handler), open_ec)); |
| 1897 | } |
| 1898 | else |
| 1899 | { |
| 1900 | detail::non_const_lvalue<ConnectHandler> handler2(handler); |
| 1901 | self_->impl_.get_service().async_connect( |
| 1902 | self_->impl_.get_implementation(), peer_endpoint, |
| 1903 | handler2.value, self_->impl_.get_executor()); |
| 1904 | } |
| 1905 | } |
| 1906 | |
| 1907 | private: |
| 1908 | basic_socket* self_; |
| 1909 | }; |
| 1910 | |
| 1911 | class initiate_async_wait |
| 1912 | { |
| 1913 | public: |
| 1914 | typedef Executor executor_type; |
| 1915 | |
| 1916 | explicit initiate_async_wait(basic_socket* self) |
| 1917 | : self_(self) |
| 1918 | { |
| 1919 | } |
| 1920 | |
| 1921 | const executor_type& get_executor() const BOOST_ASIO_NOEXCEPTnoexcept |
| 1922 | { |
| 1923 | return self_->get_executor(); |
| 1924 | } |
| 1925 | |
| 1926 | template <typename WaitHandler> |
| 1927 | void operator()(BOOST_ASIO_MOVE_ARG(WaitHandler)WaitHandler&& handler, wait_type w) const |
| 1928 | { |
| 1929 | // If you get an error on the following line it means that your handler |
| 1930 | // does not meet the documented type requirements for a WaitHandler. |
| 1931 | 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; |
| 1932 | |
| 1933 | detail::non_const_lvalue<WaitHandler> handler2(handler); |
| 1934 | self_->impl_.get_service().async_wait( |
| 1935 | self_->impl_.get_implementation(), w, |
| 1936 | handler2.value, self_->impl_.get_executor()); |
| 1937 | } |
| 1938 | |
| 1939 | private: |
| 1940 | basic_socket* self_; |
| 1941 | }; |
| 1942 | }; |
| 1943 | |
| 1944 | } // namespace asio |
| 1945 | } // namespace boost |
| 1946 | |
| 1947 | #include <boost/asio/detail/pop_options.hpp> |
| 1948 | |
| 1949 | #endif // BOOST_ASIO_BASIC_SOCKET_HPP |
| 1 | // |
| 2 | // io_object_impl.hpp |
| 3 | // ~~~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2023 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 | #if defined(BOOST_ASIO_HAS_MOVE1) |
| 65 | // Move-construct an I/O object. |
| 66 | io_object_impl(io_object_impl&& other) |
| 67 | : service_(&other.get_service()), |
| 68 | executor_(other.get_executor()) |
| 69 | { |
| 70 | service_->move_construct(implementation_, other.implementation_); |
| 71 | } |
| 72 | |
| 73 | // Perform converting move-construction of an I/O object on the same service. |
| 74 | template <typename Executor1> |
| 75 | io_object_impl(io_object_impl<IoObjectService, Executor1>&& other) |
| 76 | : service_(&other.get_service()), |
| 77 | executor_(other.get_executor()) |
| 78 | { |
| 79 | service_->move_construct(implementation_, other.get_implementation()); |
| 80 | } |
| 81 | |
| 82 | // Perform converting move-construction of an I/O object on another service. |
| 83 | template <typename IoObjectService1, typename Executor1> |
| 84 | io_object_impl(io_object_impl<IoObjectService1, Executor1>&& other) |
| 85 | : service_(&boost::asio::use_service<IoObjectService>( |
| 86 | io_object_impl::get_context(other.get_executor()))), |
| 87 | executor_(other.get_executor()) |
| 88 | { |
| 89 | service_->converting_move_construct(implementation_, |
| 90 | other.get_service(), other.get_implementation()); |
| 91 | } |
| 92 | #endif // defined(BOOST_ASIO_HAS_MOVE) |
| 93 | |
| 94 | // Destructor. |
| 95 | ~io_object_impl() |
| 96 | { |
| 97 | service_->destroy(implementation_); |
| 98 | } |
| 99 | |
| 100 | #if defined(BOOST_ASIO_HAS_MOVE1) |
| 101 | // Move-assign an I/O object. |
| 102 | io_object_impl& operator=(io_object_impl&& other) |
| 103 | { |
| 104 | if (this != &other) |
| 105 | { |
| 106 | service_->move_assign(implementation_, |
| 107 | *other.service_, other.implementation_); |
| 108 | executor_.~executor_type(); |
| 109 | new (&executor_) executor_type(other.executor_); |
| 110 | service_ = other.service_; |
| 111 | } |
| 112 | return *this; |
| 113 | } |
| 114 | #endif // defined(BOOST_ASIO_HAS_MOVE) |
| 115 | |
| 116 | // Get the executor associated with the object. |
| 117 | const executor_type& get_executor() BOOST_ASIO_NOEXCEPTnoexcept |
| 118 | { |
| 119 | return executor_; |
| 120 | } |
| 121 | |
| 122 | // Get the service associated with the I/O object. |
| 123 | service_type& get_service() |
| 124 | { |
| 125 | return *service_; |
| 126 | } |
| 127 | |
| 128 | // Get the service associated with the I/O object. |
| 129 | const service_type& get_service() const |
| 130 | { |
| 131 | return *service_; |
| 132 | } |
| 133 | |
| 134 | // Get the underlying implementation of the I/O object. |
| 135 | implementation_type& get_implementation() |
| 136 | { |
| 137 | return implementation_; |
| 138 | } |
| 139 | |
| 140 | // Get the underlying implementation of the I/O object. |
| 141 | const implementation_type& get_implementation() const |
| 142 | { |
| 143 | return implementation_; |
| 144 | } |
| 145 | |
| 146 | private: |
| 147 | // Helper function to get an executor's context. |
| 148 | template <typename T> |
| 149 | static execution_context& get_context(const T& t, |
| 150 | typename enable_if<execution::is_executor<T>::value>::type* = 0) |
| 151 | { |
| 152 | return boost::asio::query(t, execution::context); |
| 153 | } |
| 154 | |
| 155 | // Helper function to get an executor's context. |
| 156 | template <typename T> |
| 157 | static execution_context& get_context(const T& t, |
| 158 | typename enable_if<!execution::is_executor<T>::value>::type* = 0) |
| 159 | { |
| 160 | return t.context(); |
| 161 | } |
| 162 | |
| 163 | // Disallow copying and copy assignment. |
| 164 | io_object_impl(const io_object_impl&); |
| 165 | io_object_impl& operator=(const io_object_impl&); |
| 166 | |
| 167 | // The service associated with the I/O object. |
| 168 | service_type* service_; |
| 169 | |
| 170 | // The underlying implementation of the I/O object. |
| 171 | implementation_type implementation_; |
| 172 | |
| 173 | // The associated executor. |
| 174 | executor_type executor_; |
| 175 | }; |
| 176 | |
| 177 | } // namespace detail |
| 178 | } // namespace asio |
| 179 | } // namespace boost |
| 180 | |
| 181 | #include <boost/asio/detail/pop_options.hpp> |
| 182 | |
| 183 | #endif // BOOST_ASIO_DETAIL_IO_OBJECT_IMPL_HPP |
| 1 | // |
| 2 | // impl/io_context.hpp |
| 3 | // ~~~~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2023 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/detail/completion_handler.hpp> |
| 19 | #include <boost/asio/detail/executor_op.hpp> |
| 20 | #include <boost/asio/detail/fenced_block.hpp> |
| 21 | #include <boost/asio/detail/handler_type_requirements.hpp> |
| 22 | #include <boost/asio/detail/non_const_lvalue.hpp> |
| 23 | #include <boost/asio/detail/service_registry.hpp> |
| 24 | #include <boost/asio/detail/throw_error.hpp> |
| 25 | #include <boost/asio/detail/type_traits.hpp> |
| 26 | |
| 27 | #include <boost/asio/detail/push_options.hpp> |
| 28 | |
| 29 | namespace boost { |
| 30 | namespace asio { |
| 31 | |
| 32 | #if !defined(GENERATING_DOCUMENTATION) |
| 33 | |
| 34 | template <typename Service> |
| 35 | inline Service& use_service(io_context& ioc) |
| 36 | { |
| 37 | // Check that Service meets the necessary type requirements. |
| 38 | (void)static_cast<execution_context::service*>(static_cast<Service*>(0)); |
| 39 | (void)static_cast<const execution_context::id*>(&Service::id); |
| 40 | |
| 41 | return ioc.service_registry_->template use_service<Service>(ioc); |
| 42 | } |
| 43 | |
| 44 | template <> |
| 45 | inline detail::io_context_impl& use_service<detail::io_context_impl>( |
| 46 | io_context& ioc) |
| 47 | { |
| 48 | return ioc.impl_; |
| 49 | } |
| 50 | |
| 51 | #endif // !defined(GENERATING_DOCUMENTATION) |
| 52 | |
| 53 | inline io_context::executor_type |
| 54 | io_context::get_executor() BOOST_ASIO_NOEXCEPTnoexcept |
| 55 | { |
| 56 | return executor_type(*this); |
| 57 | } |
| 58 | |
| 59 | #if defined(BOOST_ASIO_HAS_CHRONO1) |
| 60 | |
| 61 | template <typename Rep, typename Period> |
| 62 | std::size_t io_context::run_for( |
| 63 | const chrono::duration<Rep, Period>& rel_time) |
| 64 | { |
| 65 | return this->run_until(chrono::steady_clock::now() + rel_time); |
| 66 | } |
| 67 | |
| 68 | template <typename Clock, typename Duration> |
| 69 | std::size_t io_context::run_until( |
| 70 | const chrono::time_point<Clock, Duration>& abs_time) |
| 71 | { |
| 72 | std::size_t n = 0; |
| 73 | while (this->run_one_until(abs_time)) |
| 74 | if (n != (std::numeric_limits<std::size_t>::max)()) |
| 75 | ++n; |
| 76 | return n; |
| 77 | } |
| 78 | |
| 79 | template <typename Rep, typename Period> |
| 80 | std::size_t io_context::run_one_for( |
| 81 | const chrono::duration<Rep, Period>& rel_time) |
| 82 | { |
| 83 | return this->run_one_until(chrono::steady_clock::now() + rel_time); |
| 84 | } |
| 85 | |
| 86 | template <typename Clock, typename Duration> |
| 87 | std::size_t io_context::run_one_until( |
| 88 | const chrono::time_point<Clock, Duration>& abs_time) |
| 89 | { |
| 90 | typename Clock::time_point now = Clock::now(); |
| 91 | while (now < abs_time) |
| 92 | { |
| 93 | typename Clock::duration rel_time = abs_time - now; |
| 94 | if (rel_time > chrono::seconds(1)) |
| 95 | rel_time = chrono::seconds(1); |
| 96 | |
| 97 | boost::system::error_code ec; |
| 98 | std::size_t s = impl_.wait_one( |
| 99 | static_cast<long>(chrono::duration_cast< |
| 100 | chrono::microseconds>(rel_time).count()), ec); |
| 101 | boost::asio::detail::throw_error(ec); |
| 102 | |
| 103 | if (s || impl_.stopped()) |
| 104 | return s; |
| 105 | |
| 106 | now = Clock::now(); |
| 107 | } |
| 108 | |
| 109 | return 0; |
| 110 | } |
| 111 | |
| 112 | #endif // defined(BOOST_ASIO_HAS_CHRONO) |
| 113 | |
| 114 | #if !defined(BOOST_ASIO_NO_DEPRECATED) |
| 115 | |
| 116 | inline void io_context::reset() |
| 117 | { |
| 118 | restart(); |
| 119 | } |
| 120 | |
| 121 | struct io_context::initiate_dispatch |
| 122 | { |
| 123 | template <typename LegacyCompletionHandler> |
| 124 | void operator()(BOOST_ASIO_MOVE_ARG(LegacyCompletionHandler)LegacyCompletionHandler&& handler, |
| 125 | io_context* self) const |
| 126 | { |
| 127 | // If you get an error on the following line it means that your handler does |
| 128 | // not meet the documented type requirements for a LegacyCompletionHandler. |
| 129 | BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK(typedef typename ::boost::asio::async_result< typename ::boost ::asio::decay<LegacyCompletionHandler>::type, void()> ::completion_handler_type asio_true_handler_type; static_assert (sizeof(boost::asio::detail::zero_arg_copyable_handler_test( boost ::asio::detail::clvref< asio_true_handler_type>(), 0)) == 1, "CompletionHandler type requirements not met"); typedef boost ::asio::detail::handler_type_requirements< sizeof( boost:: asio::detail::argbyv( boost::asio::detail::clvref< asio_true_handler_type >())) + sizeof( boost::asio::detail::rorlvref< asio_true_handler_type >()(), char(0))> __attribute__((__unused__)) |
| 130 | LegacyCompletionHandler, handler)typedef typename ::boost::asio::async_result< typename ::boost ::asio::decay<LegacyCompletionHandler>::type, void()> ::completion_handler_type asio_true_handler_type; static_assert (sizeof(boost::asio::detail::zero_arg_copyable_handler_test( boost ::asio::detail::clvref< asio_true_handler_type>(), 0)) == 1, "CompletionHandler type requirements not met"); typedef boost ::asio::detail::handler_type_requirements< sizeof( boost:: asio::detail::argbyv( boost::asio::detail::clvref< asio_true_handler_type >())) + sizeof( boost::asio::detail::rorlvref< asio_true_handler_type >()(), char(0))> __attribute__((__unused__)) type_check; |
| 131 | |
| 132 | detail::non_const_lvalue<LegacyCompletionHandler> handler2(handler); |
| 133 | if (self->impl_.can_dispatch()) |
| 134 | { |
| 135 | detail::fenced_block b(detail::fenced_block::full); |
| 136 | boost_asio_handler_invoke_helpers::invoke( |
| 137 | handler2.value, handler2.value); |
| 138 | } |
| 139 | else |
| 140 | { |
| 141 | // Allocate and construct an operation to wrap the handler. |
| 142 | typedef detail::completion_handler< |
| 143 | typename decay<LegacyCompletionHandler>::type, executor_type> op; |
| 144 | typename op::ptr p = { detail::addressof(handler2.value), |
| 145 | op::ptr::allocate(handler2.value), 0 }; |
| 146 | p.p = new (p.v) op(handler2.value, self->get_executor()); |
| 147 | |
| 148 | BOOST_ASIO_HANDLER_CREATION((*self, *p.p,(void)0 |
| 149 | "io_context", self, 0, "dispatch"))(void)0; |
| 150 | |
| 151 | self->impl_.do_dispatch(p.p); |
| 152 | p.v = p.p = 0; |
| 153 | } |
| 154 | } |
| 155 | }; |
| 156 | |
| 157 | template <typename LegacyCompletionHandler> |
| 158 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(LegacyCompletionHandler, void ())auto |
| 159 | io_context::dispatch(BOOST_ASIO_MOVE_ARG(LegacyCompletionHandler)LegacyCompletionHandler&& handler) |
| 160 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( |
| 161 | async_initiate<LegacyCompletionHandler, void ()>( |
| 162 | declval<initiate_dispatch>(), handler, this))) |
| 163 | { |
| 164 | return async_initiate<LegacyCompletionHandler, void ()>( |
| 165 | initiate_dispatch(), handler, this); |
| 166 | } |
| 167 | |
| 168 | struct io_context::initiate_post |
| 169 | { |
| 170 | template <typename LegacyCompletionHandler> |
| 171 | void operator()(BOOST_ASIO_MOVE_ARG(LegacyCompletionHandler)LegacyCompletionHandler&& handler, |
| 172 | io_context* self) const |
| 173 | { |
| 174 | // If you get an error on the following line it means that your handler does |
| 175 | // not meet the documented type requirements for a LegacyCompletionHandler. |
| 176 | BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK(typedef typename ::boost::asio::async_result< typename ::boost ::asio::decay<LegacyCompletionHandler>::type, void()> ::completion_handler_type asio_true_handler_type; static_assert (sizeof(boost::asio::detail::zero_arg_copyable_handler_test( boost ::asio::detail::clvref< asio_true_handler_type>(), 0)) == 1, "CompletionHandler type requirements not met"); typedef boost ::asio::detail::handler_type_requirements< sizeof( boost:: asio::detail::argbyv( boost::asio::detail::clvref< asio_true_handler_type >())) + sizeof( boost::asio::detail::rorlvref< asio_true_handler_type >()(), char(0))> __attribute__((__unused__)) |
| 177 | LegacyCompletionHandler, handler)typedef typename ::boost::asio::async_result< typename ::boost ::asio::decay<LegacyCompletionHandler>::type, void()> ::completion_handler_type asio_true_handler_type; static_assert (sizeof(boost::asio::detail::zero_arg_copyable_handler_test( boost ::asio::detail::clvref< asio_true_handler_type>(), 0)) == 1, "CompletionHandler type requirements not met"); typedef boost ::asio::detail::handler_type_requirements< sizeof( boost:: asio::detail::argbyv( boost::asio::detail::clvref< asio_true_handler_type >())) + sizeof( boost::asio::detail::rorlvref< asio_true_handler_type >()(), char(0))> __attribute__((__unused__)) type_check; |
| 178 | |
| 179 | detail::non_const_lvalue<LegacyCompletionHandler> handler2(handler); |
| 180 | |
| 181 | bool is_continuation = |
| 182 | boost_asio_handler_cont_helpers::is_continuation(handler2.value); |
| 183 | |
| 184 | // Allocate and construct an operation to wrap the handler. |
| 185 | typedef detail::completion_handler< |
| 186 | typename decay<LegacyCompletionHandler>::type, executor_type> op; |
| 187 | typename op::ptr p = { detail::addressof(handler2.value), |
| 188 | op::ptr::allocate(handler2.value), 0 }; |
| 189 | p.p = new (p.v) op(handler2.value, self->get_executor()); |
| 190 | |
| 191 | BOOST_ASIO_HANDLER_CREATION((*self, *p.p,(void)0 |
| 192 | "io_context", self, 0, "post"))(void)0; |
| 193 | |
| 194 | self->impl_.post_immediate_completion(p.p, is_continuation); |
| 195 | p.v = p.p = 0; |
| 196 | } |
| 197 | }; |
| 198 | |
| 199 | template <typename LegacyCompletionHandler> |
| 200 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(LegacyCompletionHandler, void ())auto |
| 201 | io_context::post(BOOST_ASIO_MOVE_ARG(LegacyCompletionHandler)LegacyCompletionHandler&& handler) |
| 202 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( |
| 203 | async_initiate<LegacyCompletionHandler, void ()>( |
| 204 | declval<initiate_post>(), handler, this))) |
| 205 | { |
| 206 | return async_initiate<LegacyCompletionHandler, void ()>( |
| 207 | initiate_post(), handler, this); |
| 208 | } |
| 209 | |
| 210 | template <typename Handler> |
| 211 | #if defined(GENERATING_DOCUMENTATION) |
| 212 | unspecified |
| 213 | #else |
| 214 | inline detail::wrapped_handler<io_context&, Handler> |
| 215 | #endif |
| 216 | io_context::wrap(Handler handler) |
| 217 | { |
| 218 | return detail::wrapped_handler<io_context&, Handler>(*this, handler); |
| 219 | } |
| 220 | |
| 221 | #endif // !defined(BOOST_ASIO_NO_DEPRECATED) |
| 222 | |
| 223 | template <typename Allocator, uintptr_t Bits> |
| 224 | io_context::basic_executor_type<Allocator, Bits>& |
| 225 | io_context::basic_executor_type<Allocator, Bits>::operator=( |
| 226 | const basic_executor_type& other) BOOST_ASIO_NOEXCEPTnoexcept |
| 227 | { |
| 228 | if (this != &other) |
| 229 | { |
| 230 | static_cast<Allocator&>(*this) = static_cast<const Allocator&>(other); |
| 231 | io_context* old_io_context = context_ptr(); |
| 232 | target_ = other.target_; |
| 233 | if (Bits & outstanding_work_tracked) |
| 234 | { |
| 235 | if (context_ptr()) |
| 236 | context_ptr()->impl_.work_started(); |
| 237 | if (old_io_context) |
| 238 | old_io_context->impl_.work_finished(); |
| 239 | } |
| 240 | } |
| 241 | return *this; |
| 242 | } |
| 243 | |
| 244 | #if defined(BOOST_ASIO_HAS_MOVE1) |
| 245 | template <typename Allocator, uintptr_t Bits> |
| 246 | io_context::basic_executor_type<Allocator, Bits>& |
| 247 | io_context::basic_executor_type<Allocator, Bits>::operator=( |
| 248 | basic_executor_type&& other) BOOST_ASIO_NOEXCEPTnoexcept |
| 249 | { |
| 250 | if (this != &other) |
| 251 | { |
| 252 | static_cast<Allocator&>(*this) = static_cast<Allocator&&>(other); |
| 253 | io_context* old_io_context = context_ptr(); |
| 254 | target_ = other.target_; |
| 255 | if (Bits & outstanding_work_tracked) |
| 256 | { |
| 257 | other.target_ = 0; |
| 258 | if (old_io_context) |
| 259 | old_io_context->impl_.work_finished(); |
| 260 | } |
| 261 | } |
| 262 | return *this; |
| 263 | } |
| 264 | #endif // defined(BOOST_ASIO_HAS_MOVE) |
| 265 | |
| 266 | template <typename Allocator, uintptr_t Bits> |
| 267 | inline bool io_context::basic_executor_type<Allocator, |
| 268 | Bits>::running_in_this_thread() const BOOST_ASIO_NOEXCEPTnoexcept |
| 269 | { |
| 270 | return context_ptr()->impl_.can_dispatch(); |
| 271 | } |
| 272 | |
| 273 | template <typename Allocator, uintptr_t Bits> |
| 274 | template <typename Function> |
| 275 | void io_context::basic_executor_type<Allocator, Bits>::execute( |
| 276 | BOOST_ASIO_MOVE_ARG(Function)Function&& f) const |
| 277 | { |
| 278 | typedef typename decay<Function>::type function_type; |
| 279 | |
| 280 | // Invoke immediately if the blocking.possibly property is enabled and we are |
| 281 | // already inside the thread pool. |
| 282 | if ((bits() & blocking_never) == 0 && context_ptr()->impl_.can_dispatch()) |
| 283 | { |
| 284 | // Make a local, non-const copy of the function. |
| 285 | function_type tmp(BOOST_ASIO_MOVE_CAST(Function)static_cast<Function&&>(f)); |
| 286 | |
| 287 | #if defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR1) \ |
| 288 | && !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 289 | try |
| 290 | { |
| 291 | #endif // defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR) |
| 292 | // && !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 293 | detail::fenced_block b(detail::fenced_block::full); |
| 294 | boost_asio_handler_invoke_helpers::invoke(tmp, tmp); |
| 295 | return; |
| 296 | #if defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR1) \ |
| 297 | && !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 298 | } |
| 299 | catch (...) |
| 300 | { |
| 301 | context_ptr()->impl_.capture_current_exception(); |
| 302 | return; |
| 303 | } |
| 304 | #endif // defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR) |
| 305 | // && !defined(BOOST_ASIO_NO_EXCEPTIONS) |
| 306 | } |
| 307 | |
| 308 | // Allocate and construct an operation to wrap the function. |
| 309 | typedef detail::executor_op<function_type, Allocator, detail::operation> op; |
| 310 | typename op::ptr p = { |
| 311 | detail::addressof(static_cast<const Allocator&>(*this)), |
| 312 | op::ptr::allocate(static_cast<const Allocator&>(*this)), 0 }; |
| 313 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)static_cast<Function&&>(f), |
| 314 | static_cast<const Allocator&>(*this)); |
| 315 | |
| 316 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p,(void)0 |
| 317 | "io_context", context_ptr(), 0, "execute"))(void)0; |
| 318 | |
| 319 | context_ptr()->impl_.post_immediate_completion(p.p, |
| 320 | (bits() & relationship_continuation) != 0); |
| 321 | p.v = p.p = 0; |
| 322 | } |
| 323 | |
| 324 | #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
| 325 | template <typename Allocator, uintptr_t Bits> |
| 326 | inline io_context& io_context::basic_executor_type< |
| 327 | Allocator, Bits>::context() const BOOST_ASIO_NOEXCEPTnoexcept |
| 328 | { |
| 329 | return *context_ptr(); |
| 330 | } |
| 331 | |
| 332 | template <typename Allocator, uintptr_t Bits> |
| 333 | inline void io_context::basic_executor_type<Allocator, |
| 334 | Bits>::on_work_started() const BOOST_ASIO_NOEXCEPTnoexcept |
| 335 | { |
| 336 | context_ptr()->impl_.work_started(); |
| 337 | } |
| 338 | |
| 339 | template <typename Allocator, uintptr_t Bits> |
| 340 | inline void io_context::basic_executor_type<Allocator, |
| 341 | Bits>::on_work_finished() const BOOST_ASIO_NOEXCEPTnoexcept |
| 342 | { |
| 343 | context_ptr()->impl_.work_finished(); |
| 344 | } |
| 345 | |
| 346 | template <typename Allocator, uintptr_t Bits> |
| 347 | template <typename Function, typename OtherAllocator> |
| 348 | void io_context::basic_executor_type<Allocator, Bits>::dispatch( |
| 349 | BOOST_ASIO_MOVE_ARG(Function)Function&& f, const OtherAllocator& a) const |
| 350 | { |
| 351 | typedef typename decay<Function>::type function_type; |
| 352 | |
| 353 | // Invoke immediately if we are already inside the thread pool. |
| 354 | if (context_ptr()->impl_.can_dispatch()) |
| 355 | { |
| 356 | // Make a local, non-const copy of the function. |
| 357 | function_type tmp(BOOST_ASIO_MOVE_CAST(Function)static_cast<Function&&>(f)); |
| 358 | |
| 359 | detail::fenced_block b(detail::fenced_block::full); |
| 360 | boost_asio_handler_invoke_helpers::invoke(tmp, tmp); |
| 361 | return; |
| 362 | } |
| 363 | |
| 364 | // Allocate and construct an operation to wrap the function. |
| 365 | typedef detail::executor_op<function_type, |
| 366 | OtherAllocator, detail::operation> op; |
| 367 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
| 368 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)static_cast<Function&&>(f), a); |
| 369 | |
| 370 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p,(void)0 |
| 371 | "io_context", context_ptr(), 0, "dispatch"))(void)0; |
| 372 | |
| 373 | context_ptr()->impl_.post_immediate_completion(p.p, false); |
| 374 | p.v = p.p = 0; |
| 375 | } |
| 376 | |
| 377 | template <typename Allocator, uintptr_t Bits> |
| 378 | template <typename Function, typename OtherAllocator> |
| 379 | void io_context::basic_executor_type<Allocator, Bits>::post( |
| 380 | BOOST_ASIO_MOVE_ARG(Function)Function&& f, const OtherAllocator& a) const |
| 381 | { |
| 382 | typedef typename decay<Function>::type function_type; |
| 383 | |
| 384 | // Allocate and construct an operation to wrap the function. |
| 385 | typedef detail::executor_op<function_type, |
| 386 | OtherAllocator, detail::operation> op; |
| 387 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
| 388 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)static_cast<Function&&>(f), a); |
| 389 | |
| 390 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p,(void)0 |
| 391 | "io_context", context_ptr(), 0, "post"))(void)0; |
| 392 | |
| 393 | context_ptr()->impl_.post_immediate_completion(p.p, false); |
| 394 | p.v = p.p = 0; |
| 395 | } |
| 396 | |
| 397 | template <typename Allocator, uintptr_t Bits> |
| 398 | template <typename Function, typename OtherAllocator> |
| 399 | void io_context::basic_executor_type<Allocator, Bits>::defer( |
| 400 | BOOST_ASIO_MOVE_ARG(Function)Function&& f, const OtherAllocator& a) const |
| 401 | { |
| 402 | typedef typename decay<Function>::type function_type; |
| 403 | |
| 404 | // Allocate and construct an operation to wrap the function. |
| 405 | typedef detail::executor_op<function_type, |
| 406 | OtherAllocator, detail::operation> op; |
| 407 | typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 }; |
| 408 | p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)static_cast<Function&&>(f), a); |
| 409 | |
| 410 | BOOST_ASIO_HANDLER_CREATION((*context_ptr(), *p.p,(void)0 |
| 411 | "io_context", context_ptr(), 0, "defer"))(void)0; |
| 412 | |
| 413 | context_ptr()->impl_.post_immediate_completion(p.p, true); |
| 414 | p.v = p.p = 0; |
| 415 | } |
| 416 | #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
| 417 | |
| 418 | #if !defined(BOOST_ASIO_NO_DEPRECATED) |
| 419 | inline io_context::work::work(boost::asio::io_context& io_context) |
| 420 | : io_context_impl_(io_context.impl_) |
| 421 | { |
| 422 | io_context_impl_.work_started(); |
| 423 | } |
| 424 | |
| 425 | inline io_context::work::work(const work& other) |
| 426 | : io_context_impl_(other.io_context_impl_) |
| 427 | { |
| 428 | io_context_impl_.work_started(); |
| 429 | } |
| 430 | |
| 431 | inline io_context::work::~work() |
| 432 | { |
| 433 | io_context_impl_.work_finished(); |
| 434 | } |
| 435 | |
| 436 | inline boost::asio::io_context& io_context::work::get_io_context() |
| 437 | { |
| 438 | return static_cast<boost::asio::io_context&>(io_context_impl_.context()); |
| 439 | } |
| 440 | #endif // !defined(BOOST_ASIO_NO_DEPRECATED) |
| 441 | |
| 442 | inline boost::asio::io_context& io_context::service::get_io_context() |
| 443 | { |
| 444 | return static_cast<boost::asio::io_context&>(context()); |
| 445 | } |
| 446 | |
| 447 | } // namespace asio |
| 448 | } // namespace boost |
| 449 | |
| 450 | #include <boost/asio/detail/pop_options.hpp> |
| 451 | |
| 452 | #endif // BOOST_ASIO_IMPL_IO_CONTEXT_HPP |
| 1 | // |
| 2 | // detail/impl/service_registry.hpp |
| 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2023 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> |
| 43 | void service_registry::add_service(Service* new_service) |
| 44 | { |
| 45 | execution_context::service::key key; |
| 46 | init_key<Service>(key, 0); |
| 47 | return do_add_service(key, new_service); |
| 48 | } |
| 49 | |
| 50 | template <typename Service> |
| 51 | bool service_registry::has_service() const |
| 52 | { |
| 53 | execution_context::service::key key; |
| 54 | init_key<Service>(key, 0); |
| 55 | return do_has_service(key); |
| 56 | } |
| 57 | |
| 58 | template <typename Service> |
| 59 | inline void service_registry::init_key( |
| 60 | execution_context::service::key& key, ...) |
| 61 | { |
| 62 | init_key_from_id(key, Service::id); |
| 63 | } |
| 64 | |
| 65 | #if !defined(BOOST_ASIO_NO_TYPEID) |
| 66 | template <typename Service> |
| 67 | void service_registry::init_key(execution_context::service::key& key, |
| 68 | typename enable_if< |
| 69 | is_base_of<typename Service::key_type, Service>::value>::type*) |
| 70 | { |
| 71 | key.type_info_ = &typeid(typeid_wrapper<Service>); |
| 72 | key.id_ = 0; |
| 73 | } |
| 74 | |
| 75 | template <typename Service> |
| 76 | void service_registry::init_key_from_id(execution_context::service::key& key, |
| 77 | const service_id<Service>& /*id*/) |
| 78 | { |
| 79 | key.type_info_ = &typeid(typeid_wrapper<Service>); |
| 80 | key.id_ = 0; |
| 81 | } |
| 82 | #endif // !defined(BOOST_ASIO_NO_TYPEID) |
| 83 | |
| 84 | template <typename Service, typename Owner> |
| 85 | execution_context::service* service_registry::create(void* owner) |
| 86 | { |
| 87 | return new Service(*static_cast<Owner*>(owner)); |
| 88 | } |
| 89 | |
| 90 | } // namespace detail |
| 91 | } // namespace asio |
| 92 | } // namespace boost |
| 93 | |
| 94 | #include <boost/asio/detail/pop_options.hpp> |
| 95 | |
| 96 | #endif // BOOST_ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP |
| 1 | // |
| 2 | // detail/impl/service_registry.ipp |
| 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2023 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 | 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(execution_context::service* service) |
| 109 | { |
| 110 | delete service; |
| 111 | } |
| 112 | |
| 113 | execution_context::service* service_registry::do_use_service( |
| 114 | const execution_context::service::key& key, |
| 115 | factory_type factory, void* owner) |
| 116 | { |
| 117 | boost::asio::detail::mutex::scoped_lock lock(mutex_); |
| 118 | |
| 119 | // First see if there is an existing service object with the given key. |
| 120 | execution_context::service* service = first_service_; |
| 121 | while (service) |
| 122 | { |
| 123 | if (keys_match(service->key_, key)) |
| 124 | return service; |
| 125 | service = service->next_; |
| 126 | } |
| 127 | |
| 128 | // Create a new service object. The service registry's mutex is not locked |
| 129 | // at this time to allow for nested calls into this function from the new |
| 130 | // service's constructor. |
| 131 | lock.unlock(); |
| 132 | auto_service_ptr new_service = { factory(owner) }; |
| 133 | new_service.ptr_->key_ = key; |
| 134 | lock.lock(); |
| 135 | |
| 136 | // Check that nobody else created another service object of the same type |
| 137 | // while the lock was released. |
| 138 | service = first_service_; |
| 139 | while (service) |
| 140 | { |
| 141 | if (keys_match(service->key_, key)) |
| 142 | return service; |
| 143 | service = service->next_; |
| 144 | } |
| 145 | |
| 146 | // Service was successfully initialised, pass ownership to registry. |
| 147 | new_service.ptr_->next_ = first_service_; |
| 148 | first_service_ = new_service.ptr_; |
| 149 | new_service.ptr_ = 0; |
| 150 | return first_service_; |
| 151 | } |
| 152 | |
| 153 | void service_registry::do_add_service( |
| 154 | const execution_context::service::key& key, |
| 155 | execution_context::service* new_service) |
| 156 | { |
| 157 | if (&owner_ != &new_service->context()) |
| 158 | boost::asio::detail::throw_exception(invalid_service_owner()); |
| 159 | |
| 160 | boost::asio::detail::mutex::scoped_lock lock(mutex_); |
| 161 | |
| 162 | // Check if there is an existing service object with the given key. |
| 163 | execution_context::service* service = first_service_; |
| 164 | while (service) |
| 165 | { |
| 166 | if (keys_match(service->key_, key)) |
| 167 | boost::asio::detail::throw_exception(service_already_exists()); |
| 168 | service = service->next_; |
| 169 | } |
| 170 | |
| 171 | // Take ownership of the service object. |
| 172 | new_service->key_ = key; |
| 173 | new_service->next_ = first_service_; |
| 174 | first_service_ = new_service; |
| 175 | } |
| 176 | |
| 177 | bool service_registry::do_has_service( |
| 178 | const execution_context::service::key& key) const |
| 179 | { |
| 180 | boost::asio::detail::mutex::scoped_lock lock(mutex_); |
| 181 | |
| 182 | execution_context::service* service = first_service_; |
| 183 | while (service) |
| 184 | { |
| 185 | if (keys_match(service->key_, key)) |
| 186 | return true; |
| 187 | service = service->next_; |
| 188 | } |
| 189 | |
| 190 | return false; |
| 191 | } |
| 192 | |
| 193 | } // namespace detail |
| 194 | } // namespace asio |
| 195 | } // namespace boost |
| 196 | |
| 197 | #include <boost/asio/detail/pop_options.hpp> |
| 198 | |
| 199 | #endif // BOOST_ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP |
| 1 | // |
| 2 | // detail/reactive_socket_service.hpp |
| 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2023 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) BOOST_ASIO_NOEXCEPTnoexcept |
| 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 | typename associated_cancellation_slot<Handler>::type 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, &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 | typename associated_cancellation_slot<Handler>::type 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, &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 | typename associated_cancellation_slot<Handler>::type 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, &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 | typename associated_cancellation_slot<Handler>::type 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, &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 | typename associated_cancellation_slot<Handler>::type 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 | #if defined(BOOST_ASIO_HAS_MOVE1) |
| 546 | // Start an asynchronous accept. The peer_endpoint object must be valid until |
| 547 | // the accept's handler is invoked. |
| 548 | template <typename PeerIoExecutor, typename Handler, typename IoExecutor> |
| 549 | void async_move_accept(implementation_type& impl, |
| 550 | const PeerIoExecutor& peer_io_ex, endpoint_type* peer_endpoint, |
| 551 | Handler& handler, const IoExecutor& io_ex) |
| 552 | { |
| 553 | bool is_continuation = |
| 554 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 555 | |
| 556 | typename associated_cancellation_slot<Handler>::type slot |
| 557 | = boost::asio::get_associated_cancellation_slot(handler); |
| 558 | |
| 559 | // Allocate and construct an operation to wrap the handler. |
| 560 | typedef reactive_socket_move_accept_op<Protocol, |
| 561 | PeerIoExecutor, Handler, IoExecutor> op; |
| 562 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 563 | op::ptr::allocate(handler), 0 }; |
| 564 | p.p = new (p.v) op(success_ec_, peer_io_ex, impl.socket_, |
| 565 | impl.state_, impl.protocol_, peer_endpoint, handler, io_ex); |
| 566 | |
| 567 | // Optionally register for per-operation cancellation. |
| 568 | if (slot.is_connected()) |
| 569 | { |
| 570 | p.p->cancellation_key_ = |
| 571 | &slot.template emplace<reactor_op_cancellation>( |
| 572 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 573 | } |
| 574 | |
| 575 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 576 | &impl, impl.socket_, "async_accept"))(void)0; |
| 577 | |
| 578 | start_accept_op(impl, p.p, is_continuation, false, &io_ex, 0); |
| 579 | p.v = p.p = 0; |
| 580 | } |
| 581 | #endif // defined(BOOST_ASIO_HAS_MOVE) |
| 582 | |
| 583 | // Connect the socket to the specified endpoint. |
| 584 | boost::system::error_code connect(implementation_type& impl, |
| 585 | const endpoint_type& peer_endpoint, boost::system::error_code& ec) |
| 586 | { |
| 587 | socket_ops::sync_connect(impl.socket_, |
| 588 | peer_endpoint.data(), peer_endpoint.size(), ec); |
| 589 | 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); |
| 590 | return ec; |
| 591 | } |
| 592 | |
| 593 | // Start an asynchronous connect. |
| 594 | template <typename Handler, typename IoExecutor> |
| 595 | void async_connect(implementation_type& impl, |
| 596 | const endpoint_type& peer_endpoint, |
| 597 | Handler& handler, const IoExecutor& io_ex) |
| 598 | { |
| 599 | bool is_continuation = |
| 600 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 601 | |
| 602 | typename associated_cancellation_slot<Handler>::type slot |
| 603 | = boost::asio::get_associated_cancellation_slot(handler); |
| 604 | |
| 605 | // Allocate and construct an operation to wrap the handler. |
| 606 | typedef reactive_socket_connect_op<Handler, IoExecutor> op; |
| 607 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 608 | op::ptr::allocate(handler), 0 }; |
| 609 | p.p = new (p.v) op(success_ec_, impl.socket_, handler, io_ex); |
| 610 | |
| 611 | // Optionally register for per-operation cancellation. |
| 612 | if (slot.is_connected()) |
| 613 | { |
| 614 | p.p->cancellation_key_ = |
| 615 | &slot.template emplace<reactor_op_cancellation>( |
| 616 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::connect_op); |
| 617 | } |
| 618 | |
| 619 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 620 | &impl, impl.socket_, "async_connect"))(void)0; |
| 621 | |
| 622 | start_connect_op(impl, p.p, is_continuation, |
| 623 | peer_endpoint.data(), peer_endpoint.size(), &io_ex, 0); |
| 624 | p.v = p.p = 0; |
| 625 | } |
| 626 | }; |
| 627 | |
| 628 | } // namespace detail |
| 629 | } // namespace asio |
| 630 | } // namespace boost |
| 631 | |
| 632 | #include <boost/asio/detail/pop_options.hpp> |
| 633 | |
| 634 | #endif // !defined(BOOST_ASIO_HAS_IOCP) |
| 635 | // && !defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 636 | |
| 637 | #endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP |
| 1 | // |
| 2 | // detail/reactive_socket_service_base.ipp |
| 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2023 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/detail/reactive_socket_service_base.hpp> |
| 25 | |
| 26 | #include <boost/asio/detail/push_options.hpp> |
| 27 | |
| 28 | namespace boost { |
| 29 | namespace asio { |
| 30 | namespace detail { |
| 31 | |
| 32 | reactive_socket_service_base::reactive_socket_service_base( |
| 33 | execution_context& context) |
| 34 | : reactor_(use_service<reactor>(context)) |
| 35 | { |
| 36 | reactor_.init_task(); |
| 37 | } |
| 38 | |
| 39 | void reactive_socket_service_base::base_shutdown() |
| 40 | { |
| 41 | } |
| 42 | |
| 43 | void reactive_socket_service_base::construct( |
| 44 | reactive_socket_service_base::base_implementation_type& impl) |
| 45 | { |
| 46 | impl.socket_ = invalid_socket; |
| 47 | impl.state_ = 0; |
| 48 | impl.reactor_data_ = reactor::per_descriptor_data(); |
| 49 | } |
| 50 | |
| 51 | void reactive_socket_service_base::base_move_construct( |
| 52 | reactive_socket_service_base::base_implementation_type& impl, |
| 53 | reactive_socket_service_base::base_implementation_type& other_impl) |
| 54 | BOOST_ASIO_NOEXCEPTnoexcept |
| 55 | { |
| 56 | impl.socket_ = other_impl.socket_; |
| 57 | other_impl.socket_ = invalid_socket; |
| 58 | |
| 59 | impl.state_ = other_impl.state_; |
| 60 | other_impl.state_ = 0; |
| 61 | |
| 62 | reactor_.move_descriptor(impl.socket_, |
| 63 | impl.reactor_data_, other_impl.reactor_data_); |
| 64 | } |
| 65 | |
| 66 | void reactive_socket_service_base::base_move_assign( |
| 67 | reactive_socket_service_base::base_implementation_type& impl, |
| 68 | reactive_socket_service_base& other_service, |
| 69 | reactive_socket_service_base::base_implementation_type& other_impl) |
| 70 | { |
| 71 | destroy(impl); |
| 72 | |
| 73 | impl.socket_ = other_impl.socket_; |
| 74 | other_impl.socket_ = invalid_socket; |
| 75 | |
| 76 | impl.state_ = other_impl.state_; |
| 77 | other_impl.state_ = 0; |
| 78 | |
| 79 | other_service.reactor_.move_descriptor(impl.socket_, |
| 80 | impl.reactor_data_, other_impl.reactor_data_); |
| 81 | } |
| 82 | |
| 83 | void reactive_socket_service_base::destroy( |
| 84 | reactive_socket_service_base::base_implementation_type& impl) |
| 85 | { |
| 86 | if (impl.socket_ != invalid_socket) |
| 87 | { |
| 88 | BOOST_ASIO_HANDLER_OPERATION((reactor_.context(),(void)0 |
| 89 | "socket", &impl, impl.socket_, "close"))(void)0; |
| 90 | |
| 91 | reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, |
| 92 | (impl.state_ & socket_ops::possible_dup) == 0); |
| 93 | |
| 94 | boost::system::error_code ignored_ec; |
| 95 | socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); |
| 96 | |
| 97 | reactor_.cleanup_descriptor_data(impl.reactor_data_); |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | boost::system::error_code reactive_socket_service_base::close( |
| 102 | reactive_socket_service_base::base_implementation_type& impl, |
| 103 | boost::system::error_code& ec) |
| 104 | { |
| 105 | if (is_open(impl)) |
| 106 | { |
| 107 | BOOST_ASIO_HANDLER_OPERATION((reactor_.context(),(void)0 |
| 108 | "socket", &impl, impl.socket_, "close"))(void)0; |
| 109 | |
| 110 | reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, |
| 111 | (impl.state_ & socket_ops::possible_dup) == 0); |
| 112 | |
| 113 | socket_ops::close(impl.socket_, impl.state_, false, ec); |
| 114 | |
| 115 | reactor_.cleanup_descriptor_data(impl.reactor_data_); |
| 116 | } |
| 117 | else |
| 118 | { |
| 119 | ec = boost::system::error_code(); |
| 120 | } |
| 121 | |
| 122 | // The descriptor is closed by the OS even if close() returns an error. |
| 123 | // |
| 124 | // (Actually, POSIX says the state of the descriptor is unspecified. On |
| 125 | // Linux the descriptor is apparently closed anyway; e.g. see |
| 126 | // http://lkml.org/lkml/2005/9/10/129 |
| 127 | // We'll just have to assume that other OSes follow the same behaviour. The |
| 128 | // known exception is when Windows's closesocket() function fails with |
| 129 | // WSAEWOULDBLOCK, but this case is handled inside socket_ops::close(). |
| 130 | construct(impl); |
| 131 | |
| 132 | return ec; |
| 133 | } |
| 134 | |
| 135 | socket_type reactive_socket_service_base::release( |
| 136 | reactive_socket_service_base::base_implementation_type& impl, |
| 137 | boost::system::error_code& ec) |
| 138 | { |
| 139 | if (!is_open(impl)) |
| 140 | { |
| 141 | ec = boost::asio::error::bad_descriptor; |
| 142 | return invalid_socket; |
| 143 | } |
| 144 | |
| 145 | BOOST_ASIO_HANDLER_OPERATION((reactor_.context(),(void)0 |
| 146 | "socket", &impl, impl.socket_, "release"))(void)0; |
| 147 | |
| 148 | reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_, false); |
| 149 | reactor_.cleanup_descriptor_data(impl.reactor_data_); |
| 150 | socket_type sock = impl.socket_; |
| 151 | construct(impl); |
| 152 | ec = boost::system::error_code(); |
| 153 | return sock; |
| 154 | } |
| 155 | |
| 156 | boost::system::error_code reactive_socket_service_base::cancel( |
| 157 | reactive_socket_service_base::base_implementation_type& impl, |
| 158 | boost::system::error_code& ec) |
| 159 | { |
| 160 | if (!is_open(impl)) |
| 161 | { |
| 162 | ec = boost::asio::error::bad_descriptor; |
| 163 | return ec; |
| 164 | } |
| 165 | |
| 166 | BOOST_ASIO_HANDLER_OPERATION((reactor_.context(),(void)0 |
| 167 | "socket", &impl, impl.socket_, "cancel"))(void)0; |
| 168 | |
| 169 | reactor_.cancel_ops(impl.socket_, impl.reactor_data_); |
| 170 | ec = boost::system::error_code(); |
| 171 | return ec; |
| 172 | } |
| 173 | |
| 174 | boost::system::error_code reactive_socket_service_base::do_open( |
| 175 | reactive_socket_service_base::base_implementation_type& impl, |
| 176 | int af, int type, int protocol, boost::system::error_code& ec) |
| 177 | { |
| 178 | if (is_open(impl)) |
| 179 | { |
| 180 | ec = boost::asio::error::already_open; |
| 181 | return ec; |
| 182 | } |
| 183 | |
| 184 | socket_holder sock(socket_ops::socket(af, type, protocol, ec)); |
| 185 | if (sock.get() == invalid_socket) |
| 186 | return ec; |
| 187 | |
| 188 | if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_)) |
| 189 | { |
| 190 | ec = boost::system::error_code(err, |
| 191 | boost::asio::error::get_system_category()); |
| 192 | return ec; |
| 193 | } |
| 194 | |
| 195 | impl.socket_ = sock.release(); |
| 196 | switch (type) |
| 197 | { |
| 198 | case SOCK_STREAMSOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; |
| 199 | case SOCK_DGRAMSOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; |
| 200 | default: impl.state_ = 0; break; |
| 201 | } |
| 202 | ec = boost::system::error_code(); |
| 203 | return ec; |
| 204 | } |
| 205 | |
| 206 | boost::system::error_code reactive_socket_service_base::do_assign( |
| 207 | reactive_socket_service_base::base_implementation_type& impl, int type, |
| 208 | const reactive_socket_service_base::native_handle_type& native_socket, |
| 209 | boost::system::error_code& ec) |
| 210 | { |
| 211 | if (is_open(impl)) |
| 212 | { |
| 213 | ec = boost::asio::error::already_open; |
| 214 | return ec; |
| 215 | } |
| 216 | |
| 217 | if (int err = reactor_.register_descriptor( |
| 218 | native_socket, impl.reactor_data_)) |
| 219 | { |
| 220 | ec = boost::system::error_code(err, |
| 221 | boost::asio::error::get_system_category()); |
| 222 | return ec; |
| 223 | } |
| 224 | |
| 225 | impl.socket_ = native_socket; |
| 226 | switch (type) |
| 227 | { |
| 228 | case SOCK_STREAMSOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; |
| 229 | case SOCK_DGRAMSOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; |
| 230 | default: impl.state_ = 0; break; |
| 231 | } |
| 232 | impl.state_ |= socket_ops::possible_dup; |
| 233 | ec = boost::system::error_code(); |
| 234 | return ec; |
| 235 | } |
| 236 | |
| 237 | void reactive_socket_service_base::do_start_op( |
| 238 | reactive_socket_service_base::base_implementation_type& impl, int op_type, |
| 239 | reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop, |
| 240 | void (*on_immediate)(operation* op, bool, const void*), |
| 241 | const void* immediate_arg) |
| 242 | { |
| 243 | if (!noop) |
| 244 | { |
| 245 | if ((impl.state_ & socket_ops::non_blocking) |
| 246 | || socket_ops::set_internal_non_blocking( |
| 247 | impl.socket_, impl.state_, true, op->ec_)) |
| 248 | { |
| 249 | reactor_.start_op(op_type, impl.socket_, impl.reactor_data_, op, |
| 250 | is_continuation, is_non_blocking, on_immediate, immediate_arg); |
| 251 | return; |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | on_immediate(op, is_continuation, immediate_arg); |
| 256 | } |
| 257 | |
| 258 | void reactive_socket_service_base::do_start_accept_op( |
| 259 | reactive_socket_service_base::base_implementation_type& impl, |
| 260 | reactor_op* op, bool is_continuation, bool peer_is_open, |
| 261 | void (*on_immediate)(operation* op, bool, const void*), |
| 262 | const void* immediate_arg) |
| 263 | { |
| 264 | if (!peer_is_open) |
| 265 | { |
| 266 | do_start_op(impl, reactor::read_op, op, is_continuation, |
| 267 | true, false, on_immediate, immediate_arg); |
| 268 | } |
| 269 | else |
| 270 | { |
| 271 | op->ec_ = boost::asio::error::already_open; |
| 272 | on_immediate(op, is_continuation, immediate_arg); |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | void reactive_socket_service_base::do_start_connect_op( |
| 277 | reactive_socket_service_base::base_implementation_type& impl, |
| 278 | reactor_op* op, bool is_continuation, const void* addr, size_t addrlen, |
| 279 | void (*on_immediate)(operation* op, bool, const void*), |
| 280 | const void* immediate_arg) |
| 281 | { |
| 282 | if ((impl.state_ & socket_ops::non_blocking) |
| 283 | || socket_ops::set_internal_non_blocking( |
| 284 | impl.socket_, impl.state_, true, op->ec_)) |
| 285 | { |
| 286 | if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) |
| 287 | { |
| 288 | if (op->ec_ == boost::asio::error::in_progress |
| 289 | || op->ec_ == boost::asio::error::would_block) |
| 290 | { |
| 291 | op->ec_ = boost::system::error_code(); |
| 292 | reactor_.start_op(reactor::connect_op, impl.socket_, impl.reactor_data_, |
| 293 | op, is_continuation, false, on_immediate, immediate_arg); |
| 294 | return; |
| 295 | } |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | on_immediate(op, is_continuation, immediate_arg); |
| 300 | } |
| 301 | |
| 302 | } // namespace detail |
| 303 | } // namespace asio |
| 304 | } // namespace boost |
| 305 | |
| 306 | #include <boost/asio/detail/pop_options.hpp> |
| 307 | |
| 308 | #endif // !defined(BOOST_ASIO_HAS_IOCP) |
| 309 | // && !defined(BOOST_ASIO_WINDOWS_RUNTIME) |
| 310 | // && !defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 311 | |
| 312 | #endif // BOOST_ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP |
| 1 | // |
| 2 | // impl/execution_context.hpp |
| 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2023 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 <boost/asio/detail/handler_type_requirements.hpp> |
| 19 | #include <boost/asio/detail/scoped_ptr.hpp> |
| 20 | #include <boost/asio/detail/service_registry.hpp> |
| 21 | |
| 22 | #include <boost/asio/detail/push_options.hpp> |
| 23 | |
| 24 | namespace boost { |
| 25 | namespace asio { |
| 26 | |
| 27 | #if !defined(GENERATING_DOCUMENTATION) |
| 28 | |
| 29 | template <typename Service> |
| 30 | inline Service& use_service(execution_context& e) |
| 31 | { |
| 32 | // Check that Service meets the necessary type requirements. |
| 33 | (void)static_cast<execution_context::service*>(static_cast<Service*>(0)); |
| 34 | |
| 35 | return e.service_registry_->template use_service<Service>(); |
| 36 | } |
| 37 | |
| 38 | #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES1) |
| 39 | |
| 40 | template <typename Service, typename... Args> |
| 41 | Service& make_service(execution_context& e, BOOST_ASIO_MOVE_ARG(Args)Args&&... args) |
| 42 | { |
| 43 | detail::scoped_ptr<Service> svc( |
| 44 | new Service(e, BOOST_ASIO_MOVE_CAST(Args)static_cast<Args&&>(args)...)); |
| 45 | e.service_registry_->template add_service<Service>(svc.get()); |
| 46 | Service& result = *svc; |
| 47 | svc.release(); |
| 48 | return result; |
| 49 | } |
| 50 | |
| 51 | #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) |
| 52 | |
| 53 | template <typename Service> |
| 54 | Service& make_service(execution_context& e) |
| 55 | { |
| 56 | detail::scoped_ptr<Service> svc(new Service(e)); |
| 57 | e.service_registry_->template add_service<Service>(svc.get()); |
| 58 | Service& result = *svc; |
| 59 | svc.release(); |
| 60 | return result; |
| 61 | } |
| 62 | |
| 63 | #define BOOST_ASIO_PRIVATE_MAKE_SERVICE_DEF(n) \ |
| 64 | template <typename Service, BOOST_ASIO_VARIADIC_TPARAMS(n)> \ |
| 65 | Service& make_service(execution_context& e, \ |
| 66 | BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \ |
| 67 | { \ |
| 68 | detail::scoped_ptr<Service> svc( \ |
| 69 | new Service(e, BOOST_ASIO_VARIADIC_MOVE_ARGS(n))); \ |
| 70 | e.service_registry_->template add_service<Service>(svc.get()); \ |
| 71 | Service& result = *svc; \ |
| 72 | svc.release(); \ |
| 73 | return result; \ |
| 74 | } \ |
| 75 | /**/ |
| 76 | BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_MAKE_SERVICE_DEF) |
| 77 | #undef BOOST_ASIO_PRIVATE_MAKE_SERVICE_DEF |
| 78 | |
| 79 | #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) |
| 80 | |
| 81 | template <typename Service> |
| 82 | inline void add_service(execution_context& e, Service* svc) |
| 83 | { |
| 84 | // Check that Service meets the necessary type requirements. |
| 85 | (void)static_cast<execution_context::service*>(static_cast<Service*>(0)); |
| 86 | |
| 87 | e.service_registry_->template add_service<Service>(svc); |
| 88 | } |
| 89 | |
| 90 | template <typename Service> |
| 91 | inline bool has_service(execution_context& e) |
| 92 | { |
| 93 | // Check that Service meets the necessary type requirements. |
| 94 | (void)static_cast<execution_context::service*>(static_cast<Service*>(0)); |
| 95 | |
| 96 | return e.service_registry_->template has_service<Service>(); |
| 97 | } |
| 98 | |
| 99 | #endif // !defined(GENERATING_DOCUMENTATION) |
| 100 | |
| 101 | inline execution_context& execution_context::service::context() |
| 102 | { |
| 103 | return owner_; |
| 104 | } |
| 105 | |
| 106 | } // namespace asio |
| 107 | } // namespace boost |
| 108 | |
| 109 | #include <boost/asio/detail/pop_options.hpp> |
| 110 | |
| 111 | #endif // BOOST_ASIO_IMPL_EXECUTION_CONTEXT_HPP |
| 1 | // |
| 2 | // detail/impl/epoll_reactor.ipp |
| 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2023 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_EPOLL_REACTOR_IPP |
| 12 | #define BOOST_ASIO_DETAIL_IMPL_EPOLL_REACTOR_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_EPOLL1) |
| 21 | |
| 22 | #include <cstddef> |
| 23 | #include <sys/epoll.h> |
| 24 | #include <boost/asio/detail/epoll_reactor.hpp> |
| 25 | #include <boost/asio/detail/scheduler.hpp> |
| 26 | #include <boost/asio/detail/throw_error.hpp> |
| 27 | #include <boost/asio/error.hpp> |
| 28 | |
| 29 | #if defined(BOOST_ASIO_HAS_TIMERFD1) |
| 30 | # include <sys/timerfd.h> |
| 31 | #endif // defined(BOOST_ASIO_HAS_TIMERFD) |
| 32 | |
| 33 | #include <boost/asio/detail/push_options.hpp> |
| 34 | |
| 35 | namespace boost { |
| 36 | namespace asio { |
| 37 | namespace detail { |
| 38 | |
| 39 | epoll_reactor::epoll_reactor(boost::asio::execution_context& ctx) |
| 40 | : execution_context_service_base<epoll_reactor>(ctx), |
| 41 | scheduler_(use_service<scheduler>(ctx)), |
| 42 | mutex_(BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING((((static_cast<unsigned>(scheduler_.concurrency_hint()) & (0xFFFF0000u | 0x2u)) ^ 0xA5100000u) != 0) |
| 43 | REACTOR_REGISTRATION, scheduler_.concurrency_hint())(((static_cast<unsigned>(scheduler_.concurrency_hint()) & (0xFFFF0000u | 0x2u)) ^ 0xA5100000u) != 0)), |
| 44 | interrupter_(), |
| 45 | epoll_fd_(do_epoll_create()), |
| 46 | timer_fd_(do_timerfd_create()), |
| 47 | shutdown_(false), |
| 48 | registered_descriptors_mutex_(mutex_.enabled()) |
| 49 | { |
| 50 | // Add the interrupter's descriptor to epoll. |
| 51 | epoll_event ev = { 0, { 0 } }; |
| 52 | ev.events = EPOLLINEPOLLIN | EPOLLERREPOLLERR | EPOLLETEPOLLET; |
| 53 | ev.data.ptr = &interrupter_; |
| 54 | epoll_ctl(epoll_fd_, EPOLL_CTL_ADD1, interrupter_.read_descriptor(), &ev); |
| 55 | interrupter_.interrupt(); |
| 56 | |
| 57 | // Add the timer descriptor to epoll. |
| 58 | if (timer_fd_ != -1) |
| 59 | { |
| 60 | ev.events = EPOLLINEPOLLIN | EPOLLERREPOLLERR; |
| 61 | ev.data.ptr = &timer_fd_; |
| 62 | epoll_ctl(epoll_fd_, EPOLL_CTL_ADD1, timer_fd_, &ev); |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | epoll_reactor::~epoll_reactor() |
| 67 | { |
| 68 | if (epoll_fd_ != -1) |
| 69 | close(epoll_fd_); |
| 70 | if (timer_fd_ != -1) |
| 71 | close(timer_fd_); |
| 72 | } |
| 73 | |
| 74 | void epoll_reactor::shutdown() |
| 75 | { |
| 76 | mutex::scoped_lock lock(mutex_); |
| 77 | shutdown_ = true; |
| 78 | lock.unlock(); |
| 79 | |
| 80 | op_queue<operation> ops; |
| 81 | |
| 82 | while (descriptor_state* state = registered_descriptors_.first()) |
| 83 | { |
| 84 | for (int i = 0; i < max_ops; ++i) |
| 85 | ops.push(state->op_queue_[i]); |
| 86 | state->shutdown_ = true; |
| 87 | registered_descriptors_.free(state); |
| 88 | } |
| 89 | |
| 90 | timer_queues_.get_all_timers(ops); |
| 91 | |
| 92 | scheduler_.abandon_operations(ops); |
| 93 | } |
| 94 | |
| 95 | void epoll_reactor::notify_fork( |
| 96 | boost::asio::execution_context::fork_event fork_ev) |
| 97 | { |
| 98 | if (fork_ev == boost::asio::execution_context::fork_child) |
| 99 | { |
| 100 | if (epoll_fd_ != -1) |
| 101 | ::close(epoll_fd_); |
| 102 | epoll_fd_ = -1; |
| 103 | epoll_fd_ = do_epoll_create(); |
| 104 | |
| 105 | if (timer_fd_ != -1) |
| 106 | ::close(timer_fd_); |
| 107 | timer_fd_ = -1; |
| 108 | timer_fd_ = do_timerfd_create(); |
| 109 | |
| 110 | interrupter_.recreate(); |
| 111 | |
| 112 | // Add the interrupter's descriptor to epoll. |
| 113 | epoll_event ev = { 0, { 0 } }; |
| 114 | ev.events = EPOLLINEPOLLIN | EPOLLERREPOLLERR | EPOLLETEPOLLET; |
| 115 | ev.data.ptr = &interrupter_; |
| 116 | epoll_ctl(epoll_fd_, EPOLL_CTL_ADD1, interrupter_.read_descriptor(), &ev); |
| 117 | interrupter_.interrupt(); |
| 118 | |
| 119 | // Add the timer descriptor to epoll. |
| 120 | if (timer_fd_ != -1) |
| 121 | { |
| 122 | ev.events = EPOLLINEPOLLIN | EPOLLERREPOLLERR; |
| 123 | ev.data.ptr = &timer_fd_; |
| 124 | epoll_ctl(epoll_fd_, EPOLL_CTL_ADD1, timer_fd_, &ev); |
| 125 | } |
| 126 | |
| 127 | update_timeout(); |
| 128 | |
| 129 | // Re-register all descriptors with epoll. |
| 130 | mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); |
| 131 | for (descriptor_state* state = registered_descriptors_.first(); |
| 132 | state != 0; state = state->next_) |
| 133 | { |
| 134 | ev.events = state->registered_events_; |
| 135 | ev.data.ptr = state; |
| 136 | int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD1, state->descriptor_, &ev); |
| 137 | if (result != 0) |
| 138 | { |
| 139 | boost::system::error_code ec(errno(*__errno_location ()), |
| 140 | boost::asio::error::get_system_category()); |
| 141 | boost::asio::detail::throw_error(ec, "epoll re-registration"); |
| 142 | } |
| 143 | } |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | void epoll_reactor::init_task() |
| 148 | { |
| 149 | scheduler_.init_task(); |
| 150 | } |
| 151 | |
| 152 | int epoll_reactor::register_descriptor(socket_type descriptor, |
| 153 | epoll_reactor::per_descriptor_data& descriptor_data) |
| 154 | { |
| 155 | descriptor_data = allocate_descriptor_state(); |
| 156 | |
| 157 | BOOST_ASIO_HANDLER_REACTOR_REGISTRATION(((void)0 |
| 158 | context(), static_cast<uintmax_t>(descriptor),(void)0 |
| 159 | reinterpret_cast<uintmax_t>(descriptor_data)))(void)0; |
| 160 | |
| 161 | { |
| 162 | mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); |
| 163 | |
| 164 | descriptor_data->reactor_ = this; |
| 165 | descriptor_data->descriptor_ = descriptor; |
| 166 | descriptor_data->shutdown_ = false; |
| 167 | for (int i = 0; i < max_ops; ++i) |
| 168 | descriptor_data->try_speculative_[i] = true; |
| 169 | } |
| 170 | |
| 171 | epoll_event ev = { 0, { 0 } }; |
| 172 | ev.events = EPOLLINEPOLLIN | EPOLLERREPOLLERR | EPOLLHUPEPOLLHUP | EPOLLPRIEPOLLPRI | EPOLLETEPOLLET; |
| 173 | descriptor_data->registered_events_ = ev.events; |
| 174 | ev.data.ptr = descriptor_data; |
| 175 | int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD1, descriptor, &ev); |
| 176 | if (result != 0) |
| 177 | { |
| 178 | if (errno(*__errno_location ()) == EPERM1) |
| 179 | { |
| 180 | // This file descriptor type is not supported by epoll. However, if it is |
| 181 | // a regular file then operations on it will not block. We will allow |
| 182 | // this descriptor to be used and fail later if an operation on it would |
| 183 | // otherwise require a trip through the reactor. |
| 184 | descriptor_data->registered_events_ = 0; |
| 185 | return 0; |
| 186 | } |
| 187 | return errno(*__errno_location ()); |
| 188 | } |
| 189 | |
| 190 | return 0; |
| 191 | } |
| 192 | |
| 193 | int epoll_reactor::register_internal_descriptor( |
| 194 | int op_type, socket_type descriptor, |
| 195 | epoll_reactor::per_descriptor_data& descriptor_data, reactor_op* op) |
| 196 | { |
| 197 | descriptor_data = allocate_descriptor_state(); |
| 198 | |
| 199 | BOOST_ASIO_HANDLER_REACTOR_REGISTRATION(((void)0 |
| 200 | context(), static_cast<uintmax_t>(descriptor),(void)0 |
| 201 | reinterpret_cast<uintmax_t>(descriptor_data)))(void)0; |
| 202 | |
| 203 | { |
| 204 | mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); |
| 205 | |
| 206 | descriptor_data->reactor_ = this; |
| 207 | descriptor_data->descriptor_ = descriptor; |
| 208 | descriptor_data->shutdown_ = false; |
| 209 | descriptor_data->op_queue_[op_type].push(op); |
| 210 | for (int i = 0; i < max_ops; ++i) |
| 211 | descriptor_data->try_speculative_[i] = true; |
| 212 | } |
| 213 | |
| 214 | epoll_event ev = { 0, { 0 } }; |
| 215 | ev.events = EPOLLINEPOLLIN | EPOLLERREPOLLERR | EPOLLHUPEPOLLHUP | EPOLLPRIEPOLLPRI | EPOLLETEPOLLET; |
| 216 | descriptor_data->registered_events_ = ev.events; |
| 217 | ev.data.ptr = descriptor_data; |
| 218 | int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD1, descriptor, &ev); |
| 219 | if (result != 0) |
| 220 | return errno(*__errno_location ()); |
| 221 | |
| 222 | return 0; |
| 223 | } |
| 224 | |
| 225 | void epoll_reactor::move_descriptor(socket_type, |
| 226 | epoll_reactor::per_descriptor_data& target_descriptor_data, |
| 227 | epoll_reactor::per_descriptor_data& source_descriptor_data) |
| 228 | { |
| 229 | target_descriptor_data = source_descriptor_data; |
| 230 | source_descriptor_data = 0; |
| 231 | } |
| 232 | |
| 233 | void epoll_reactor::call_post_immediate_completion( |
| 234 | operation* op, bool is_continuation, const void* self) |
| 235 | { |
| 236 | static_cast<const epoll_reactor*>(self)->post_immediate_completion( |
| 237 | op, is_continuation); |
| 238 | } |
| 239 | |
| 240 | void epoll_reactor::start_op(int op_type, socket_type descriptor, |
| 241 | epoll_reactor::per_descriptor_data& descriptor_data, reactor_op* op, |
| 242 | bool is_continuation, bool allow_speculative, |
| 243 | void (*on_immediate)(operation*, bool, const void*), |
| 244 | const void* immediate_arg) |
| 245 | { |
| 246 | if (!descriptor_data) |
| 247 | { |
| 248 | op->ec_ = boost::asio::error::bad_descriptor; |
| 249 | on_immediate(op, is_continuation, immediate_arg); |
| 250 | return; |
| 251 | } |
| 252 | |
| 253 | mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); |
| 254 | |
| 255 | if (descriptor_data->shutdown_) |
| 256 | { |
| 257 | on_immediate(op, is_continuation, immediate_arg); |
| 258 | return; |
| 259 | } |
| 260 | |
| 261 | if (descriptor_data->op_queue_[op_type].empty()) |
| 262 | { |
| 263 | if (allow_speculative |
| 264 | && (op_type != read_op |
| 265 | || descriptor_data->op_queue_[except_op].empty())) |
| 266 | { |
| 267 | if (descriptor_data->try_speculative_[op_type]) |
| 268 | { |
| 269 | if (reactor_op::status status = op->perform()) |
| 270 | { |
| 271 | if (status == reactor_op::done_and_exhausted) |
| 272 | if (descriptor_data->registered_events_ != 0) |
| 273 | descriptor_data->try_speculative_[op_type] = false; |
| 274 | descriptor_lock.unlock(); |
| 275 | on_immediate(op, is_continuation, immediate_arg); |
| 276 | return; |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | if (descriptor_data->registered_events_ == 0) |
| 281 | { |
| 282 | op->ec_ = boost::asio::error::operation_not_supported; |
| 283 | on_immediate(op, is_continuation, immediate_arg); |
| 284 | return; |
| 285 | } |
| 286 | |
| 287 | if (op_type == write_op) |
| 288 | { |
| 289 | if ((descriptor_data->registered_events_ & EPOLLOUTEPOLLOUT) == 0) |
| 290 | { |
| 291 | epoll_event ev = { 0, { 0 } }; |
| 292 | ev.events = descriptor_data->registered_events_ | EPOLLOUTEPOLLOUT; |
| 293 | ev.data.ptr = descriptor_data; |
| 294 | if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD3, descriptor, &ev) == 0) |
| 295 | { |
| 296 | descriptor_data->registered_events_ |= ev.events; |
| 297 | } |
| 298 | else |
| 299 | { |
| 300 | op->ec_ = boost::system::error_code(errno(*__errno_location ()), |
| 301 | boost::asio::error::get_system_category()); |
| 302 | on_immediate(op, is_continuation, immediate_arg); |
| 303 | return; |
| 304 | } |
| 305 | } |
| 306 | } |
| 307 | } |
| 308 | else if (descriptor_data->registered_events_ == 0) |
| 309 | { |
| 310 | op->ec_ = boost::asio::error::operation_not_supported; |
| 311 | on_immediate(op, is_continuation, immediate_arg); |
| 312 | return; |
| 313 | } |
| 314 | else |
| 315 | { |
| 316 | if (op_type == write_op) |
| 317 | { |
| 318 | descriptor_data->registered_events_ |= EPOLLOUTEPOLLOUT; |
| 319 | } |
| 320 | |
| 321 | epoll_event ev = { 0, { 0 } }; |
| 322 | ev.events = descriptor_data->registered_events_; |
| 323 | ev.data.ptr = descriptor_data; |
| 324 | epoll_ctl(epoll_fd_, EPOLL_CTL_MOD3, descriptor, &ev); |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | descriptor_data->op_queue_[op_type].push(op); |
| 329 | scheduler_.work_started(); |
| 330 | } |
| 331 | |
| 332 | void epoll_reactor::cancel_ops(socket_type, |
| 333 | epoll_reactor::per_descriptor_data& descriptor_data) |
| 334 | { |
| 335 | if (!descriptor_data) |
| 336 | return; |
| 337 | |
| 338 | mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); |
| 339 | |
| 340 | op_queue<operation> ops; |
| 341 | for (int i = 0; i < max_ops; ++i) |
| 342 | { |
| 343 | while (reactor_op* op = descriptor_data->op_queue_[i].front()) |
| 344 | { |
| 345 | op->ec_ = boost::asio::error::operation_aborted; |
| 346 | descriptor_data->op_queue_[i].pop(); |
| 347 | ops.push(op); |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | descriptor_lock.unlock(); |
| 352 | |
| 353 | scheduler_.post_deferred_completions(ops); |
| 354 | } |
| 355 | |
| 356 | void epoll_reactor::cancel_ops_by_key(socket_type, |
| 357 | epoll_reactor::per_descriptor_data& descriptor_data, |
| 358 | int op_type, void* cancellation_key) |
| 359 | { |
| 360 | if (!descriptor_data) |
| 361 | return; |
| 362 | |
| 363 | mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); |
| 364 | |
| 365 | op_queue<operation> ops; |
| 366 | op_queue<reactor_op> other_ops; |
| 367 | while (reactor_op* op = descriptor_data->op_queue_[op_type].front()) |
| 368 | { |
| 369 | descriptor_data->op_queue_[op_type].pop(); |
| 370 | if (op->cancellation_key_ == cancellation_key) |
| 371 | { |
| 372 | op->ec_ = boost::asio::error::operation_aborted; |
| 373 | ops.push(op); |
| 374 | } |
| 375 | else |
| 376 | other_ops.push(op); |
| 377 | } |
| 378 | descriptor_data->op_queue_[op_type].push(other_ops); |
| 379 | |
| 380 | descriptor_lock.unlock(); |
| 381 | |
| 382 | scheduler_.post_deferred_completions(ops); |
| 383 | } |
| 384 | |
| 385 | void epoll_reactor::deregister_descriptor(socket_type descriptor, |
| 386 | epoll_reactor::per_descriptor_data& descriptor_data, bool closing) |
| 387 | { |
| 388 | if (!descriptor_data) |
| 389 | return; |
| 390 | |
| 391 | mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); |
| 392 | |
| 393 | if (!descriptor_data->shutdown_) |
| 394 | { |
| 395 | if (closing) |
| 396 | { |
| 397 | // The descriptor will be automatically removed from the epoll set when |
| 398 | // it is closed. |
| 399 | } |
| 400 | else if (descriptor_data->registered_events_ != 0) |
| 401 | { |
| 402 | epoll_event ev = { 0, { 0 } }; |
| 403 | epoll_ctl(epoll_fd_, EPOLL_CTL_DEL2, descriptor, &ev); |
| 404 | } |
| 405 | |
| 406 | op_queue<operation> ops; |
| 407 | for (int i = 0; i < max_ops; ++i) |
| 408 | { |
| 409 | while (reactor_op* op = descriptor_data->op_queue_[i].front()) |
| 410 | { |
| 411 | op->ec_ = boost::asio::error::operation_aborted; |
| 412 | descriptor_data->op_queue_[i].pop(); |
| 413 | ops.push(op); |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | descriptor_data->descriptor_ = -1; |
| 418 | descriptor_data->shutdown_ = true; |
| 419 | |
| 420 | descriptor_lock.unlock(); |
| 421 | |
| 422 | BOOST_ASIO_HANDLER_REACTOR_DEREGISTRATION(((void)0 |
| 423 | context(), static_cast<uintmax_t>(descriptor),(void)0 |
| 424 | reinterpret_cast<uintmax_t>(descriptor_data)))(void)0; |
| 425 | |
| 426 | scheduler_.post_deferred_completions(ops); |
| 427 | |
| 428 | // Leave descriptor_data set so that it will be freed by the subsequent |
| 429 | // call to cleanup_descriptor_data. |
| 430 | } |
| 431 | else |
| 432 | { |
| 433 | // We are shutting down, so prevent cleanup_descriptor_data from freeing |
| 434 | // the descriptor_data object and let the destructor free it instead. |
| 435 | descriptor_data = 0; |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | void epoll_reactor::deregister_internal_descriptor(socket_type descriptor, |
| 440 | epoll_reactor::per_descriptor_data& descriptor_data) |
| 441 | { |
| 442 | if (!descriptor_data) |
| 443 | return; |
| 444 | |
| 445 | mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); |
| 446 | |
| 447 | if (!descriptor_data->shutdown_) |
| 448 | { |
| 449 | epoll_event ev = { 0, { 0 } }; |
| 450 | epoll_ctl(epoll_fd_, EPOLL_CTL_DEL2, descriptor, &ev); |
| 451 | |
| 452 | op_queue<operation> ops; |
| 453 | for (int i = 0; i < max_ops; ++i) |
| 454 | ops.push(descriptor_data->op_queue_[i]); |
| 455 | |
| 456 | descriptor_data->descriptor_ = -1; |
| 457 | descriptor_data->shutdown_ = true; |
| 458 | |
| 459 | descriptor_lock.unlock(); |
| 460 | |
| 461 | BOOST_ASIO_HANDLER_REACTOR_DEREGISTRATION(((void)0 |
| 462 | context(), static_cast<uintmax_t>(descriptor),(void)0 |
| 463 | reinterpret_cast<uintmax_t>(descriptor_data)))(void)0; |
| 464 | |
| 465 | // Leave descriptor_data set so that it will be freed by the subsequent |
| 466 | // call to cleanup_descriptor_data. |
| 467 | } |
| 468 | else |
| 469 | { |
| 470 | // We are shutting down, so prevent cleanup_descriptor_data from freeing |
| 471 | // the descriptor_data object and let the destructor free it instead. |
| 472 | descriptor_data = 0; |
| 473 | } |
| 474 | } |
| 475 | |
| 476 | void epoll_reactor::cleanup_descriptor_data( |
| 477 | per_descriptor_data& descriptor_data) |
| 478 | { |
| 479 | if (descriptor_data) |
| 480 | { |
| 481 | free_descriptor_state(descriptor_data); |
| 482 | descriptor_data = 0; |
| 483 | } |
| 484 | } |
| 485 | |
| 486 | void epoll_reactor::run(long usec, op_queue<operation>& ops) |
| 487 | { |
| 488 | // This code relies on the fact that the scheduler queues the reactor task |
| 489 | // behind all descriptor operations generated by this function. This means, |
| 490 | // that by the time we reach this point, any previously returned descriptor |
| 491 | // operations have already been dequeued. Therefore it is now safe for us to |
| 492 | // reuse and return them for the scheduler to queue again. |
| 493 | |
| 494 | // Calculate timeout. Check the timer queues only if timerfd is not in use. |
| 495 | int timeout; |
| 496 | if (usec == 0) |
| 497 | timeout = 0; |
| 498 | else |
| 499 | { |
| 500 | timeout = (usec < 0) ? -1 : ((usec - 1) / 1000 + 1); |
| 501 | if (timer_fd_ == -1) |
| 502 | { |
| 503 | mutex::scoped_lock lock(mutex_); |
| 504 | timeout = get_timeout(timeout); |
| 505 | } |
| 506 | } |
| 507 | |
| 508 | // Block on the epoll descriptor. |
| 509 | epoll_event events[128]; |
| 510 | int num_events = epoll_wait(epoll_fd_, events, 128, timeout); |
| 511 | |
| 512 | #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING) |
| 513 | // Trace the waiting events. |
| 514 | for (int i = 0; i < num_events; ++i) |
| 515 | { |
| 516 | void* ptr = events[i].data.ptr; |
| 517 | if (ptr == &interrupter_) |
| 518 | { |
| 519 | // Ignore. |
| 520 | } |
| 521 | # if defined(BOOST_ASIO_HAS_TIMERFD1) |
| 522 | else if (ptr == &timer_fd_) |
| 523 | { |
| 524 | // Ignore. |
| 525 | } |
| 526 | # endif // defined(BOOST_ASIO_HAS_TIMERFD) |
| 527 | else |
| 528 | { |
| 529 | unsigned event_mask = 0; |
| 530 | if ((events[i].events & EPOLLINEPOLLIN) != 0) |
| 531 | event_mask |= BOOST_ASIO_HANDLER_REACTOR_READ_EVENT0; |
| 532 | if ((events[i].events & EPOLLOUTEPOLLOUT)) |
| 533 | event_mask |= BOOST_ASIO_HANDLER_REACTOR_WRITE_EVENT0; |
| 534 | if ((events[i].events & (EPOLLERREPOLLERR | EPOLLHUPEPOLLHUP)) != 0) |
| 535 | event_mask |= BOOST_ASIO_HANDLER_REACTOR_ERROR_EVENT0; |
| 536 | BOOST_ASIO_HANDLER_REACTOR_EVENTS((context(),(void)0 |
| 537 | reinterpret_cast<uintmax_t>(ptr), event_mask))(void)0; |
| 538 | } |
| 539 | } |
| 540 | #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING) |
| 541 | |
| 542 | #if defined(BOOST_ASIO_HAS_TIMERFD1) |
| 543 | bool check_timers = (timer_fd_ == -1); |
| 544 | #else // defined(BOOST_ASIO_HAS_TIMERFD) |
| 545 | bool check_timers = true; |
| 546 | #endif // defined(BOOST_ASIO_HAS_TIMERFD) |
| 547 | |
| 548 | // Dispatch the waiting events. |
| 549 | for (int i = 0; i < num_events; ++i) |
| 550 | { |
| 551 | void* ptr = events[i].data.ptr; |
| 552 | if (ptr == &interrupter_) |
| 553 | { |
| 554 | // No need to reset the interrupter since we're leaving the descriptor |
| 555 | // in a ready-to-read state and relying on edge-triggered notifications |
| 556 | // to make it so that we only get woken up when the descriptor's epoll |
| 557 | // registration is updated. |
| 558 | |
| 559 | #if defined(BOOST_ASIO_HAS_TIMERFD1) |
| 560 | if (timer_fd_ == -1) |
| 561 | check_timers = true; |
| 562 | #else // defined(BOOST_ASIO_HAS_TIMERFD) |
| 563 | check_timers = true; |
| 564 | #endif // defined(BOOST_ASIO_HAS_TIMERFD) |
| 565 | } |
| 566 | #if defined(BOOST_ASIO_HAS_TIMERFD1) |
| 567 | else if (ptr == &timer_fd_) |
| 568 | { |
| 569 | check_timers = true; |
| 570 | } |
| 571 | #endif // defined(BOOST_ASIO_HAS_TIMERFD) |
| 572 | else |
| 573 | { |
| 574 | // The descriptor operation doesn't count as work in and of itself, so we |
| 575 | // don't call work_started() here. This still allows the scheduler to |
| 576 | // stop if the only remaining operations are descriptor operations. |
| 577 | descriptor_state* descriptor_data = static_cast<descriptor_state*>(ptr); |
| 578 | if (!ops.is_enqueued(descriptor_data)) |
| 579 | { |
| 580 | descriptor_data->set_ready_events(events[i].events); |
| 581 | ops.push(descriptor_data); |
| 582 | } |
| 583 | else |
| 584 | { |
| 585 | descriptor_data->add_ready_events(events[i].events); |
| 586 | } |
| 587 | } |
| 588 | } |
| 589 | |
| 590 | if (check_timers) |
| 591 | { |
| 592 | mutex::scoped_lock common_lock(mutex_); |
| 593 | timer_queues_.get_ready_timers(ops); |
| 594 | |
| 595 | #if defined(BOOST_ASIO_HAS_TIMERFD1) |
| 596 | if (timer_fd_ != -1) |
| 597 | { |
| 598 | itimerspec new_timeout; |
| 599 | itimerspec old_timeout; |
| 600 | int flags = get_timeout(new_timeout); |
| 601 | timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); |
| 602 | } |
| 603 | #endif // defined(BOOST_ASIO_HAS_TIMERFD) |
| 604 | } |
| 605 | } |
| 606 | |
| 607 | void epoll_reactor::interrupt() |
| 608 | { |
| 609 | epoll_event ev = { 0, { 0 } }; |
| 610 | ev.events = EPOLLINEPOLLIN | EPOLLERREPOLLERR | EPOLLETEPOLLET; |
| 611 | ev.data.ptr = &interrupter_; |
| 612 | epoll_ctl(epoll_fd_, EPOLL_CTL_MOD3, interrupter_.read_descriptor(), &ev); |
| 613 | } |
| 614 | |
| 615 | int epoll_reactor::do_epoll_create() |
| 616 | { |
| 617 | #if defined(EPOLL_CLOEXECEPOLL_CLOEXEC) |
| 618 | int fd = epoll_create1(EPOLL_CLOEXECEPOLL_CLOEXEC); |
| 619 | #else // defined(EPOLL_CLOEXEC) |
| 620 | int fd = -1; |
| 621 | errno(*__errno_location ()) = EINVAL22; |
| 622 | #endif // defined(EPOLL_CLOEXEC) |
| 623 | |
| 624 | if (fd == -1 && (errno(*__errno_location ()) == EINVAL22 || errno(*__errno_location ()) == ENOSYS38)) |
| 625 | { |
| 626 | fd = epoll_create(epoll_size); |
| 627 | if (fd != -1) |
| 628 | ::fcntl(fd, F_SETFD2, FD_CLOEXEC1); |
| 629 | } |
| 630 | |
| 631 | if (fd == -1) |
| 632 | { |
| 633 | boost::system::error_code ec(errno(*__errno_location ()), |
| 634 | boost::asio::error::get_system_category()); |
| 635 | boost::asio::detail::throw_error(ec, "epoll"); |
| 636 | } |
| 637 | |
| 638 | return fd; |
| 639 | } |
| 640 | |
| 641 | int epoll_reactor::do_timerfd_create() |
| 642 | { |
| 643 | #if defined(BOOST_ASIO_HAS_TIMERFD1) |
| 644 | # if defined(TFD_CLOEXECTFD_CLOEXEC) |
| 645 | int fd = timerfd_create(CLOCK_MONOTONIC1, TFD_CLOEXECTFD_CLOEXEC); |
| 646 | # else // defined(TFD_CLOEXEC) |
| 647 | int fd = -1; |
| 648 | errno(*__errno_location ()) = EINVAL22; |
| 649 | # endif // defined(TFD_CLOEXEC) |
| 650 | |
| 651 | if (fd == -1 && errno(*__errno_location ()) == EINVAL22) |
| 652 | { |
| 653 | fd = timerfd_create(CLOCK_MONOTONIC1, 0); |
| 654 | if (fd != -1) |
| 655 | ::fcntl(fd, F_SETFD2, FD_CLOEXEC1); |
| 656 | } |
| 657 | |
| 658 | return fd; |
| 659 | #else // defined(BOOST_ASIO_HAS_TIMERFD) |
| 660 | return -1; |
| 661 | #endif // defined(BOOST_ASIO_HAS_TIMERFD) |
| 662 | } |
| 663 | |
| 664 | epoll_reactor::descriptor_state* epoll_reactor::allocate_descriptor_state() |
| 665 | { |
| 666 | mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); |
| 667 | return registered_descriptors_.alloc(BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING((((static_cast<unsigned>(scheduler_.concurrency_hint()) & (0xFFFF0000u | 0x4u)) ^ 0xA5100000u) != 0) |
| 668 | REACTOR_IO, scheduler_.concurrency_hint())(((static_cast<unsigned>(scheduler_.concurrency_hint()) & (0xFFFF0000u | 0x4u)) ^ 0xA5100000u) != 0)); |
| 669 | } |
| 670 | |
| 671 | void epoll_reactor::free_descriptor_state(epoll_reactor::descriptor_state* s) |
| 672 | { |
| 673 | mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); |
| 674 | registered_descriptors_.free(s); |
| 675 | } |
| 676 | |
| 677 | void epoll_reactor::do_add_timer_queue(timer_queue_base& queue) |
| 678 | { |
| 679 | mutex::scoped_lock lock(mutex_); |
| 680 | timer_queues_.insert(&queue); |
| 681 | } |
| 682 | |
| 683 | void epoll_reactor::do_remove_timer_queue(timer_queue_base& queue) |
| 684 | { |
| 685 | mutex::scoped_lock lock(mutex_); |
| 686 | timer_queues_.erase(&queue); |
| 687 | } |
| 688 | |
| 689 | void epoll_reactor::update_timeout() |
| 690 | { |
| 691 | #if defined(BOOST_ASIO_HAS_TIMERFD1) |
| 692 | if (timer_fd_ != -1) |
| 693 | { |
| 694 | itimerspec new_timeout; |
| 695 | itimerspec old_timeout; |
| 696 | int flags = get_timeout(new_timeout); |
| 697 | timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); |
| 698 | return; |
| 699 | } |
| 700 | #endif // defined(BOOST_ASIO_HAS_TIMERFD) |
| 701 | interrupt(); |
| 702 | } |
| 703 | |
| 704 | int epoll_reactor::get_timeout(int msec) |
| 705 | { |
| 706 | // By default we will wait no longer than 5 minutes. This will ensure that |
| 707 | // any changes to the system clock are detected after no longer than this. |
| 708 | const int max_msec = 5 * 60 * 1000; |
| 709 | return timer_queues_.wait_duration_msec( |
| 710 | (msec < 0 || max_msec < msec) ? max_msec : msec); |
| 711 | } |
| 712 | |
| 713 | #if defined(BOOST_ASIO_HAS_TIMERFD1) |
| 714 | int epoll_reactor::get_timeout(itimerspec& ts) |
| 715 | { |
| 716 | ts.it_interval.tv_sec = 0; |
| 717 | ts.it_interval.tv_nsec = 0; |
| 718 | |
| 719 | long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); |
| 720 | ts.it_value.tv_sec = usec / 1000000; |
| 721 | ts.it_value.tv_nsec = usec ? (usec % 1000000) * 1000 : 1; |
| 722 | |
| 723 | return usec ? 0 : TFD_TIMER_ABSTIMETFD_TIMER_ABSTIME; |
| 724 | } |
| 725 | #endif // defined(BOOST_ASIO_HAS_TIMERFD) |
| 726 | |
| 727 | struct epoll_reactor::perform_io_cleanup_on_block_exit |
| 728 | { |
| 729 | explicit perform_io_cleanup_on_block_exit(epoll_reactor* r) |
| 730 | : reactor_(r), first_op_(0) |
| 731 | { |
| 732 | } |
| 733 | |
| 734 | ~perform_io_cleanup_on_block_exit() |
| 735 | { |
| 736 | if (first_op_) |
| 737 | { |
| 738 | // Post the remaining completed operations for invocation. |
| 739 | if (!ops_.empty()) |
| 740 | reactor_->scheduler_.post_deferred_completions(ops_); |
| 741 | |
| 742 | // A user-initiated operation has completed, but there's no need to |
| 743 | // explicitly call work_finished() here. Instead, we'll take advantage of |
| 744 | // the fact that the scheduler will call work_finished() once we return. |
| 745 | } |
| 746 | else |
| 747 | { |
| 748 | // No user-initiated operations have completed, so we need to compensate |
| 749 | // for the work_finished() call that the scheduler will make once this |
| 750 | // operation returns. |
| 751 | reactor_->scheduler_.compensating_work_started(); |
| 752 | } |
| 753 | } |
| 754 | |
| 755 | epoll_reactor* reactor_; |
| 756 | op_queue<operation> ops_; |
| 757 | operation* first_op_; |
| 758 | }; |
| 759 | |
| 760 | epoll_reactor::descriptor_state::descriptor_state(bool locking) |
| 761 | : operation(&epoll_reactor::descriptor_state::do_complete), |
| 762 | mutex_(locking) |
| 763 | { |
| 764 | } |
| 765 | |
| 766 | operation* epoll_reactor::descriptor_state::perform_io(uint32_t events) |
| 767 | { |
| 768 | mutex_.lock(); |
| 769 | perform_io_cleanup_on_block_exit io_cleanup(reactor_); |
| 770 | mutex::scoped_lock descriptor_lock(mutex_, mutex::scoped_lock::adopt_lock); |
| 771 | |
| 772 | // Exception operations must be processed first to ensure that any |
| 773 | // out-of-band data is read before normal data. |
| 774 | static const int flag[max_ops] = { EPOLLINEPOLLIN, EPOLLOUTEPOLLOUT, EPOLLPRIEPOLLPRI }; |
| 775 | for (int j = max_ops - 1; j >= 0; --j) |
| 776 | { |
| 777 | if (events & (flag[j] | EPOLLERREPOLLERR | EPOLLHUPEPOLLHUP)) |
| 778 | { |
| 779 | try_speculative_[j] = true; |
| 780 | while (reactor_op* op = op_queue_[j].front()) |
| 781 | { |
| 782 | if (reactor_op::status status = op->perform()) |
| 783 | { |
| 784 | op_queue_[j].pop(); |
| 785 | io_cleanup.ops_.push(op); |
| 786 | if (status == reactor_op::done_and_exhausted) |
| 787 | { |
| 788 | try_speculative_[j] = false; |
| 789 | break; |
| 790 | } |
| 791 | } |
| 792 | else |
| 793 | break; |
| 794 | } |
| 795 | } |
| 796 | } |
| 797 | |
| 798 | // The first operation will be returned for completion now. The others will |
| 799 | // be posted for later by the io_cleanup object's destructor. |
| 800 | io_cleanup.first_op_ = io_cleanup.ops_.front(); |
| 801 | io_cleanup.ops_.pop(); |
| 802 | return io_cleanup.first_op_; |
| 803 | } |
| 804 | |
| 805 | void epoll_reactor::descriptor_state::do_complete( |
| 806 | void* owner, operation* base, |
| 807 | const boost::system::error_code& ec, std::size_t bytes_transferred) |
| 808 | { |
| 809 | if (owner) |
| 810 | { |
| 811 | descriptor_state* descriptor_data = static_cast<descriptor_state*>(base); |
| 812 | uint32_t events = static_cast<uint32_t>(bytes_transferred); |
| 813 | if (operation* op = descriptor_data->perform_io(events)) |
| 814 | { |
| 815 | op->complete(owner, ec, 0); |
| 816 | } |
| 817 | } |
| 818 | } |
| 819 | |
| 820 | } // namespace detail |
| 821 | } // namespace asio |
| 822 | } // namespace boost |
| 823 | |
| 824 | #include <boost/asio/detail/pop_options.hpp> |
| 825 | |
| 826 | #endif // defined(BOOST_ASIO_HAS_EPOLL) |
| 827 | |
| 828 | #endif // BOOST_ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP |
| 1 | // |
| 2 | // detail/scoped_lock.hpp |
| 3 | // ~~~~~~~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2023 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-2023 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 | // Lock the mutex. |
| 50 | void lock() |
| 51 | { |
| 52 | (void)::pthread_mutex_lock(&mutex_); // Ignore EINVAL. |
| 53 | } |
| 54 | |
| 55 | // Unlock the mutex. |
| 56 | void unlock() |
| 57 | { |
| 58 | (void)::pthread_mutex_unlock(&mutex_); // Ignore EINVAL. |
| 59 | } |
| 60 | |
| 61 | private: |
| 62 | friend class posix_event; |
| 63 | ::pthread_mutex_t mutex_; |
| 64 | }; |
| 65 | |
| 66 | } // namespace detail |
| 67 | } // namespace asio |
| 68 | } // namespace boost |
| 69 | |
| 70 | #include <boost/asio/detail/pop_options.hpp> |
| 71 | |
| 72 | #if defined(BOOST_ASIO_HEADER_ONLY1) |
| 73 | # include <boost/asio/detail/impl/posix_mutex.ipp> |
| 74 | #endif // defined(BOOST_ASIO_HEADER_ONLY) |
| 75 | |
| 76 | #endif // defined(BOOST_ASIO_HAS_PTHREADS) |
| 77 | |
| 78 | #endif // BOOST_ASIO_DETAIL_POSIX_MUTEX_HPP |
| 1 | // |
| 2 | // detail/reactive_socket_service_base.hpp |
| 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2023 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) BOOST_ASIO_NOEXCEPTnoexcept; |
| 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 | typename associated_cancellation_slot<Handler>::type 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, &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, false, false, &io_ex, 0); |
| 247 | p.v = p.p = 0; |
| 248 | } |
| 249 | |
| 250 | // Send the given data to the peer. |
| 251 | template <typename ConstBufferSequence> |
| 252 | size_t send(base_implementation_type& impl, |
| 253 | const ConstBufferSequence& buffers, |
| 254 | socket_base::message_flags flags, boost::system::error_code& ec) |
| 255 | { |
| 256 | typedef buffer_sequence_adapter<boost::asio::const_buffer, |
| 257 | ConstBufferSequence> bufs_type; |
| 258 | |
| 259 | if (bufs_type::is_single_buffer) |
| 260 | { |
| 261 | return socket_ops::sync_send1(impl.socket_, |
| 262 | impl.state_, bufs_type::first(buffers).data(), |
| 263 | bufs_type::first(buffers).size(), flags, ec); |
| 264 | } |
| 265 | else |
| 266 | { |
| 267 | bufs_type bufs(buffers); |
| 268 | return socket_ops::sync_send(impl.socket_, impl.state_, |
| 269 | bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | // Wait until data can be sent without blocking. |
| 274 | size_t send(base_implementation_type& impl, const null_buffers&, |
| 275 | socket_base::message_flags, boost::system::error_code& ec) |
| 276 | { |
| 277 | // Wait for socket to become ready. |
| 278 | socket_ops::poll_write(impl.socket_, impl.state_, -1, ec); |
| 279 | |
| 280 | return 0; |
| 281 | } |
| 282 | |
| 283 | // Start an asynchronous send. The data being sent must be valid for the |
| 284 | // lifetime of the asynchronous operation. |
| 285 | template <typename ConstBufferSequence, typename Handler, typename IoExecutor> |
| 286 | void async_send(base_implementation_type& impl, |
| 287 | const ConstBufferSequence& buffers, socket_base::message_flags flags, |
| 288 | Handler& handler, const IoExecutor& io_ex) |
| 289 | { |
| 290 | bool is_continuation = |
| 291 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 292 | |
| 293 | typename associated_cancellation_slot<Handler>::type slot |
| 294 | = boost::asio::get_associated_cancellation_slot(handler); |
| 295 | |
| 296 | // Allocate and construct an operation to wrap the handler. |
| 297 | typedef reactive_socket_send_op< |
| 298 | ConstBufferSequence, Handler, IoExecutor> op; |
| 299 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 300 | op::ptr::allocate(handler), 0 }; |
| 301 | p.p = new (p.v) op(success_ec_, impl.socket_, |
| 302 | impl.state_, buffers, flags, handler, io_ex); |
| 303 | |
| 304 | // Optionally register for per-operation cancellation. |
| 305 | if (slot.is_connected()) |
| 306 | { |
| 307 | p.p->cancellation_key_ = |
| 308 | &slot.template emplace<reactor_op_cancellation>( |
| 309 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::write_op); |
| 310 | } |
| 311 | |
| 312 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 313 | &impl, impl.socket_, "async_send"))(void)0; |
| 314 | |
| 315 | start_op(impl, reactor::write_op, p.p, is_continuation, true, |
| 316 | ((impl.state_ & socket_ops::stream_oriented) |
| 317 | && buffer_sequence_adapter<boost::asio::const_buffer, |
| 318 | ConstBufferSequence>::all_empty(buffers)), &io_ex, 0); |
| 319 | p.v = p.p = 0; |
| 320 | } |
| 321 | |
| 322 | // Start an asynchronous wait until data can be sent without blocking. |
| 323 | template <typename Handler, typename IoExecutor> |
| 324 | void async_send(base_implementation_type& impl, const null_buffers&, |
| 325 | socket_base::message_flags, Handler& handler, const IoExecutor& io_ex) |
| 326 | { |
| 327 | bool is_continuation = |
| 328 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 329 | |
| 330 | typename associated_cancellation_slot<Handler>::type slot |
| 331 | = boost::asio::get_associated_cancellation_slot(handler); |
| 332 | |
| 333 | // Allocate and construct an operation to wrap the handler. |
| 334 | typedef reactive_null_buffers_op<Handler, IoExecutor> op; |
| 335 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 336 | op::ptr::allocate(handler), 0 }; |
| 337 | p.p = new (p.v) op(success_ec_, handler, io_ex); |
| 338 | |
| 339 | // Optionally register for per-operation cancellation. |
| 340 | if (slot.is_connected()) |
| 341 | { |
| 342 | p.p->cancellation_key_ = |
| 343 | &slot.template emplace<reactor_op_cancellation>( |
| 344 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::write_op); |
| 345 | } |
| 346 | |
| 347 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 348 | &impl, impl.socket_, "async_send(null_buffers)"))(void)0; |
| 349 | |
| 350 | start_op(impl, reactor::write_op, p.p, |
| 351 | is_continuation, false, false, &io_ex, 0); |
| 352 | p.v = p.p = 0; |
| 353 | } |
| 354 | |
| 355 | // Receive some data from the peer. Returns the number of bytes received. |
| 356 | template <typename MutableBufferSequence> |
| 357 | size_t receive(base_implementation_type& impl, |
| 358 | const MutableBufferSequence& buffers, |
| 359 | socket_base::message_flags flags, boost::system::error_code& ec) |
| 360 | { |
| 361 | typedef buffer_sequence_adapter<boost::asio::mutable_buffer, |
| 362 | MutableBufferSequence> bufs_type; |
| 363 | |
| 364 | if (bufs_type::is_single_buffer) |
| 365 | { |
| 366 | return socket_ops::sync_recv1(impl.socket_, |
| 367 | impl.state_, bufs_type::first(buffers).data(), |
| 368 | bufs_type::first(buffers).size(), flags, ec); |
| 369 | } |
| 370 | else |
| 371 | { |
| 372 | bufs_type bufs(buffers); |
| 373 | return socket_ops::sync_recv(impl.socket_, impl.state_, |
| 374 | bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | // Wait until data can be received without blocking. |
| 379 | size_t receive(base_implementation_type& impl, const null_buffers&, |
| 380 | socket_base::message_flags, boost::system::error_code& ec) |
| 381 | { |
| 382 | // Wait for socket to become ready. |
| 383 | socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); |
| 384 | |
| 385 | return 0; |
| 386 | } |
| 387 | |
| 388 | // Start an asynchronous receive. The buffer for the data being received |
| 389 | // must be valid for the lifetime of the asynchronous operation. |
| 390 | template <typename MutableBufferSequence, |
| 391 | typename Handler, typename IoExecutor> |
| 392 | void async_receive(base_implementation_type& impl, |
| 393 | const MutableBufferSequence& buffers, socket_base::message_flags flags, |
| 394 | Handler& handler, const IoExecutor& io_ex) |
| 395 | { |
| 396 | bool is_continuation = |
| 397 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 398 | |
| 399 | typename associated_cancellation_slot<Handler>::type slot |
| 400 | = boost::asio::get_associated_cancellation_slot(handler); |
| 401 | |
| 402 | // Allocate and construct an operation to wrap the handler. |
| 403 | typedef reactive_socket_recv_op< |
| 404 | MutableBufferSequence, Handler, IoExecutor> op; |
| 405 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 406 | op::ptr::allocate(handler), 0 }; |
| 407 | p.p = new (p.v) op(success_ec_, impl.socket_, |
| 408 | impl.state_, buffers, flags, handler, io_ex); |
| 409 | |
| 410 | // Optionally register for per-operation cancellation. |
| 411 | if (slot.is_connected()) |
| 412 | { |
| 413 | p.p->cancellation_key_ = |
| 414 | &slot.template emplace<reactor_op_cancellation>( |
| 415 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 416 | } |
| 417 | |
| 418 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 419 | &impl, impl.socket_, "async_receive"))(void)0; |
| 420 | |
| 421 | start_op(impl, |
| 422 | (flags & socket_base::message_out_of_band) |
| 423 | ? reactor::except_op : reactor::read_op, |
| 424 | p.p, is_continuation, |
| 425 | (flags & socket_base::message_out_of_band) == 0, |
| 426 | ((impl.state_ & socket_ops::stream_oriented) |
| 427 | && buffer_sequence_adapter<boost::asio::mutable_buffer, |
| 428 | MutableBufferSequence>::all_empty(buffers)), &io_ex, 0); |
| 429 | p.v = p.p = 0; |
| 430 | } |
| 431 | |
| 432 | // Wait until data can be received without blocking. |
| 433 | template <typename Handler, typename IoExecutor> |
| 434 | void async_receive(base_implementation_type& impl, |
| 435 | const null_buffers&, socket_base::message_flags flags, |
| 436 | Handler& handler, const IoExecutor& io_ex) |
| 437 | { |
| 438 | bool is_continuation = |
| 439 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 440 | |
| 441 | typename associated_cancellation_slot<Handler>::type slot |
| 442 | = boost::asio::get_associated_cancellation_slot(handler); |
| 443 | |
| 444 | // Allocate and construct an operation to wrap the handler. |
| 445 | typedef reactive_null_buffers_op<Handler, IoExecutor> op; |
| 446 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 447 | op::ptr::allocate(handler), 0 }; |
| 448 | p.p = new (p.v) op(success_ec_, handler, io_ex); |
| 449 | |
| 450 | // Optionally register for per-operation cancellation. |
| 451 | if (slot.is_connected()) |
| 452 | { |
| 453 | p.p->cancellation_key_ = |
| 454 | &slot.template emplace<reactor_op_cancellation>( |
| 455 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 456 | } |
| 457 | |
| 458 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 459 | &impl, impl.socket_, "async_receive(null_buffers)"))(void)0; |
| 460 | |
| 461 | start_op(impl, |
| 462 | (flags & socket_base::message_out_of_band) |
| 463 | ? reactor::except_op : reactor::read_op, |
| 464 | p.p, is_continuation, false, false, &io_ex, 0); |
| 465 | p.v = p.p = 0; |
| 466 | } |
| 467 | |
| 468 | // Receive some data with associated flags. Returns the number of bytes |
| 469 | // received. |
| 470 | template <typename MutableBufferSequence> |
| 471 | size_t receive_with_flags(base_implementation_type& impl, |
| 472 | const MutableBufferSequence& buffers, |
| 473 | socket_base::message_flags in_flags, |
| 474 | socket_base::message_flags& out_flags, boost::system::error_code& ec) |
| 475 | { |
| 476 | buffer_sequence_adapter<boost::asio::mutable_buffer, |
| 477 | MutableBufferSequence> bufs(buffers); |
| 478 | |
| 479 | return socket_ops::sync_recvmsg(impl.socket_, impl.state_, |
| 480 | bufs.buffers(), bufs.count(), in_flags, out_flags, ec); |
| 481 | } |
| 482 | |
| 483 | // Wait until data can be received without blocking. |
| 484 | size_t receive_with_flags(base_implementation_type& impl, |
| 485 | const null_buffers&, socket_base::message_flags, |
| 486 | socket_base::message_flags& out_flags, boost::system::error_code& ec) |
| 487 | { |
| 488 | // Wait for socket to become ready. |
| 489 | socket_ops::poll_read(impl.socket_, impl.state_, -1, ec); |
| 490 | |
| 491 | // Clear out_flags, since we cannot give it any other sensible value when |
| 492 | // performing a null_buffers operation. |
| 493 | out_flags = 0; |
| 494 | |
| 495 | return 0; |
| 496 | } |
| 497 | |
| 498 | // Start an asynchronous receive. The buffer for the data being received |
| 499 | // must be valid for the lifetime of the asynchronous operation. |
| 500 | template <typename MutableBufferSequence, |
| 501 | typename Handler, typename IoExecutor> |
| 502 | void async_receive_with_flags(base_implementation_type& impl, |
| 503 | const MutableBufferSequence& buffers, socket_base::message_flags in_flags, |
| 504 | socket_base::message_flags& out_flags, Handler& handler, |
| 505 | const IoExecutor& io_ex) |
| 506 | { |
| 507 | bool is_continuation = |
| 508 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 509 | |
| 510 | typename associated_cancellation_slot<Handler>::type slot |
| 511 | = boost::asio::get_associated_cancellation_slot(handler); |
| 512 | |
| 513 | // Allocate and construct an operation to wrap the handler. |
| 514 | typedef reactive_socket_recvmsg_op< |
| 515 | MutableBufferSequence, Handler, IoExecutor> op; |
| 516 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 517 | op::ptr::allocate(handler), 0 }; |
| 518 | p.p = new (p.v) op(success_ec_, impl.socket_, |
| 519 | buffers, in_flags, out_flags, handler, io_ex); |
| 520 | |
| 521 | // Optionally register for per-operation cancellation. |
| 522 | if (slot.is_connected()) |
| 523 | { |
| 524 | p.p->cancellation_key_ = |
| 525 | &slot.template emplace<reactor_op_cancellation>( |
| 526 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 527 | } |
| 528 | |
| 529 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 530 | &impl, impl.socket_, "async_receive_with_flags"))(void)0; |
| 531 | |
| 532 | start_op(impl, |
| 533 | (in_flags & socket_base::message_out_of_band) |
| 534 | ? reactor::except_op : reactor::read_op, |
| 535 | p.p, is_continuation, |
| 536 | (in_flags & socket_base::message_out_of_band) == 0, false, &io_ex, 0); |
| 537 | p.v = p.p = 0; |
| 538 | } |
| 539 | |
| 540 | // Wait until data can be received without blocking. |
| 541 | template <typename Handler, typename IoExecutor> |
| 542 | void async_receive_with_flags(base_implementation_type& impl, |
| 543 | const null_buffers&, socket_base::message_flags in_flags, |
| 544 | socket_base::message_flags& out_flags, Handler& handler, |
| 545 | const IoExecutor& io_ex) |
| 546 | { |
| 547 | bool is_continuation = |
| 548 | boost_asio_handler_cont_helpers::is_continuation(handler); |
| 549 | |
| 550 | typename associated_cancellation_slot<Handler>::type slot |
| 551 | = boost::asio::get_associated_cancellation_slot(handler); |
| 552 | |
| 553 | // Allocate and construct an operation to wrap the handler. |
| 554 | typedef reactive_null_buffers_op<Handler, IoExecutor> op; |
| 555 | typename op::ptr p = { boost::asio::detail::addressof(handler), |
| 556 | op::ptr::allocate(handler), 0 }; |
| 557 | p.p = new (p.v) op(success_ec_, handler, io_ex); |
| 558 | |
| 559 | // Optionally register for per-operation cancellation. |
| 560 | if (slot.is_connected()) |
| 561 | { |
| 562 | p.p->cancellation_key_ = |
| 563 | &slot.template emplace<reactor_op_cancellation>( |
| 564 | &reactor_, &impl.reactor_data_, impl.socket_, reactor::read_op); |
| 565 | } |
| 566 | |
| 567 | BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",(void)0 |
| 568 | &impl, impl.socket_, "async_receive_with_flags(null_buffers)"))(void)0; |
| 569 | |
| 570 | // Clear out_flags, since we cannot give it any other sensible value when |
| 571 | // performing a null_buffers operation. |
| 572 | out_flags = 0; |
| 573 | |
| 574 | start_op(impl, |
| 575 | (in_flags & socket_base::message_out_of_band) |
| 576 | ? reactor::except_op : reactor::read_op, |
| 577 | p.p, is_continuation, false, false, &io_ex, 0); |
| 578 | p.v = p.p = 0; |
| 579 | } |
| 580 | |
| 581 | protected: |
| 582 | // Open a new socket implementation. |
| 583 | BOOST_ASIO_DECLinline boost::system::error_code do_open( |
| 584 | base_implementation_type& impl, int af, |
| 585 | int type, int protocol, boost::system::error_code& ec); |
| 586 | |
| 587 | // Assign a native socket to a socket implementation. |
| 588 | BOOST_ASIO_DECLinline boost::system::error_code do_assign( |
| 589 | base_implementation_type& impl, int type, |
| 590 | const native_handle_type& native_socket, boost::system::error_code& ec); |
| 591 | |
| 592 | // Start the asynchronous read or write operation. |
| 593 | BOOST_ASIO_DECLinline void do_start_op(base_implementation_type& impl, int op_type, |
| 594 | reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop, |
| 595 | void (*on_immediate)(operation* op, bool, const void*), |
| 596 | const void* immediate_arg); |
| 597 | |
| 598 | // Start the asynchronous operation for handlers that are specialised for |
| 599 | // immediate completion. |
| 600 | template <typename Op> |
| 601 | void start_op(base_implementation_type& impl, int op_type, Op* op, |
| 602 | bool is_continuation, bool is_non_blocking, bool noop, |
| 603 | const void* io_ex, ...) |
| 604 | { |
| 605 | return do_start_op(impl, op_type, op, is_continuation, |
| 606 | is_non_blocking, noop, &Op::do_immediate, io_ex); |
| 607 | } |
| 608 | |
| 609 | // Start the asynchronous operation for handlers that are not specialised for |
| 610 | // immediate completion. |
| 611 | template <typename Op> |
| 612 | void start_op(base_implementation_type& impl, int op_type, Op* op, |
| 613 | bool is_continuation, bool is_non_blocking, bool noop, const void*, |
| 614 | typename enable_if< |
| 615 | is_same< |
| 616 | typename associated_immediate_executor< |
| 617 | typename Op::handler_type, |
| 618 | typename Op::io_executor_type |
| 619 | >::asio_associated_immediate_executor_is_unspecialised, |
| 620 | void |
| 621 | >::value |
| 622 | >::type*) |
| 623 | { |
| 624 | return do_start_op(impl, op_type, op, is_continuation, is_non_blocking, |
| 625 | noop, &reactor::call_post_immediate_completion, &reactor_); |
| 626 | } |
| 627 | |
| 628 | // Start the asynchronous accept operation. |
| 629 | BOOST_ASIO_DECLinline void do_start_accept_op(base_implementation_type& impl, |
| 630 | reactor_op* op, bool is_continuation, bool peer_is_open, |
| 631 | void (*on_immediate)(operation* op, bool, const void*), |
| 632 | const void* immediate_arg); |
| 633 | |
| 634 | // Start the asynchronous accept operation for handlers that are specialised |
| 635 | // for immediate completion. |
| 636 | template <typename Op> |
| 637 | void start_accept_op(base_implementation_type& impl, Op* op, |
| 638 | bool is_continuation, bool peer_is_open, const void* io_ex, ...) |
| 639 | { |
| 640 | return do_start_accept_op(impl, op, is_continuation, |
| 641 | peer_is_open, &Op::do_immediate, io_ex); |
| 642 | } |
| 643 | |
| 644 | // Start the asynchronous operation for handlers that are not specialised for |
| 645 | // immediate completion. |
| 646 | template <typename Op> |
| 647 | void start_accept_op(base_implementation_type& impl, Op* op, |
| 648 | bool is_continuation, bool peer_is_open, const void*, |
| 649 | typename enable_if< |
| 650 | is_same< |
| 651 | typename associated_immediate_executor< |
| 652 | typename Op::handler_type, |
| 653 | typename Op::io_executor_type |
| 654 | >::asio_associated_immediate_executor_is_unspecialised, |
| 655 | void |
| 656 | >::value |
| 657 | >::type*) |
| 658 | { |
| 659 | return do_start_accept_op(impl, op, is_continuation, peer_is_open, |
| 660 | &reactor::call_post_immediate_completion, &reactor_); |
| 661 | } |
| 662 | |
| 663 | // Start the asynchronous connect operation. |
| 664 | BOOST_ASIO_DECLinline void do_start_connect_op(base_implementation_type& impl, |
| 665 | reactor_op* op, bool is_continuation, const void* addr, size_t addrlen, |
| 666 | void (*on_immediate)(operation* op, bool, const void*), |
| 667 | const void* immediate_arg); |
| 668 | |
| 669 | // Start the asynchronous operation for handlers that are specialised for |
| 670 | // immediate completion. |
| 671 | template <typename Op> |
| 672 | void start_connect_op(base_implementation_type& impl, |
| 673 | Op* op, bool is_continuation, const void* addr, |
| 674 | size_t addrlen, const void* io_ex, ...) |
| 675 | { |
| 676 | return do_start_connect_op(impl, op, is_continuation, |
| 677 | addr, addrlen, &Op::do_immediate, io_ex); |
| 678 | } |
| 679 | |
| 680 | // Start the asynchronous operation for handlers that are not specialised for |
| 681 | // immediate completion. |
| 682 | template <typename Op> |
| 683 | void start_connect_op(base_implementation_type& impl, Op* op, |
| 684 | bool is_continuation, const void* addr, size_t addrlen, const void*, |
| 685 | typename enable_if< |
| 686 | is_same< |
| 687 | typename associated_immediate_executor< |
| 688 | typename Op::handler_type, |
| 689 | typename Op::io_executor_type |
| 690 | >::asio_associated_immediate_executor_is_unspecialised, |
| 691 | void |
| 692 | >::value |
| 693 | >::type*) |
| 694 | { |
| 695 | return do_start_connect_op(impl, op, is_continuation, addr, |
| 696 | addrlen, &reactor::call_post_immediate_completion, &reactor_); |
| 697 | } |
| 698 | |
| 699 | // Helper class used to implement per-operation cancellation |
| 700 | class reactor_op_cancellation |
| 701 | { |
| 702 | public: |
| 703 | reactor_op_cancellation(reactor* r, |
| 704 | reactor::per_descriptor_data* p, socket_type d, int o) |
| 705 | : reactor_(r), |
| 706 | reactor_data_(p), |
| 707 | descriptor_(d), |
| 708 | op_type_(o) |
| 709 | { |
| 710 | } |
| 711 | |
| 712 | void operator()(cancellation_type_t type) |
| 713 | { |
| 714 | if (!!(type & |
| 715 | (cancellation_type::terminal |
| 716 | | cancellation_type::partial |
| 717 | | cancellation_type::total))) |
| 718 | { |
| 719 | reactor_->cancel_ops_by_key(descriptor_, |
| 720 | *reactor_data_, op_type_, this); |
| 721 | } |
| 722 | } |
| 723 | |
| 724 | private: |
| 725 | reactor* reactor_; |
| 726 | reactor::per_descriptor_data* reactor_data_; |
| 727 | socket_type descriptor_; |
| 728 | int op_type_; |
| 729 | }; |
| 730 | |
| 731 | // The selector that performs event demultiplexing for the service. |
| 732 | reactor& reactor_; |
| 733 | |
| 734 | // Cached success value to avoid accessing category singleton. |
| 735 | const boost::system::error_code success_ec_; |
| 736 | }; |
| 737 | |
| 738 | } // namespace detail |
| 739 | } // namespace asio |
| 740 | } // namespace boost |
| 741 | |
| 742 | #include <boost/asio/detail/pop_options.hpp> |
| 743 | |
| 744 | #if defined(BOOST_ASIO_HEADER_ONLY1) |
| 745 | # include <boost/asio/detail/impl/reactive_socket_service_base.ipp> |
| 746 | #endif // defined(BOOST_ASIO_HEADER_ONLY) |
| 747 | |
| 748 | #endif // !defined(BOOST_ASIO_HAS_IOCP) |
| 749 | // && !defined(BOOST_ASIO_WINDOWS_RUNTIME) |
| 750 | // && !defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT) |
| 751 | |
| 752 | #endif // BOOST_ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP |
| 1 | // | ||||||||||||||||||||||||||||||||||
| 2 | // detail/impl/socket_ops.ipp | ||||||||||||||||||||||||||||||||||
| 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||||||||||||||||||||||||||||||
| 4 | // | ||||||||||||||||||||||||||||||||||
| 5 | // Copyright (c) 2003-2023 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 | #include <boost/asio/detail/push_options.hpp> | ||||||||||||||||||||||||||||||||||
| 45 | |||||||||||||||||||||||||||||||||||
| 46 | namespace boost { | ||||||||||||||||||||||||||||||||||
| 47 | namespace asio { | ||||||||||||||||||||||||||||||||||
| 48 | namespace detail { | ||||||||||||||||||||||||||||||||||
| 49 | namespace socket_ops { | ||||||||||||||||||||||||||||||||||
| 50 | |||||||||||||||||||||||||||||||||||
| 51 | #if !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 52 | |||||||||||||||||||||||||||||||||||
| 53 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 54 | struct msghdr { int msg_namelen; }; | ||||||||||||||||||||||||||||||||||
| 55 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 56 | |||||||||||||||||||||||||||||||||||
| 57 | #if defined(__hpux) | ||||||||||||||||||||||||||||||||||
| 58 | // HP-UX doesn't declare these functions extern "C", so they are declared again | ||||||||||||||||||||||||||||||||||
| 59 | // here to avoid linker errors about undefined symbols. | ||||||||||||||||||||||||||||||||||
| 60 | extern "C" char* if_indextoname(unsigned int, char*); | ||||||||||||||||||||||||||||||||||
| 61 | extern "C" unsigned int if_nametoindex(const char*); | ||||||||||||||||||||||||||||||||||
| 62 | #endif // defined(__hpux) | ||||||||||||||||||||||||||||||||||
| 63 | |||||||||||||||||||||||||||||||||||
| 64 | #endif // !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 65 | |||||||||||||||||||||||||||||||||||
| 66 | inline void clear_last_error() | ||||||||||||||||||||||||||||||||||
| 67 | { | ||||||||||||||||||||||||||||||||||
| 68 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 69 | WSASetLastError(0); | ||||||||||||||||||||||||||||||||||
| 70 | #else | ||||||||||||||||||||||||||||||||||
| 71 | errno(*__errno_location ()) = 0; | ||||||||||||||||||||||||||||||||||
| 72 | #endif | ||||||||||||||||||||||||||||||||||
| 73 | } | ||||||||||||||||||||||||||||||||||
| 74 | |||||||||||||||||||||||||||||||||||
| 75 | #if !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 76 | |||||||||||||||||||||||||||||||||||
| 77 | inline void get_last_error( | ||||||||||||||||||||||||||||||||||
| 78 | boost::system::error_code& ec, bool is_error_condition) | ||||||||||||||||||||||||||||||||||
| 79 | { | ||||||||||||||||||||||||||||||||||
| 80 | if (!is_error_condition) | ||||||||||||||||||||||||||||||||||
| 81 | { | ||||||||||||||||||||||||||||||||||
| 82 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 83 | } | ||||||||||||||||||||||||||||||||||
| 84 | else | ||||||||||||||||||||||||||||||||||
| 85 | { | ||||||||||||||||||||||||||||||||||
| 86 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 87 | ec = boost::system::error_code(WSAGetLastError(), | ||||||||||||||||||||||||||||||||||
| 88 | boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||
| 89 | #else | ||||||||||||||||||||||||||||||||||
| 90 | ec = boost::system::error_code(errno(*__errno_location ()), | ||||||||||||||||||||||||||||||||||
| 91 | boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||
| 92 | #endif | ||||||||||||||||||||||||||||||||||
| 93 | } | ||||||||||||||||||||||||||||||||||
| 94 | } | ||||||||||||||||||||||||||||||||||
| 95 | |||||||||||||||||||||||||||||||||||
| 96 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||
| 97 | inline socket_type call_accept(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||
| 98 | socket_type s, void* addr, std::size_t* addrlen) | ||||||||||||||||||||||||||||||||||
| 99 | { | ||||||||||||||||||||||||||||||||||
| 100 | SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; | ||||||||||||||||||||||||||||||||||
| 101 | socket_type result = ::accept(s, | ||||||||||||||||||||||||||||||||||
| 102 | static_cast<socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||
| 103 | addrlen ? &tmp_addrlen : 0); | ||||||||||||||||||||||||||||||||||
| 104 | if (addrlen) | ||||||||||||||||||||||||||||||||||
| 105 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||
| 106 | return result; | ||||||||||||||||||||||||||||||||||
| 107 | } | ||||||||||||||||||||||||||||||||||
| 108 | |||||||||||||||||||||||||||||||||||
| 109 | socket_type accept(socket_type s, void* addr, | ||||||||||||||||||||||||||||||||||
| 110 | std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 111 | { | ||||||||||||||||||||||||||||||||||
| 112 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 113 | { | ||||||||||||||||||||||||||||||||||
| 114 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 115 | return invalid_socket; | ||||||||||||||||||||||||||||||||||
| 116 | } | ||||||||||||||||||||||||||||||||||
| 117 | |||||||||||||||||||||||||||||||||||
| 118 | socket_type new_s = call_accept(&msghdr::msg_namelen, s, addr, addrlen); | ||||||||||||||||||||||||||||||||||
| 119 | get_last_error(ec, new_s == invalid_socket); | ||||||||||||||||||||||||||||||||||
| 120 | if (new_s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 121 | return new_s; | ||||||||||||||||||||||||||||||||||
| 122 | |||||||||||||||||||||||||||||||||||
| 123 | #if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) | ||||||||||||||||||||||||||||||||||
| 124 | int optval = 1; | ||||||||||||||||||||||||||||||||||
| 125 | int result = ::setsockopt(new_s, SOL_SOCKET1, | ||||||||||||||||||||||||||||||||||
| 126 | SO_NOSIGPIPE, &optval, sizeof(optval)); | ||||||||||||||||||||||||||||||||||
| 127 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 128 | if (result != 0) | ||||||||||||||||||||||||||||||||||
| 129 | { | ||||||||||||||||||||||||||||||||||
| 130 | ::close(new_s); | ||||||||||||||||||||||||||||||||||
| 131 | return invalid_socket; | ||||||||||||||||||||||||||||||||||
| 132 | } | ||||||||||||||||||||||||||||||||||
| 133 | #endif | ||||||||||||||||||||||||||||||||||
| 134 | |||||||||||||||||||||||||||||||||||
| 135 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 136 | return new_s; | ||||||||||||||||||||||||||||||||||
| 137 | } | ||||||||||||||||||||||||||||||||||
| 138 | |||||||||||||||||||||||||||||||||||
| 139 | socket_type sync_accept(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||
| 140 | void* addr, std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 141 | { | ||||||||||||||||||||||||||||||||||
| 142 | // Accept a socket. | ||||||||||||||||||||||||||||||||||
| 143 | for (;;) | ||||||||||||||||||||||||||||||||||
| 144 | { | ||||||||||||||||||||||||||||||||||
| 145 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||
| 146 | socket_type new_socket = socket_ops::accept(s, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||
| 147 | |||||||||||||||||||||||||||||||||||
| 148 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 149 | if (new_socket != invalid_socket) | ||||||||||||||||||||||||||||||||||
| 150 | return new_socket; | ||||||||||||||||||||||||||||||||||
| 151 | |||||||||||||||||||||||||||||||||||
| 152 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 153 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 154 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 155 | { | ||||||||||||||||||||||||||||||||||
| 156 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 157 | return invalid_socket; | ||||||||||||||||||||||||||||||||||
| 158 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||
| 159 | } | ||||||||||||||||||||||||||||||||||
| 160 | else if (ec == boost::asio::error::connection_aborted) | ||||||||||||||||||||||||||||||||||
| 161 | { | ||||||||||||||||||||||||||||||||||
| 162 | if (state & enable_connection_aborted) | ||||||||||||||||||||||||||||||||||
| 163 | return invalid_socket; | ||||||||||||||||||||||||||||||||||
| 164 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||
| 165 | } | ||||||||||||||||||||||||||||||||||
| 166 | #if defined(EPROTO71) | ||||||||||||||||||||||||||||||||||
| 167 | else if (ec.value() == EPROTO71) | ||||||||||||||||||||||||||||||||||
| 168 | { | ||||||||||||||||||||||||||||||||||
| 169 | if (state & enable_connection_aborted) | ||||||||||||||||||||||||||||||||||
| 170 | return invalid_socket; | ||||||||||||||||||||||||||||||||||
| 171 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||
| 172 | } | ||||||||||||||||||||||||||||||||||
| 173 | #endif // defined(EPROTO) | ||||||||||||||||||||||||||||||||||
| 174 | else | ||||||||||||||||||||||||||||||||||
| 175 | return invalid_socket; | ||||||||||||||||||||||||||||||||||
| 176 | |||||||||||||||||||||||||||||||||||
| 177 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||
| 178 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||
| 179 | return invalid_socket; | ||||||||||||||||||||||||||||||||||
| 180 | } | ||||||||||||||||||||||||||||||||||
| 181 | } | ||||||||||||||||||||||||||||||||||
| 182 | |||||||||||||||||||||||||||||||||||
| 183 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 184 | |||||||||||||||||||||||||||||||||||
| 185 | void complete_iocp_accept(socket_type s, void* output_buffer, | ||||||||||||||||||||||||||||||||||
| 186 | DWORD address_length, void* addr, std::size_t* addrlen, | ||||||||||||||||||||||||||||||||||
| 187 | socket_type new_socket, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 188 | { | ||||||||||||||||||||||||||||||||||
| 189 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||
| 190 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 191 | ec = boost::asio::error::connection_aborted; | ||||||||||||||||||||||||||||||||||
| 192 | |||||||||||||||||||||||||||||||||||
| 193 | if (!ec) | ||||||||||||||||||||||||||||||||||
| 194 | { | ||||||||||||||||||||||||||||||||||
| 195 | // Get the address of the peer. | ||||||||||||||||||||||||||||||||||
| 196 | if (addr && addrlen) | ||||||||||||||||||||||||||||||||||
| 197 | { | ||||||||||||||||||||||||||||||||||
| 198 | LPSOCKADDR local_addr = 0; | ||||||||||||||||||||||||||||||||||
| 199 | int local_addr_length = 0; | ||||||||||||||||||||||||||||||||||
| 200 | LPSOCKADDR remote_addr = 0; | ||||||||||||||||||||||||||||||||||
| 201 | int remote_addr_length = 0; | ||||||||||||||||||||||||||||||||||
| 202 | GetAcceptExSockaddrs(output_buffer, 0, address_length, | ||||||||||||||||||||||||||||||||||
| 203 | address_length, &local_addr, &local_addr_length, | ||||||||||||||||||||||||||||||||||
| 204 | &remote_addr, &remote_addr_length); | ||||||||||||||||||||||||||||||||||
| 205 | if (static_cast<std::size_t>(remote_addr_length) > *addrlen) | ||||||||||||||||||||||||||||||||||
| 206 | { | ||||||||||||||||||||||||||||||||||
| 207 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 208 | } | ||||||||||||||||||||||||||||||||||
| 209 | else | ||||||||||||||||||||||||||||||||||
| 210 | { | ||||||||||||||||||||||||||||||||||
| 211 | using namespace std; // For memcpy. | ||||||||||||||||||||||||||||||||||
| 212 | memcpy(addr, remote_addr, remote_addr_length); | ||||||||||||||||||||||||||||||||||
| 213 | *addrlen = static_cast<std::size_t>(remote_addr_length); | ||||||||||||||||||||||||||||||||||
| 214 | } | ||||||||||||||||||||||||||||||||||
| 215 | } | ||||||||||||||||||||||||||||||||||
| 216 | |||||||||||||||||||||||||||||||||||
| 217 | // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname | ||||||||||||||||||||||||||||||||||
| 218 | // and getpeername will work on the accepted socket. | ||||||||||||||||||||||||||||||||||
| 219 | SOCKET update_ctx_param = s; | ||||||||||||||||||||||||||||||||||
| 220 | socket_ops::state_type state = 0; | ||||||||||||||||||||||||||||||||||
| 221 | socket_ops::setsockopt(new_socket, state, | ||||||||||||||||||||||||||||||||||
| 222 | SOL_SOCKET1, SO_UPDATE_ACCEPT_CONTEXT, | ||||||||||||||||||||||||||||||||||
| 223 | &update_ctx_param, sizeof(SOCKET), ec); | ||||||||||||||||||||||||||||||||||
| 224 | } | ||||||||||||||||||||||||||||||||||
| 225 | } | ||||||||||||||||||||||||||||||||||
| 226 | |||||||||||||||||||||||||||||||||||
| 227 | #else // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 228 | |||||||||||||||||||||||||||||||||||
| 229 | bool non_blocking_accept(socket_type s, | ||||||||||||||||||||||||||||||||||
| 230 | state_type state, void* addr, std::size_t* addrlen, | ||||||||||||||||||||||||||||||||||
| 231 | boost::system::error_code& ec, socket_type& new_socket) | ||||||||||||||||||||||||||||||||||
| 232 | { | ||||||||||||||||||||||||||||||||||
| 233 | for (;;) | ||||||||||||||||||||||||||||||||||
| 234 | { | ||||||||||||||||||||||||||||||||||
| 235 | // Accept the waiting connection. | ||||||||||||||||||||||||||||||||||
| 236 | new_socket = socket_ops::accept(s, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||
| 237 | |||||||||||||||||||||||||||||||||||
| 238 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 239 | if (new_socket != invalid_socket) | ||||||||||||||||||||||||||||||||||
| 240 | return true; | ||||||||||||||||||||||||||||||||||
| 241 | |||||||||||||||||||||||||||||||||||
| 242 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||
| 243 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||
| 244 | continue; | ||||||||||||||||||||||||||||||||||
| 245 | |||||||||||||||||||||||||||||||||||
| 246 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 247 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 248 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 249 | { | ||||||||||||||||||||||||||||||||||
| 250 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||
| 251 | } | ||||||||||||||||||||||||||||||||||
| 252 | else if (ec == boost::asio::error::connection_aborted) | ||||||||||||||||||||||||||||||||||
| 253 | { | ||||||||||||||||||||||||||||||||||
| 254 | if (state & enable_connection_aborted) | ||||||||||||||||||||||||||||||||||
| 255 | return true; | ||||||||||||||||||||||||||||||||||
| 256 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||
| 257 | } | ||||||||||||||||||||||||||||||||||
| 258 | #if defined(EPROTO71) | ||||||||||||||||||||||||||||||||||
| 259 | else if (ec.value() == EPROTO71) | ||||||||||||||||||||||||||||||||||
| 260 | { | ||||||||||||||||||||||||||||||||||
| 261 | if (state & enable_connection_aborted) | ||||||||||||||||||||||||||||||||||
| 262 | return true; | ||||||||||||||||||||||||||||||||||
| 263 | // Fall through to retry operation. | ||||||||||||||||||||||||||||||||||
| 264 | } | ||||||||||||||||||||||||||||||||||
| 265 | #endif // defined(EPROTO) | ||||||||||||||||||||||||||||||||||
| 266 | else | ||||||||||||||||||||||||||||||||||
| 267 | return true; | ||||||||||||||||||||||||||||||||||
| 268 | |||||||||||||||||||||||||||||||||||
| 269 | return false; | ||||||||||||||||||||||||||||||||||
| 270 | } | ||||||||||||||||||||||||||||||||||
| 271 | } | ||||||||||||||||||||||||||||||||||
| 272 | |||||||||||||||||||||||||||||||||||
| 273 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 274 | |||||||||||||||||||||||||||||||||||
| 275 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||
| 276 | inline int call_bind(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||
| 277 | socket_type s, const void* addr, std::size_t addrlen) | ||||||||||||||||||||||||||||||||||
| 278 | { | ||||||||||||||||||||||||||||||||||
| 279 | return ::bind(s, static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||
| 280 | (SockLenType)addrlen); | ||||||||||||||||||||||||||||||||||
| 281 | } | ||||||||||||||||||||||||||||||||||
| 282 | |||||||||||||||||||||||||||||||||||
| 283 | int bind(socket_type s, const void* addr, | ||||||||||||||||||||||||||||||||||
| 284 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 285 | { | ||||||||||||||||||||||||||||||||||
| 286 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 287 | { | ||||||||||||||||||||||||||||||||||
| 288 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 289 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 290 | } | ||||||||||||||||||||||||||||||||||
| 291 | |||||||||||||||||||||||||||||||||||
| 292 | int result = call_bind(&msghdr::msg_namelen, s, addr, addrlen); | ||||||||||||||||||||||||||||||||||
| 293 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 294 | return result; | ||||||||||||||||||||||||||||||||||
| 295 | } | ||||||||||||||||||||||||||||||||||
| 296 | |||||||||||||||||||||||||||||||||||
| 297 | int close(socket_type s, state_type& state, | ||||||||||||||||||||||||||||||||||
| 298 | bool destruction, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 299 | { | ||||||||||||||||||||||||||||||||||
| 300 | int result = 0; | ||||||||||||||||||||||||||||||||||
| 301 | if (s != invalid_socket) | ||||||||||||||||||||||||||||||||||
| 302 | { | ||||||||||||||||||||||||||||||||||
| 303 | // We don't want the destructor to block, so set the socket to linger in | ||||||||||||||||||||||||||||||||||
| 304 | // the background. If the user doesn't like this behaviour then they need | ||||||||||||||||||||||||||||||||||
| 305 | // to explicitly close the socket. | ||||||||||||||||||||||||||||||||||
| 306 | if (destruction && (state & user_set_linger)) | ||||||||||||||||||||||||||||||||||
| 307 | { | ||||||||||||||||||||||||||||||||||
| 308 | ::linger opt; | ||||||||||||||||||||||||||||||||||
| 309 | opt.l_onoff = 0; | ||||||||||||||||||||||||||||||||||
| 310 | opt.l_linger = 0; | ||||||||||||||||||||||||||||||||||
| 311 | boost::system::error_code ignored_ec; | ||||||||||||||||||||||||||||||||||
| 312 | socket_ops::setsockopt(s, state, SOL_SOCKET1, | ||||||||||||||||||||||||||||||||||
| 313 | SO_LINGER13, &opt, sizeof(opt), ignored_ec); | ||||||||||||||||||||||||||||||||||
| 314 | } | ||||||||||||||||||||||||||||||||||
| 315 | |||||||||||||||||||||||||||||||||||
| 316 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 317 | result = ::closesocket(s); | ||||||||||||||||||||||||||||||||||
| 318 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 319 | result = ::close(s); | ||||||||||||||||||||||||||||||||||
| 320 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 321 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 322 | |||||||||||||||||||||||||||||||||||
| 323 | if (result != 0 | ||||||||||||||||||||||||||||||||||
| 324 | && (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 325 | || ec == boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||
| 326 | { | ||||||||||||||||||||||||||||||||||
| 327 | // According to UNIX Network Programming Vol. 1, it is possible for | ||||||||||||||||||||||||||||||||||
| 328 | // close() to fail with EWOULDBLOCK under certain circumstances. What | ||||||||||||||||||||||||||||||||||
| 329 | // isn't clear is the state of the descriptor after this error. The one | ||||||||||||||||||||||||||||||||||
| 330 | // current OS where this behaviour is seen, Windows, says that the socket | ||||||||||||||||||||||||||||||||||
| 331 | // remains open. Therefore we'll put the descriptor back into blocking | ||||||||||||||||||||||||||||||||||
| 332 | // mode and have another attempt at closing it. | ||||||||||||||||||||||||||||||||||
| 333 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 334 | ioctl_arg_type arg = 0; | ||||||||||||||||||||||||||||||||||
| 335 | ::ioctlsocket(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||
| 336 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 337 | # if defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||
| 338 | int flags = ::fcntl(s, F_GETFL3, 0); | ||||||||||||||||||||||||||||||||||
| 339 | if (flags >= 0) | ||||||||||||||||||||||||||||||||||
| 340 | ::fcntl(s, F_SETFL4, flags & ~O_NONBLOCK04000); | ||||||||||||||||||||||||||||||||||
| 341 | # else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||
| 342 | ioctl_arg_type arg = 0; | ||||||||||||||||||||||||||||||||||
| 343 | ::ioctl(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||
| 344 | # endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||
| 345 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 346 | state &= ~non_blocking; | ||||||||||||||||||||||||||||||||||
| 347 | |||||||||||||||||||||||||||||||||||
| 348 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 349 | result = ::closesocket(s); | ||||||||||||||||||||||||||||||||||
| 350 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 351 | result = ::close(s); | ||||||||||||||||||||||||||||||||||
| 352 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 353 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 354 | } | ||||||||||||||||||||||||||||||||||
| 355 | } | ||||||||||||||||||||||||||||||||||
| 356 | |||||||||||||||||||||||||||||||||||
| 357 | return result; | ||||||||||||||||||||||||||||||||||
| 358 | } | ||||||||||||||||||||||||||||||||||
| 359 | |||||||||||||||||||||||||||||||||||
| 360 | bool set_user_non_blocking(socket_type s, | ||||||||||||||||||||||||||||||||||
| 361 | state_type& state, bool value, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 362 | { | ||||||||||||||||||||||||||||||||||
| 363 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 364 | { | ||||||||||||||||||||||||||||||||||
| 365 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 366 | return false; | ||||||||||||||||||||||||||||||||||
| 367 | } | ||||||||||||||||||||||||||||||||||
| 368 | |||||||||||||||||||||||||||||||||||
| 369 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 370 | ioctl_arg_type arg = (value ? 1 : 0); | ||||||||||||||||||||||||||||||||||
| 371 | int result = ::ioctlsocket(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||
| 372 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 373 | #elif defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||
| 374 | int result = ::fcntl(s, F_GETFL3, 0); | ||||||||||||||||||||||||||||||||||
| 375 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 376 | if (result >= 0) | ||||||||||||||||||||||||||||||||||
| 377 | { | ||||||||||||||||||||||||||||||||||
| 378 | int flag = (value ? (result | O_NONBLOCK04000) : (result & ~O_NONBLOCK04000)); | ||||||||||||||||||||||||||||||||||
| 379 | result = ::fcntl(s, F_SETFL4, flag); | ||||||||||||||||||||||||||||||||||
| 380 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 381 | } | ||||||||||||||||||||||||||||||||||
| 382 | #else | ||||||||||||||||||||||||||||||||||
| 383 | ioctl_arg_type arg = (value ? 1 : 0); | ||||||||||||||||||||||||||||||||||
| 384 | int result = ::ioctl(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||
| 385 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 386 | #endif | ||||||||||||||||||||||||||||||||||
| 387 | |||||||||||||||||||||||||||||||||||
| 388 | if (result >= 0) | ||||||||||||||||||||||||||||||||||
| 389 | { | ||||||||||||||||||||||||||||||||||
| 390 | if (value) | ||||||||||||||||||||||||||||||||||
| 391 | state |= user_set_non_blocking; | ||||||||||||||||||||||||||||||||||
| 392 | else | ||||||||||||||||||||||||||||||||||
| 393 | { | ||||||||||||||||||||||||||||||||||
| 394 | // Clearing the user-set non-blocking mode always overrides any | ||||||||||||||||||||||||||||||||||
| 395 | // internally-set non-blocking flag. Any subsequent asynchronous | ||||||||||||||||||||||||||||||||||
| 396 | // operations will need to re-enable non-blocking I/O. | ||||||||||||||||||||||||||||||||||
| 397 | state &= ~(user_set_non_blocking | internal_non_blocking); | ||||||||||||||||||||||||||||||||||
| 398 | } | ||||||||||||||||||||||||||||||||||
| 399 | return true; | ||||||||||||||||||||||||||||||||||
| 400 | } | ||||||||||||||||||||||||||||||||||
| 401 | |||||||||||||||||||||||||||||||||||
| 402 | return false; | ||||||||||||||||||||||||||||||||||
| 403 | } | ||||||||||||||||||||||||||||||||||
| 404 | |||||||||||||||||||||||||||||||||||
| 405 | bool set_internal_non_blocking(socket_type s, | ||||||||||||||||||||||||||||||||||
| 406 | state_type& state, bool value, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 407 | { | ||||||||||||||||||||||||||||||||||
| 408 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 409 | { | ||||||||||||||||||||||||||||||||||
| 410 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 411 | return false; | ||||||||||||||||||||||||||||||||||
| 412 | } | ||||||||||||||||||||||||||||||||||
| 413 | |||||||||||||||||||||||||||||||||||
| 414 | if (!value && (state & user_set_non_blocking)) | ||||||||||||||||||||||||||||||||||
| 415 | { | ||||||||||||||||||||||||||||||||||
| 416 | // It does not make sense to clear the internal non-blocking flag if the | ||||||||||||||||||||||||||||||||||
| 417 | // user still wants non-blocking behaviour. Return an error and let the | ||||||||||||||||||||||||||||||||||
| 418 | // caller figure out whether to update the user-set non-blocking flag. | ||||||||||||||||||||||||||||||||||
| 419 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 420 | return false; | ||||||||||||||||||||||||||||||||||
| 421 | } | ||||||||||||||||||||||||||||||||||
| 422 | |||||||||||||||||||||||||||||||||||
| 423 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 424 | ioctl_arg_type arg = (value ? 1 : 0); | ||||||||||||||||||||||||||||||||||
| 425 | int result = ::ioctlsocket(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||
| 426 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 427 | #elif defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||
| 428 | int result = ::fcntl(s, F_GETFL3, 0); | ||||||||||||||||||||||||||||||||||
| 429 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 430 | if (result >= 0) | ||||||||||||||||||||||||||||||||||
| 431 | { | ||||||||||||||||||||||||||||||||||
| 432 | int flag = (value ? (result | O_NONBLOCK04000) : (result & ~O_NONBLOCK04000)); | ||||||||||||||||||||||||||||||||||
| 433 | result = ::fcntl(s, F_SETFL4, flag); | ||||||||||||||||||||||||||||||||||
| 434 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 435 | } | ||||||||||||||||||||||||||||||||||
| 436 | #else | ||||||||||||||||||||||||||||||||||
| 437 | ioctl_arg_type arg = (value ? 1 : 0); | ||||||||||||||||||||||||||||||||||
| 438 | int result = ::ioctl(s, FIONBIO0x5421, &arg); | ||||||||||||||||||||||||||||||||||
| 439 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 440 | #endif | ||||||||||||||||||||||||||||||||||
| 441 | |||||||||||||||||||||||||||||||||||
| 442 | if (result >= 0) | ||||||||||||||||||||||||||||||||||
| 443 | { | ||||||||||||||||||||||||||||||||||
| 444 | if (value) | ||||||||||||||||||||||||||||||||||
| 445 | state |= internal_non_blocking; | ||||||||||||||||||||||||||||||||||
| 446 | else | ||||||||||||||||||||||||||||||||||
| 447 | state &= ~internal_non_blocking; | ||||||||||||||||||||||||||||||||||
| 448 | return true; | ||||||||||||||||||||||||||||||||||
| 449 | } | ||||||||||||||||||||||||||||||||||
| 450 | |||||||||||||||||||||||||||||||||||
| 451 | return false; | ||||||||||||||||||||||||||||||||||
| 452 | } | ||||||||||||||||||||||||||||||||||
| 453 | |||||||||||||||||||||||||||||||||||
| 454 | int shutdown(socket_type s, int what, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 455 | { | ||||||||||||||||||||||||||||||||||
| 456 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 457 | { | ||||||||||||||||||||||||||||||||||
| 458 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 459 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 460 | } | ||||||||||||||||||||||||||||||||||
| 461 | |||||||||||||||||||||||||||||||||||
| 462 | int result = ::shutdown(s, what); | ||||||||||||||||||||||||||||||||||
| 463 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 464 | return result; | ||||||||||||||||||||||||||||||||||
| 465 | } | ||||||||||||||||||||||||||||||||||
| 466 | |||||||||||||||||||||||||||||||||||
| 467 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||
| 468 | inline int call_connect(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||
| 469 | socket_type s, const void* addr, std::size_t addrlen) | ||||||||||||||||||||||||||||||||||
| 470 | { | ||||||||||||||||||||||||||||||||||
| 471 | return ::connect(s, static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||
| 472 | (SockLenType)addrlen); | ||||||||||||||||||||||||||||||||||
| 473 | } | ||||||||||||||||||||||||||||||||||
| 474 | |||||||||||||||||||||||||||||||||||
| 475 | int connect(socket_type s, const void* addr, | ||||||||||||||||||||||||||||||||||
| 476 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 477 | { | ||||||||||||||||||||||||||||||||||
| 478 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 479 | { | ||||||||||||||||||||||||||||||||||
| 480 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 481 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 482 | } | ||||||||||||||||||||||||||||||||||
| 483 | |||||||||||||||||||||||||||||||||||
| 484 | int result = call_connect(&msghdr::msg_namelen, s, addr, addrlen); | ||||||||||||||||||||||||||||||||||
| 485 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 486 | #if defined(__linux__1) | ||||||||||||||||||||||||||||||||||
| 487 | if (result != 0 && ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 488 | { | ||||||||||||||||||||||||||||||||||
| 489 | if (static_cast<const socket_addr_type*>(addr)->sa_family == AF_UNIX1) | ||||||||||||||||||||||||||||||||||
| 490 | ec = boost::asio::error::in_progress; | ||||||||||||||||||||||||||||||||||
| 491 | else | ||||||||||||||||||||||||||||||||||
| 492 | ec = boost::asio::error::no_buffer_space; | ||||||||||||||||||||||||||||||||||
| 493 | } | ||||||||||||||||||||||||||||||||||
| 494 | #endif // defined(__linux__) | ||||||||||||||||||||||||||||||||||
| 495 | return result; | ||||||||||||||||||||||||||||||||||
| 496 | } | ||||||||||||||||||||||||||||||||||
| 497 | |||||||||||||||||||||||||||||||||||
| 498 | void sync_connect(socket_type s, const void* addr, | ||||||||||||||||||||||||||||||||||
| 499 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 500 | { | ||||||||||||||||||||||||||||||||||
| 501 | // Perform the connect operation. | ||||||||||||||||||||||||||||||||||
| 502 | socket_ops::connect(s, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||
| 503 | if (ec != boost::asio::error::in_progress | ||||||||||||||||||||||||||||||||||
| 504 | && ec != boost::asio::error::would_block) | ||||||||||||||||||||||||||||||||||
| 505 | { | ||||||||||||||||||||||||||||||||||
| 506 | // The connect operation finished immediately. | ||||||||||||||||||||||||||||||||||
| 507 | return; | ||||||||||||||||||||||||||||||||||
| 508 | } | ||||||||||||||||||||||||||||||||||
| 509 | |||||||||||||||||||||||||||||||||||
| 510 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||
| 511 | if (socket_ops::poll_connect(s, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||
| 512 | return; | ||||||||||||||||||||||||||||||||||
| 513 | |||||||||||||||||||||||||||||||||||
| 514 | // Get the error code from the connect operation. | ||||||||||||||||||||||||||||||||||
| 515 | int connect_error = 0; | ||||||||||||||||||||||||||||||||||
| 516 | size_t connect_error_len = sizeof(connect_error); | ||||||||||||||||||||||||||||||||||
| 517 | if (socket_ops::getsockopt(s, 0, SOL_SOCKET1, SO_ERROR4, | ||||||||||||||||||||||||||||||||||
| 518 | &connect_error, &connect_error_len, ec) == socket_error_retval) | ||||||||||||||||||||||||||||||||||
| 519 | return; | ||||||||||||||||||||||||||||||||||
| 520 | |||||||||||||||||||||||||||||||||||
| 521 | // Return the result of the connect operation. | ||||||||||||||||||||||||||||||||||
| 522 | ec = boost::system::error_code(connect_error, | ||||||||||||||||||||||||||||||||||
| 523 | boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||
| 524 | } | ||||||||||||||||||||||||||||||||||
| 525 | |||||||||||||||||||||||||||||||||||
| 526 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 527 | |||||||||||||||||||||||||||||||||||
| 528 | void complete_iocp_connect(socket_type s, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 529 | { | ||||||||||||||||||||||||||||||||||
| 530 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||
| 531 | switch (ec.value()) | ||||||||||||||||||||||||||||||||||
| 532 | { | ||||||||||||||||||||||||||||||||||
| 533 | case ERROR_CONNECTION_REFUSED: | ||||||||||||||||||||||||||||||||||
| 534 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 535 | break; | ||||||||||||||||||||||||||||||||||
| 536 | case ERROR_NETWORK_UNREACHABLE: | ||||||||||||||||||||||||||||||||||
| 537 | ec = boost::asio::error::network_unreachable; | ||||||||||||||||||||||||||||||||||
| 538 | break; | ||||||||||||||||||||||||||||||||||
| 539 | case ERROR_HOST_UNREACHABLE: | ||||||||||||||||||||||||||||||||||
| 540 | ec = boost::asio::error::host_unreachable; | ||||||||||||||||||||||||||||||||||
| 541 | break; | ||||||||||||||||||||||||||||||||||
| 542 | case ERROR_SEM_TIMEOUT: | ||||||||||||||||||||||||||||||||||
| 543 | ec = boost::asio::error::timed_out; | ||||||||||||||||||||||||||||||||||
| 544 | break; | ||||||||||||||||||||||||||||||||||
| 545 | default: | ||||||||||||||||||||||||||||||||||
| 546 | break; | ||||||||||||||||||||||||||||||||||
| 547 | } | ||||||||||||||||||||||||||||||||||
| 548 | |||||||||||||||||||||||||||||||||||
| 549 | if (!ec) | ||||||||||||||||||||||||||||||||||
| 550 | { | ||||||||||||||||||||||||||||||||||
| 551 | // Need to set the SO_UPDATE_CONNECT_CONTEXT option so that getsockname | ||||||||||||||||||||||||||||||||||
| 552 | // and getpeername will work on the connected socket. | ||||||||||||||||||||||||||||||||||
| 553 | socket_ops::state_type state = 0; | ||||||||||||||||||||||||||||||||||
| 554 | const int so_update_connect_context = 0x7010; | ||||||||||||||||||||||||||||||||||
| 555 | socket_ops::setsockopt(s, state, SOL_SOCKET1, | ||||||||||||||||||||||||||||||||||
| 556 | so_update_connect_context, 0, 0, ec); | ||||||||||||||||||||||||||||||||||
| 557 | } | ||||||||||||||||||||||||||||||||||
| 558 | } | ||||||||||||||||||||||||||||||||||
| 559 | |||||||||||||||||||||||||||||||||||
| 560 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 561 | |||||||||||||||||||||||||||||||||||
| 562 | bool non_blocking_connect(socket_type s, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 563 | { | ||||||||||||||||||||||||||||||||||
| 564 | // Check if the connect operation has finished. This is required since we may | ||||||||||||||||||||||||||||||||||
| 565 | // get spurious readiness notifications from the reactor. | ||||||||||||||||||||||||||||||||||
| 566 | #if defined(BOOST_ASIO_WINDOWS) \ | ||||||||||||||||||||||||||||||||||
| 567 | || defined(__CYGWIN__) \ | ||||||||||||||||||||||||||||||||||
| 568 | || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 569 | fd_set write_fds; | ||||||||||||||||||||||||||||||||||
| 570 | 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); | ||||||||||||||||||||||||||||||||||
| 571 | 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))))))); | ||||||||||||||||||||||||||||||||||
| 572 | fd_set except_fds; | ||||||||||||||||||||||||||||||||||
| 573 | 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); | ||||||||||||||||||||||||||||||||||
| 574 | 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))))))); | ||||||||||||||||||||||||||||||||||
| 575 | timeval zero_timeout; | ||||||||||||||||||||||||||||||||||
| 576 | zero_timeout.tv_sec = 0; | ||||||||||||||||||||||||||||||||||
| 577 | zero_timeout.tv_usec = 0; | ||||||||||||||||||||||||||||||||||
| 578 | int ready = ::select(s + 1, 0, &write_fds, &except_fds, &zero_timeout); | ||||||||||||||||||||||||||||||||||
| 579 | #else // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||
| 580 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 581 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 582 | pollfd fds; | ||||||||||||||||||||||||||||||||||
| 583 | fds.fd = s; | ||||||||||||||||||||||||||||||||||
| 584 | fds.events = POLLOUT0x004; | ||||||||||||||||||||||||||||||||||
| 585 | fds.revents = 0; | ||||||||||||||||||||||||||||||||||
| 586 | int ready = ::poll(&fds, 1, 0); | ||||||||||||||||||||||||||||||||||
| 587 | #endif // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||
| 588 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 589 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 590 | if (ready == 0) | ||||||||||||||||||||||||||||||||||
| 591 | { | ||||||||||||||||||||||||||||||||||
| 592 | // The asynchronous connect operation is still in progress. | ||||||||||||||||||||||||||||||||||
| 593 | return false; | ||||||||||||||||||||||||||||||||||
| 594 | } | ||||||||||||||||||||||||||||||||||
| 595 | |||||||||||||||||||||||||||||||||||
| 596 | // Get the error code from the connect operation. | ||||||||||||||||||||||||||||||||||
| 597 | int connect_error = 0; | ||||||||||||||||||||||||||||||||||
| 598 | size_t connect_error_len = sizeof(connect_error); | ||||||||||||||||||||||||||||||||||
| 599 | if (socket_ops::getsockopt(s, 0, SOL_SOCKET1, SO_ERROR4, | ||||||||||||||||||||||||||||||||||
| 600 | &connect_error, &connect_error_len, ec) == 0) | ||||||||||||||||||||||||||||||||||
| 601 | { | ||||||||||||||||||||||||||||||||||
| 602 | if (connect_error) | ||||||||||||||||||||||||||||||||||
| 603 | { | ||||||||||||||||||||||||||||||||||
| 604 | ec = boost::system::error_code(connect_error, | ||||||||||||||||||||||||||||||||||
| 605 | boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||
| 606 | } | ||||||||||||||||||||||||||||||||||
| 607 | else | ||||||||||||||||||||||||||||||||||
| 608 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 609 | } | ||||||||||||||||||||||||||||||||||
| 610 | |||||||||||||||||||||||||||||||||||
| 611 | return true; | ||||||||||||||||||||||||||||||||||
| 612 | } | ||||||||||||||||||||||||||||||||||
| 613 | |||||||||||||||||||||||||||||||||||
| 614 | int socketpair(int af, int type, int protocol, | ||||||||||||||||||||||||||||||||||
| 615 | socket_type sv[2], boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 616 | { | ||||||||||||||||||||||||||||||||||
| 617 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 618 | (void)(af); | ||||||||||||||||||||||||||||||||||
| 619 | (void)(type); | ||||||||||||||||||||||||||||||||||
| 620 | (void)(protocol); | ||||||||||||||||||||||||||||||||||
| 621 | (void)(sv); | ||||||||||||||||||||||||||||||||||
| 622 | ec = boost::asio::error::operation_not_supported; | ||||||||||||||||||||||||||||||||||
| 623 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 624 | #else | ||||||||||||||||||||||||||||||||||
| 625 | int result = ::socketpair(af, type, protocol, sv); | ||||||||||||||||||||||||||||||||||
| 626 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 627 | return result; | ||||||||||||||||||||||||||||||||||
| 628 | #endif | ||||||||||||||||||||||||||||||||||
| 629 | } | ||||||||||||||||||||||||||||||||||
| 630 | |||||||||||||||||||||||||||||||||||
| 631 | bool sockatmark(socket_type s, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 632 | { | ||||||||||||||||||||||||||||||||||
| 633 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 634 | { | ||||||||||||||||||||||||||||||||||
| 635 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 636 | return false; | ||||||||||||||||||||||||||||||||||
| 637 | } | ||||||||||||||||||||||||||||||||||
| 638 | |||||||||||||||||||||||||||||||||||
| 639 | #if defined(SIOCATMARK0x8905) | ||||||||||||||||||||||||||||||||||
| 640 | ioctl_arg_type value = 0; | ||||||||||||||||||||||||||||||||||
| 641 | # if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 642 | int result = ::ioctlsocket(s, SIOCATMARK0x8905, &value); | ||||||||||||||||||||||||||||||||||
| 643 | # else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 644 | int result = ::ioctl(s, SIOCATMARK0x8905, &value); | ||||||||||||||||||||||||||||||||||
| 645 | # endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 646 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 647 | # if defined(ENOTTY25) | ||||||||||||||||||||||||||||||||||
| 648 | if (ec.value() == ENOTTY25) | ||||||||||||||||||||||||||||||||||
| 649 | ec = boost::asio::error::not_socket; | ||||||||||||||||||||||||||||||||||
| 650 | # endif // defined(ENOTTY) | ||||||||||||||||||||||||||||||||||
| 651 | #else // defined(SIOCATMARK) | ||||||||||||||||||||||||||||||||||
| 652 | int value = ::sockatmark(s); | ||||||||||||||||||||||||||||||||||
| 653 | get_last_error(ec, value < 0); | ||||||||||||||||||||||||||||||||||
| 654 | #endif // defined(SIOCATMARK) | ||||||||||||||||||||||||||||||||||
| 655 | |||||||||||||||||||||||||||||||||||
| 656 | return ec ? false : value != 0; | ||||||||||||||||||||||||||||||||||
| 657 | } | ||||||||||||||||||||||||||||||||||
| 658 | |||||||||||||||||||||||||||||||||||
| 659 | size_t available(socket_type s, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 660 | { | ||||||||||||||||||||||||||||||||||
| 661 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 662 | { | ||||||||||||||||||||||||||||||||||
| 663 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 664 | return 0; | ||||||||||||||||||||||||||||||||||
| 665 | } | ||||||||||||||||||||||||||||||||||
| 666 | |||||||||||||||||||||||||||||||||||
| 667 | ioctl_arg_type value = 0; | ||||||||||||||||||||||||||||||||||
| 668 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 669 | int result = ::ioctlsocket(s, FIONREAD0x541B, &value); | ||||||||||||||||||||||||||||||||||
| 670 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 671 | int result = ::ioctl(s, FIONREAD0x541B, &value); | ||||||||||||||||||||||||||||||||||
| 672 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 673 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 674 | #if defined(ENOTTY25) | ||||||||||||||||||||||||||||||||||
| 675 | if (ec.value() == ENOTTY25) | ||||||||||||||||||||||||||||||||||
| 676 | ec = boost::asio::error::not_socket; | ||||||||||||||||||||||||||||||||||
| 677 | #endif // defined(ENOTTY) | ||||||||||||||||||||||||||||||||||
| 678 | |||||||||||||||||||||||||||||||||||
| 679 | return ec ? static_cast<size_t>(0) : static_cast<size_t>(value); | ||||||||||||||||||||||||||||||||||
| 680 | } | ||||||||||||||||||||||||||||||||||
| 681 | |||||||||||||||||||||||||||||||||||
| 682 | int listen(socket_type s, int backlog, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 683 | { | ||||||||||||||||||||||||||||||||||
| 684 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 685 | { | ||||||||||||||||||||||||||||||||||
| 686 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 687 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 688 | } | ||||||||||||||||||||||||||||||||||
| 689 | |||||||||||||||||||||||||||||||||||
| 690 | int result = ::listen(s, backlog); | ||||||||||||||||||||||||||||||||||
| 691 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 692 | return result; | ||||||||||||||||||||||||||||||||||
| 693 | } | ||||||||||||||||||||||||||||||||||
| 694 | |||||||||||||||||||||||||||||||||||
| 695 | inline void init_buf_iov_base(void*& base, void* addr) | ||||||||||||||||||||||||||||||||||
| 696 | { | ||||||||||||||||||||||||||||||||||
| 697 | base = addr; | ||||||||||||||||||||||||||||||||||
| 698 | } | ||||||||||||||||||||||||||||||||||
| 699 | |||||||||||||||||||||||||||||||||||
| 700 | template <typename T> | ||||||||||||||||||||||||||||||||||
| 701 | inline void init_buf_iov_base(T& base, void* addr) | ||||||||||||||||||||||||||||||||||
| 702 | { | ||||||||||||||||||||||||||||||||||
| 703 | base = static_cast<T>(addr); | ||||||||||||||||||||||||||||||||||
| 704 | } | ||||||||||||||||||||||||||||||||||
| 705 | |||||||||||||||||||||||||||||||||||
| 706 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 707 | typedef WSABUF buf; | ||||||||||||||||||||||||||||||||||
| 708 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 709 | typedef iovec buf; | ||||||||||||||||||||||||||||||||||
| 710 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 711 | |||||||||||||||||||||||||||||||||||
| 712 | void init_buf(buf& b, void* data, size_t size) | ||||||||||||||||||||||||||||||||||
| 713 | { | ||||||||||||||||||||||||||||||||||
| 714 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 715 | b.buf = static_cast<char*>(data); | ||||||||||||||||||||||||||||||||||
| 716 | b.len = static_cast<u_long>(size); | ||||||||||||||||||||||||||||||||||
| 717 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 718 | init_buf_iov_base(b.iov_base, data); | ||||||||||||||||||||||||||||||||||
| 719 | b.iov_len = size; | ||||||||||||||||||||||||||||||||||
| 720 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 721 | } | ||||||||||||||||||||||||||||||||||
| 722 | |||||||||||||||||||||||||||||||||||
| 723 | void init_buf(buf& b, const void* data, size_t size) | ||||||||||||||||||||||||||||||||||
| 724 | { | ||||||||||||||||||||||||||||||||||
| 725 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 726 | b.buf = static_cast<char*>(const_cast<void*>(data)); | ||||||||||||||||||||||||||||||||||
| 727 | b.len = static_cast<u_long>(size); | ||||||||||||||||||||||||||||||||||
| 728 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 729 | init_buf_iov_base(b.iov_base, const_cast<void*>(data)); | ||||||||||||||||||||||||||||||||||
| 730 | b.iov_len = size; | ||||||||||||||||||||||||||||||||||
| 731 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 732 | } | ||||||||||||||||||||||||||||||||||
| 733 | |||||||||||||||||||||||||||||||||||
| 734 | inline void init_msghdr_msg_name(void*& name, void* addr) | ||||||||||||||||||||||||||||||||||
| 735 | { | ||||||||||||||||||||||||||||||||||
| 736 | name = static_cast<socket_addr_type*>(addr); | ||||||||||||||||||||||||||||||||||
| 737 | } | ||||||||||||||||||||||||||||||||||
| 738 | |||||||||||||||||||||||||||||||||||
| 739 | inline void init_msghdr_msg_name(void*& name, const socket_addr_type* addr) | ||||||||||||||||||||||||||||||||||
| 740 | { | ||||||||||||||||||||||||||||||||||
| 741 | name = const_cast<socket_addr_type*>(addr); | ||||||||||||||||||||||||||||||||||
| 742 | } | ||||||||||||||||||||||||||||||||||
| 743 | |||||||||||||||||||||||||||||||||||
| 744 | template <typename T> | ||||||||||||||||||||||||||||||||||
| 745 | inline void init_msghdr_msg_name(T& name, void* addr) | ||||||||||||||||||||||||||||||||||
| 746 | { | ||||||||||||||||||||||||||||||||||
| 747 | name = static_cast<T>(addr); | ||||||||||||||||||||||||||||||||||
| 748 | } | ||||||||||||||||||||||||||||||||||
| 749 | |||||||||||||||||||||||||||||||||||
| 750 | template <typename T> | ||||||||||||||||||||||||||||||||||
| 751 | inline void init_msghdr_msg_name(T& name, const void* addr) | ||||||||||||||||||||||||||||||||||
| 752 | { | ||||||||||||||||||||||||||||||||||
| 753 | name = static_cast<T>(const_cast<void*>(addr)); | ||||||||||||||||||||||||||||||||||
| 754 | } | ||||||||||||||||||||||||||||||||||
| 755 | |||||||||||||||||||||||||||||||||||
| 756 | signed_size_type recv(socket_type s, buf* bufs, size_t count, | ||||||||||||||||||||||||||||||||||
| 757 | int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 758 | { | ||||||||||||||||||||||||||||||||||
| 759 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 760 | // Receive some data. | ||||||||||||||||||||||||||||||||||
| 761 | DWORD recv_buf_count = static_cast<DWORD>(count); | ||||||||||||||||||||||||||||||||||
| 762 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 763 | DWORD recv_flags = flags; | ||||||||||||||||||||||||||||||||||
| 764 | int result = ::WSARecv(s, bufs, recv_buf_count, | ||||||||||||||||||||||||||||||||||
| 765 | &bytes_transferred, &recv_flags, 0, 0); | ||||||||||||||||||||||||||||||||||
| 766 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 767 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 768 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 769 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 770 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 771 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||
| 772 | result = 0; | ||||||||||||||||||||||||||||||||||
| 773 | if (result != 0) | ||||||||||||||||||||||||||||||||||
| 774 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 775 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 776 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||
| 777 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 778 | msghdr msg = msghdr(); | ||||||||||||||||||||||||||||||||||
| 779 | msg.msg_iov = bufs; | ||||||||||||||||||||||||||||||||||
| 780 | msg.msg_iovlen = static_cast<int>(count); | ||||||||||||||||||||||||||||||||||
| 781 | signed_size_type result = ::recvmsg(s, &msg, flags); | ||||||||||||||||||||||||||||||||||
| 782 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 783 | return result; | ||||||||||||||||||||||||||||||||||
| 784 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 785 | } | ||||||||||||||||||||||||||||||||||
| 786 | |||||||||||||||||||||||||||||||||||
| 787 | signed_size_type recv1(socket_type s, void* data, size_t size, | ||||||||||||||||||||||||||||||||||
| 788 | int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 789 | { | ||||||||||||||||||||||||||||||||||
| 790 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 791 | // Receive some data. | ||||||||||||||||||||||||||||||||||
| 792 | WSABUF buf; | ||||||||||||||||||||||||||||||||||
| 793 | buf.buf = const_cast<char*>(static_cast<const char*>(data)); | ||||||||||||||||||||||||||||||||||
| 794 | buf.len = static_cast<ULONG>(size); | ||||||||||||||||||||||||||||||||||
| 795 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 796 | DWORD recv_flags = flags; | ||||||||||||||||||||||||||||||||||
| 797 | int result = ::WSARecv(s, &buf, 1, | ||||||||||||||||||||||||||||||||||
| 798 | &bytes_transferred, &recv_flags, 0, 0); | ||||||||||||||||||||||||||||||||||
| 799 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 800 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 801 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 802 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 803 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 804 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||
| 805 | result = 0; | ||||||||||||||||||||||||||||||||||
| 806 | if (result != 0) | ||||||||||||||||||||||||||||||||||
| 807 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 808 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 809 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||
| 810 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 811 | signed_size_type result = ::recv(s, static_cast<char*>(data), size, flags); | ||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
| 812 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 813 | return result; | ||||||||||||||||||||||||||||||||||
| 814 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 815 | } | ||||||||||||||||||||||||||||||||||
| 816 | |||||||||||||||||||||||||||||||||||
| 817 | size_t sync_recv(socket_type s, state_type state, buf* bufs, | ||||||||||||||||||||||||||||||||||
| 818 | size_t count, int flags, bool all_empty, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 819 | { | ||||||||||||||||||||||||||||||||||
| 820 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 821 | { | ||||||||||||||||||||||||||||||||||
| 822 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 823 | return 0; | ||||||||||||||||||||||||||||||||||
| 824 | } | ||||||||||||||||||||||||||||||||||
| 825 | |||||||||||||||||||||||||||||||||||
| 826 | // A request to read 0 bytes on a stream is a no-op. | ||||||||||||||||||||||||||||||||||
| 827 | if (all_empty && (state & stream_oriented)) | ||||||||||||||||||||||||||||||||||
| 828 | { | ||||||||||||||||||||||||||||||||||
| 829 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 830 | return 0; | ||||||||||||||||||||||||||||||||||
| 831 | } | ||||||||||||||||||||||||||||||||||
| 832 | |||||||||||||||||||||||||||||||||||
| 833 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 834 | for (;;) | ||||||||||||||||||||||||||||||||||
| 835 | { | ||||||||||||||||||||||||||||||||||
| 836 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||
| 837 | signed_size_type bytes = socket_ops::recv(s, bufs, count, flags, ec); | ||||||||||||||||||||||||||||||||||
| 838 | |||||||||||||||||||||||||||||||||||
| 839 | // Check for EOF. | ||||||||||||||||||||||||||||||||||
| 840 | if ((state & stream_oriented) && bytes == 0) | ||||||||||||||||||||||||||||||||||
| 841 | { | ||||||||||||||||||||||||||||||||||
| 842 | ec = boost::asio::error::eof; | ||||||||||||||||||||||||||||||||||
| 843 | return 0; | ||||||||||||||||||||||||||||||||||
| 844 | } | ||||||||||||||||||||||||||||||||||
| 845 | |||||||||||||||||||||||||||||||||||
| 846 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 847 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 848 | return bytes; | ||||||||||||||||||||||||||||||||||
| 849 | |||||||||||||||||||||||||||||||||||
| 850 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 851 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 852 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 853 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||
| 854 | return 0; | ||||||||||||||||||||||||||||||||||
| 855 | |||||||||||||||||||||||||||||||||||
| 856 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||
| 857 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||
| 858 | return 0; | ||||||||||||||||||||||||||||||||||
| 859 | } | ||||||||||||||||||||||||||||||||||
| 860 | } | ||||||||||||||||||||||||||||||||||
| 861 | |||||||||||||||||||||||||||||||||||
| 862 | size_t sync_recv1(socket_type s, state_type state, void* data, | ||||||||||||||||||||||||||||||||||
| 863 | size_t size, int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 864 | { | ||||||||||||||||||||||||||||||||||
| 865 | if (s
| ||||||||||||||||||||||||||||||||||
| 866 | { | ||||||||||||||||||||||||||||||||||
| 867 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 868 | return 0; | ||||||||||||||||||||||||||||||||||
| 869 | } | ||||||||||||||||||||||||||||||||||
| 870 | |||||||||||||||||||||||||||||||||||
| 871 | // A request to read 0 bytes on a stream is a no-op. | ||||||||||||||||||||||||||||||||||
| 872 | if (size == 0 && (state & stream_oriented)) | ||||||||||||||||||||||||||||||||||
| 873 | { | ||||||||||||||||||||||||||||||||||
| 874 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 875 | return 0; | ||||||||||||||||||||||||||||||||||
| 876 | } | ||||||||||||||||||||||||||||||||||
| 877 | |||||||||||||||||||||||||||||||||||
| 878 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 879 | for (;;) | ||||||||||||||||||||||||||||||||||
| 880 | { | ||||||||||||||||||||||||||||||||||
| 881 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||
| 882 | signed_size_type bytes = socket_ops::recv1(s, data, size, flags, ec); | ||||||||||||||||||||||||||||||||||
| 883 | |||||||||||||||||||||||||||||||||||
| 884 | // Check for EOF. | ||||||||||||||||||||||||||||||||||
| 885 | if ((state & stream_oriented) && bytes == 0) | ||||||||||||||||||||||||||||||||||
| 886 | { | ||||||||||||||||||||||||||||||||||
| 887 | ec = boost::asio::error::eof; | ||||||||||||||||||||||||||||||||||
| 888 | return 0; | ||||||||||||||||||||||||||||||||||
| 889 | } | ||||||||||||||||||||||||||||||||||
| 890 | |||||||||||||||||||||||||||||||||||
| 891 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 892 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 893 | return bytes; | ||||||||||||||||||||||||||||||||||
| 894 | |||||||||||||||||||||||||||||||||||
| 895 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 896 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 897 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 898 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||
| 899 | return 0; | ||||||||||||||||||||||||||||||||||
| 900 | |||||||||||||||||||||||||||||||||||
| 901 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||
| 902 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||
| 903 | return 0; | ||||||||||||||||||||||||||||||||||
| 904 | } | ||||||||||||||||||||||||||||||||||
| 905 | } | ||||||||||||||||||||||||||||||||||
| 906 | |||||||||||||||||||||||||||||||||||
| 907 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 908 | |||||||||||||||||||||||||||||||||||
| 909 | void complete_iocp_recv(state_type state, | ||||||||||||||||||||||||||||||||||
| 910 | const weak_cancel_token_type& cancel_token, bool all_empty, | ||||||||||||||||||||||||||||||||||
| 911 | boost::system::error_code& ec, size_t bytes_transferred) | ||||||||||||||||||||||||||||||||||
| 912 | { | ||||||||||||||||||||||||||||||||||
| 913 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||
| 914 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 915 | { | ||||||||||||||||||||||||||||||||||
| 916 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||
| 917 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||
| 918 | else | ||||||||||||||||||||||||||||||||||
| 919 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 920 | } | ||||||||||||||||||||||||||||||||||
| 921 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 922 | { | ||||||||||||||||||||||||||||||||||
| 923 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 924 | } | ||||||||||||||||||||||||||||||||||
| 925 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||
| 926 | { | ||||||||||||||||||||||||||||||||||
| 927 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 928 | } | ||||||||||||||||||||||||||||||||||
| 929 | |||||||||||||||||||||||||||||||||||
| 930 | // Check for connection closed. | ||||||||||||||||||||||||||||||||||
| 931 | else if (!ec && bytes_transferred == 0 | ||||||||||||||||||||||||||||||||||
| 932 | && (state & stream_oriented) != 0 | ||||||||||||||||||||||||||||||||||
| 933 | && !all_empty) | ||||||||||||||||||||||||||||||||||
| 934 | { | ||||||||||||||||||||||||||||||||||
| 935 | ec = boost::asio::error::eof; | ||||||||||||||||||||||||||||||||||
| 936 | } | ||||||||||||||||||||||||||||||||||
| 937 | } | ||||||||||||||||||||||||||||||||||
| 938 | |||||||||||||||||||||||||||||||||||
| 939 | #else // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 940 | |||||||||||||||||||||||||||||||||||
| 941 | bool non_blocking_recv(socket_type s, | ||||||||||||||||||||||||||||||||||
| 942 | buf* bufs, size_t count, int flags, bool is_stream, | ||||||||||||||||||||||||||||||||||
| 943 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||
| 944 | { | ||||||||||||||||||||||||||||||||||
| 945 | for (;;) | ||||||||||||||||||||||||||||||||||
| 946 | { | ||||||||||||||||||||||||||||||||||
| 947 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 948 | signed_size_type bytes = socket_ops::recv(s, bufs, count, flags, ec); | ||||||||||||||||||||||||||||||||||
| 949 | |||||||||||||||||||||||||||||||||||
| 950 | // Check for end of stream. | ||||||||||||||||||||||||||||||||||
| 951 | if (is_stream && bytes == 0) | ||||||||||||||||||||||||||||||||||
| 952 | { | ||||||||||||||||||||||||||||||||||
| 953 | ec = boost::asio::error::eof; | ||||||||||||||||||||||||||||||||||
| 954 | return true; | ||||||||||||||||||||||||||||||||||
| 955 | } | ||||||||||||||||||||||||||||||||||
| 956 | |||||||||||||||||||||||||||||||||||
| 957 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 958 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 959 | { | ||||||||||||||||||||||||||||||||||
| 960 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||
| 961 | return true; | ||||||||||||||||||||||||||||||||||
| 962 | } | ||||||||||||||||||||||||||||||||||
| 963 | |||||||||||||||||||||||||||||||||||
| 964 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||
| 965 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||
| 966 | continue; | ||||||||||||||||||||||||||||||||||
| 967 | |||||||||||||||||||||||||||||||||||
| 968 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||
| 969 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 970 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 971 | return false; | ||||||||||||||||||||||||||||||||||
| 972 | |||||||||||||||||||||||||||||||||||
| 973 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 974 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 975 | return true; | ||||||||||||||||||||||||||||||||||
| 976 | } | ||||||||||||||||||||||||||||||||||
| 977 | } | ||||||||||||||||||||||||||||||||||
| 978 | |||||||||||||||||||||||||||||||||||
| 979 | bool non_blocking_recv1(socket_type s, | ||||||||||||||||||||||||||||||||||
| 980 | void* data, size_t size, int flags, bool is_stream, | ||||||||||||||||||||||||||||||||||
| 981 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||
| 982 | { | ||||||||||||||||||||||||||||||||||
| 983 | for (;;) | ||||||||||||||||||||||||||||||||||
| 984 | { | ||||||||||||||||||||||||||||||||||
| 985 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 986 | signed_size_type bytes = socket_ops::recv1(s, data, size, flags, ec); | ||||||||||||||||||||||||||||||||||
| 987 | |||||||||||||||||||||||||||||||||||
| 988 | // Check for end of stream. | ||||||||||||||||||||||||||||||||||
| 989 | if (is_stream && bytes == 0) | ||||||||||||||||||||||||||||||||||
| 990 | { | ||||||||||||||||||||||||||||||||||
| 991 | ec = boost::asio::error::eof; | ||||||||||||||||||||||||||||||||||
| 992 | return true; | ||||||||||||||||||||||||||||||||||
| 993 | } | ||||||||||||||||||||||||||||||||||
| 994 | |||||||||||||||||||||||||||||||||||
| 995 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 996 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 997 | { | ||||||||||||||||||||||||||||||||||
| 998 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||
| 999 | return true; | ||||||||||||||||||||||||||||||||||
| 1000 | } | ||||||||||||||||||||||||||||||||||
| 1001 | |||||||||||||||||||||||||||||||||||
| 1002 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||
| 1003 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||
| 1004 | continue; | ||||||||||||||||||||||||||||||||||
| 1005 | |||||||||||||||||||||||||||||||||||
| 1006 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||
| 1007 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1008 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 1009 | return false; | ||||||||||||||||||||||||||||||||||
| 1010 | |||||||||||||||||||||||||||||||||||
| 1011 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1012 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1013 | return true; | ||||||||||||||||||||||||||||||||||
| 1014 | } | ||||||||||||||||||||||||||||||||||
| 1015 | } | ||||||||||||||||||||||||||||||||||
| 1016 | |||||||||||||||||||||||||||||||||||
| 1017 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1018 | |||||||||||||||||||||||||||||||||||
| 1019 | signed_size_type recvfrom(socket_type s, buf* bufs, size_t count, | ||||||||||||||||||||||||||||||||||
| 1020 | int flags, void* addr, std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1021 | { | ||||||||||||||||||||||||||||||||||
| 1022 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1023 | // Receive some data. | ||||||||||||||||||||||||||||||||||
| 1024 | DWORD recv_buf_count = static_cast<DWORD>(count); | ||||||||||||||||||||||||||||||||||
| 1025 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1026 | DWORD recv_flags = flags; | ||||||||||||||||||||||||||||||||||
| 1027 | int tmp_addrlen = (int)*addrlen; | ||||||||||||||||||||||||||||||||||
| 1028 | int result = ::WSARecvFrom(s, bufs, recv_buf_count, &bytes_transferred, | ||||||||||||||||||||||||||||||||||
| 1029 | &recv_flags, static_cast<socket_addr_type*>(addr), &tmp_addrlen, 0, 0); | ||||||||||||||||||||||||||||||||||
| 1030 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 1031 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||
| 1032 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 1033 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 1034 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 1035 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 1036 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||
| 1037 | result = 0; | ||||||||||||||||||||||||||||||||||
| 1038 | if (result != 0) | ||||||||||||||||||||||||||||||||||
| 1039 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1040 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1041 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||
| 1042 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1043 | msghdr msg = msghdr(); | ||||||||||||||||||||||||||||||||||
| 1044 | init_msghdr_msg_name(msg.msg_name, addr); | ||||||||||||||||||||||||||||||||||
| 1045 | msg.msg_namelen = static_cast<int>(*addrlen); | ||||||||||||||||||||||||||||||||||
| 1046 | msg.msg_iov = bufs; | ||||||||||||||||||||||||||||||||||
| 1047 | msg.msg_iovlen = static_cast<int>(count); | ||||||||||||||||||||||||||||||||||
| 1048 | signed_size_type result = ::recvmsg(s, &msg, flags); | ||||||||||||||||||||||||||||||||||
| 1049 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 1050 | *addrlen = msg.msg_namelen; | ||||||||||||||||||||||||||||||||||
| 1051 | return result; | ||||||||||||||||||||||||||||||||||
| 1052 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1053 | } | ||||||||||||||||||||||||||||||||||
| 1054 | |||||||||||||||||||||||||||||||||||
| 1055 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||
| 1056 | inline signed_size_type call_recvfrom(SockLenType msghdr::*, socket_type s, | ||||||||||||||||||||||||||||||||||
| 1057 | void* data, size_t size, int flags, void* addr, std::size_t* addrlen) | ||||||||||||||||||||||||||||||||||
| 1058 | { | ||||||||||||||||||||||||||||||||||
| 1059 | SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; | ||||||||||||||||||||||||||||||||||
| 1060 | signed_size_type result = ::recvfrom(s, static_cast<char*>(data), size, | ||||||||||||||||||||||||||||||||||
| 1061 | flags, static_cast<socket_addr_type*>(addr), addrlen ? &tmp_addrlen : 0); | ||||||||||||||||||||||||||||||||||
| 1062 | if (addrlen) | ||||||||||||||||||||||||||||||||||
| 1063 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||
| 1064 | return result; | ||||||||||||||||||||||||||||||||||
| 1065 | } | ||||||||||||||||||||||||||||||||||
| 1066 | |||||||||||||||||||||||||||||||||||
| 1067 | signed_size_type recvfrom1(socket_type s, void* data, size_t size, | ||||||||||||||||||||||||||||||||||
| 1068 | int flags, void* addr, std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1069 | { | ||||||||||||||||||||||||||||||||||
| 1070 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1071 | // Receive some data. | ||||||||||||||||||||||||||||||||||
| 1072 | WSABUF buf; | ||||||||||||||||||||||||||||||||||
| 1073 | buf.buf = static_cast<char*>(data); | ||||||||||||||||||||||||||||||||||
| 1074 | buf.len = static_cast<ULONG>(size); | ||||||||||||||||||||||||||||||||||
| 1075 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1076 | DWORD recv_flags = flags; | ||||||||||||||||||||||||||||||||||
| 1077 | int tmp_addrlen = (int)*addrlen; | ||||||||||||||||||||||||||||||||||
| 1078 | int result = ::WSARecvFrom(s, &buf, 1, &bytes_transferred, &recv_flags, | ||||||||||||||||||||||||||||||||||
| 1079 | static_cast<socket_addr_type*>(addr), &tmp_addrlen, 0, 0); | ||||||||||||||||||||||||||||||||||
| 1080 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 1081 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||
| 1082 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 1083 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 1084 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 1085 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 1086 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||
| 1087 | result = 0; | ||||||||||||||||||||||||||||||||||
| 1088 | if (result != 0) | ||||||||||||||||||||||||||||||||||
| 1089 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1090 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1091 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||
| 1092 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1093 | signed_size_type result = call_recvfrom(&msghdr::msg_namelen, | ||||||||||||||||||||||||||||||||||
| 1094 | s, data, size, flags, addr, addrlen); | ||||||||||||||||||||||||||||||||||
| 1095 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 1096 | return result; | ||||||||||||||||||||||||||||||||||
| 1097 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1098 | } | ||||||||||||||||||||||||||||||||||
| 1099 | |||||||||||||||||||||||||||||||||||
| 1100 | size_t sync_recvfrom(socket_type s, state_type state, buf* bufs, size_t count, | ||||||||||||||||||||||||||||||||||
| 1101 | int flags, void* addr, std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1102 | { | ||||||||||||||||||||||||||||||||||
| 1103 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 1104 | { | ||||||||||||||||||||||||||||||||||
| 1105 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 1106 | return 0; | ||||||||||||||||||||||||||||||||||
| 1107 | } | ||||||||||||||||||||||||||||||||||
| 1108 | |||||||||||||||||||||||||||||||||||
| 1109 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 1110 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1111 | { | ||||||||||||||||||||||||||||||||||
| 1112 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||
| 1113 | signed_size_type bytes = socket_ops::recvfrom( | ||||||||||||||||||||||||||||||||||
| 1114 | s, bufs, count, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||
| 1115 | |||||||||||||||||||||||||||||||||||
| 1116 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1117 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1118 | return bytes; | ||||||||||||||||||||||||||||||||||
| 1119 | |||||||||||||||||||||||||||||||||||
| 1120 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1121 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 1122 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1123 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||
| 1124 | return 0; | ||||||||||||||||||||||||||||||||||
| 1125 | |||||||||||||||||||||||||||||||||||
| 1126 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||
| 1127 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||
| 1128 | return 0; | ||||||||||||||||||||||||||||||||||
| 1129 | } | ||||||||||||||||||||||||||||||||||
| 1130 | } | ||||||||||||||||||||||||||||||||||
| 1131 | |||||||||||||||||||||||||||||||||||
| 1132 | size_t sync_recvfrom1(socket_type s, state_type state, void* data, size_t size, | ||||||||||||||||||||||||||||||||||
| 1133 | int flags, void* addr, std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1134 | { | ||||||||||||||||||||||||||||||||||
| 1135 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 1136 | { | ||||||||||||||||||||||||||||||||||
| 1137 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 1138 | return 0; | ||||||||||||||||||||||||||||||||||
| 1139 | } | ||||||||||||||||||||||||||||||||||
| 1140 | |||||||||||||||||||||||||||||||||||
| 1141 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 1142 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1143 | { | ||||||||||||||||||||||||||||||||||
| 1144 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||
| 1145 | signed_size_type bytes = socket_ops::recvfrom1( | ||||||||||||||||||||||||||||||||||
| 1146 | s, data, size, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||
| 1147 | |||||||||||||||||||||||||||||||||||
| 1148 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1149 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1150 | return bytes; | ||||||||||||||||||||||||||||||||||
| 1151 | |||||||||||||||||||||||||||||||||||
| 1152 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1153 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 1154 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1155 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||
| 1156 | return 0; | ||||||||||||||||||||||||||||||||||
| 1157 | |||||||||||||||||||||||||||||||||||
| 1158 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||
| 1159 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||
| 1160 | return 0; | ||||||||||||||||||||||||||||||||||
| 1161 | } | ||||||||||||||||||||||||||||||||||
| 1162 | } | ||||||||||||||||||||||||||||||||||
| 1163 | |||||||||||||||||||||||||||||||||||
| 1164 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1165 | |||||||||||||||||||||||||||||||||||
| 1166 | void complete_iocp_recvfrom( | ||||||||||||||||||||||||||||||||||
| 1167 | const weak_cancel_token_type& cancel_token, | ||||||||||||||||||||||||||||||||||
| 1168 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1169 | { | ||||||||||||||||||||||||||||||||||
| 1170 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||
| 1171 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 1172 | { | ||||||||||||||||||||||||||||||||||
| 1173 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||
| 1174 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||
| 1175 | else | ||||||||||||||||||||||||||||||||||
| 1176 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 1177 | } | ||||||||||||||||||||||||||||||||||
| 1178 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 1179 | { | ||||||||||||||||||||||||||||||||||
| 1180 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 1181 | } | ||||||||||||||||||||||||||||||||||
| 1182 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||
| 1183 | { | ||||||||||||||||||||||||||||||||||
| 1184 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1185 | } | ||||||||||||||||||||||||||||||||||
| 1186 | } | ||||||||||||||||||||||||||||||||||
| 1187 | |||||||||||||||||||||||||||||||||||
| 1188 | #else // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1189 | |||||||||||||||||||||||||||||||||||
| 1190 | bool non_blocking_recvfrom(socket_type s, buf* bufs, | ||||||||||||||||||||||||||||||||||
| 1191 | size_t count, int flags, void* addr, std::size_t* addrlen, | ||||||||||||||||||||||||||||||||||
| 1192 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||
| 1193 | { | ||||||||||||||||||||||||||||||||||
| 1194 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1195 | { | ||||||||||||||||||||||||||||||||||
| 1196 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 1197 | signed_size_type bytes = socket_ops::recvfrom( | ||||||||||||||||||||||||||||||||||
| 1198 | s, bufs, count, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||
| 1199 | |||||||||||||||||||||||||||||||||||
| 1200 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1201 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1202 | { | ||||||||||||||||||||||||||||||||||
| 1203 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||
| 1204 | return true; | ||||||||||||||||||||||||||||||||||
| 1205 | } | ||||||||||||||||||||||||||||||||||
| 1206 | |||||||||||||||||||||||||||||||||||
| 1207 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||
| 1208 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||
| 1209 | continue; | ||||||||||||||||||||||||||||||||||
| 1210 | |||||||||||||||||||||||||||||||||||
| 1211 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||
| 1212 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1213 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 1214 | return false; | ||||||||||||||||||||||||||||||||||
| 1215 | |||||||||||||||||||||||||||||||||||
| 1216 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1217 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1218 | return true; | ||||||||||||||||||||||||||||||||||
| 1219 | } | ||||||||||||||||||||||||||||||||||
| 1220 | } | ||||||||||||||||||||||||||||||||||
| 1221 | |||||||||||||||||||||||||||||||||||
| 1222 | bool non_blocking_recvfrom1(socket_type s, void* data, | ||||||||||||||||||||||||||||||||||
| 1223 | size_t size, int flags, void* addr, std::size_t* addrlen, | ||||||||||||||||||||||||||||||||||
| 1224 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||
| 1225 | { | ||||||||||||||||||||||||||||||||||
| 1226 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1227 | { | ||||||||||||||||||||||||||||||||||
| 1228 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 1229 | signed_size_type bytes = socket_ops::recvfrom1( | ||||||||||||||||||||||||||||||||||
| 1230 | s, data, size, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||
| 1231 | |||||||||||||||||||||||||||||||||||
| 1232 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1233 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1234 | { | ||||||||||||||||||||||||||||||||||
| 1235 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||
| 1236 | return true; | ||||||||||||||||||||||||||||||||||
| 1237 | } | ||||||||||||||||||||||||||||||||||
| 1238 | |||||||||||||||||||||||||||||||||||
| 1239 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||
| 1240 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||
| 1241 | continue; | ||||||||||||||||||||||||||||||||||
| 1242 | |||||||||||||||||||||||||||||||||||
| 1243 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||
| 1244 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1245 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 1246 | return false; | ||||||||||||||||||||||||||||||||||
| 1247 | |||||||||||||||||||||||||||||||||||
| 1248 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1249 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1250 | return true; | ||||||||||||||||||||||||||||||||||
| 1251 | } | ||||||||||||||||||||||||||||||||||
| 1252 | } | ||||||||||||||||||||||||||||||||||
| 1253 | |||||||||||||||||||||||||||||||||||
| 1254 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1255 | |||||||||||||||||||||||||||||||||||
| 1256 | signed_size_type recvmsg(socket_type s, buf* bufs, size_t count, | ||||||||||||||||||||||||||||||||||
| 1257 | int in_flags, int& out_flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1258 | { | ||||||||||||||||||||||||||||||||||
| 1259 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1260 | out_flags = 0; | ||||||||||||||||||||||||||||||||||
| 1261 | return socket_ops::recv(s, bufs, count, in_flags, ec); | ||||||||||||||||||||||||||||||||||
| 1262 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1263 | msghdr msg = msghdr(); | ||||||||||||||||||||||||||||||||||
| 1264 | msg.msg_iov = bufs; | ||||||||||||||||||||||||||||||||||
| 1265 | msg.msg_iovlen = static_cast<int>(count); | ||||||||||||||||||||||||||||||||||
| 1266 | signed_size_type result = ::recvmsg(s, &msg, in_flags); | ||||||||||||||||||||||||||||||||||
| 1267 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 1268 | if (result >= 0) | ||||||||||||||||||||||||||||||||||
| 1269 | out_flags = msg.msg_flags; | ||||||||||||||||||||||||||||||||||
| 1270 | else | ||||||||||||||||||||||||||||||||||
| 1271 | out_flags = 0; | ||||||||||||||||||||||||||||||||||
| 1272 | return result; | ||||||||||||||||||||||||||||||||||
| 1273 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1274 | } | ||||||||||||||||||||||||||||||||||
| 1275 | |||||||||||||||||||||||||||||||||||
| 1276 | size_t sync_recvmsg(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||
| 1277 | buf* bufs, size_t count, int in_flags, int& out_flags, | ||||||||||||||||||||||||||||||||||
| 1278 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1279 | { | ||||||||||||||||||||||||||||||||||
| 1280 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 1281 | { | ||||||||||||||||||||||||||||||||||
| 1282 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 1283 | return 0; | ||||||||||||||||||||||||||||||||||
| 1284 | } | ||||||||||||||||||||||||||||||||||
| 1285 | |||||||||||||||||||||||||||||||||||
| 1286 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 1287 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1288 | { | ||||||||||||||||||||||||||||||||||
| 1289 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||
| 1290 | signed_size_type bytes = socket_ops::recvmsg( | ||||||||||||||||||||||||||||||||||
| 1291 | s, bufs, count, in_flags, out_flags, ec); | ||||||||||||||||||||||||||||||||||
| 1292 | |||||||||||||||||||||||||||||||||||
| 1293 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1294 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1295 | return bytes; | ||||||||||||||||||||||||||||||||||
| 1296 | |||||||||||||||||||||||||||||||||||
| 1297 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1298 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 1299 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1300 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||
| 1301 | return 0; | ||||||||||||||||||||||||||||||||||
| 1302 | |||||||||||||||||||||||||||||||||||
| 1303 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||
| 1304 | if (socket_ops::poll_read(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||
| 1305 | return 0; | ||||||||||||||||||||||||||||||||||
| 1306 | } | ||||||||||||||||||||||||||||||||||
| 1307 | } | ||||||||||||||||||||||||||||||||||
| 1308 | |||||||||||||||||||||||||||||||||||
| 1309 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1310 | |||||||||||||||||||||||||||||||||||
| 1311 | void complete_iocp_recvmsg( | ||||||||||||||||||||||||||||||||||
| 1312 | const weak_cancel_token_type& cancel_token, | ||||||||||||||||||||||||||||||||||
| 1313 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1314 | { | ||||||||||||||||||||||||||||||||||
| 1315 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||
| 1316 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 1317 | { | ||||||||||||||||||||||||||||||||||
| 1318 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||
| 1319 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||
| 1320 | else | ||||||||||||||||||||||||||||||||||
| 1321 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 1322 | } | ||||||||||||||||||||||||||||||||||
| 1323 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 1324 | { | ||||||||||||||||||||||||||||||||||
| 1325 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 1326 | } | ||||||||||||||||||||||||||||||||||
| 1327 | else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) | ||||||||||||||||||||||||||||||||||
| 1328 | { | ||||||||||||||||||||||||||||||||||
| 1329 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1330 | } | ||||||||||||||||||||||||||||||||||
| 1331 | } | ||||||||||||||||||||||||||||||||||
| 1332 | |||||||||||||||||||||||||||||||||||
| 1333 | #else // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1334 | |||||||||||||||||||||||||||||||||||
| 1335 | bool non_blocking_recvmsg(socket_type s, | ||||||||||||||||||||||||||||||||||
| 1336 | buf* bufs, size_t count, int in_flags, int& out_flags, | ||||||||||||||||||||||||||||||||||
| 1337 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||
| 1338 | { | ||||||||||||||||||||||||||||||||||
| 1339 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1340 | { | ||||||||||||||||||||||||||||||||||
| 1341 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 1342 | signed_size_type bytes = socket_ops::recvmsg( | ||||||||||||||||||||||||||||||||||
| 1343 | s, bufs, count, in_flags, out_flags, ec); | ||||||||||||||||||||||||||||||||||
| 1344 | |||||||||||||||||||||||||||||||||||
| 1345 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1346 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1347 | { | ||||||||||||||||||||||||||||||||||
| 1348 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||
| 1349 | return true; | ||||||||||||||||||||||||||||||||||
| 1350 | } | ||||||||||||||||||||||||||||||||||
| 1351 | |||||||||||||||||||||||||||||||||||
| 1352 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||
| 1353 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||
| 1354 | continue; | ||||||||||||||||||||||||||||||||||
| 1355 | |||||||||||||||||||||||||||||||||||
| 1356 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||
| 1357 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1358 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 1359 | return false; | ||||||||||||||||||||||||||||||||||
| 1360 | |||||||||||||||||||||||||||||||||||
| 1361 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1362 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1363 | return true; | ||||||||||||||||||||||||||||||||||
| 1364 | } | ||||||||||||||||||||||||||||||||||
| 1365 | } | ||||||||||||||||||||||||||||||||||
| 1366 | |||||||||||||||||||||||||||||||||||
| 1367 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1368 | |||||||||||||||||||||||||||||||||||
| 1369 | signed_size_type send(socket_type s, const buf* bufs, size_t count, | ||||||||||||||||||||||||||||||||||
| 1370 | int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1371 | { | ||||||||||||||||||||||||||||||||||
| 1372 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1373 | // Send the data. | ||||||||||||||||||||||||||||||||||
| 1374 | DWORD send_buf_count = static_cast<DWORD>(count); | ||||||||||||||||||||||||||||||||||
| 1375 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1376 | DWORD send_flags = flags; | ||||||||||||||||||||||||||||||||||
| 1377 | int result = ::WSASend(s, const_cast<buf*>(bufs), | ||||||||||||||||||||||||||||||||||
| 1378 | send_buf_count, &bytes_transferred, send_flags, 0, 0); | ||||||||||||||||||||||||||||||||||
| 1379 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 1380 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 1381 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 1382 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 1383 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 1384 | if (result != 0) | ||||||||||||||||||||||||||||||||||
| 1385 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1386 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1387 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||
| 1388 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1389 | msghdr msg = msghdr(); | ||||||||||||||||||||||||||||||||||
| 1390 | msg.msg_iov = const_cast<buf*>(bufs); | ||||||||||||||||||||||||||||||||||
| 1391 | msg.msg_iovlen = static_cast<int>(count); | ||||||||||||||||||||||||||||||||||
| 1392 | #if defined(BOOST_ASIO_HAS_MSG_NOSIGNAL1) | ||||||||||||||||||||||||||||||||||
| 1393 | flags |= MSG_NOSIGNALMSG_NOSIGNAL; | ||||||||||||||||||||||||||||||||||
| 1394 | #endif // defined(BOOST_ASIO_HAS_MSG_NOSIGNAL) | ||||||||||||||||||||||||||||||||||
| 1395 | signed_size_type result = ::sendmsg(s, &msg, flags); | ||||||||||||||||||||||||||||||||||
| 1396 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 1397 | return result; | ||||||||||||||||||||||||||||||||||
| 1398 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1399 | } | ||||||||||||||||||||||||||||||||||
| 1400 | |||||||||||||||||||||||||||||||||||
| 1401 | signed_size_type send1(socket_type s, const void* data, size_t size, | ||||||||||||||||||||||||||||||||||
| 1402 | int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1403 | { | ||||||||||||||||||||||||||||||||||
| 1404 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1405 | // Send the data. | ||||||||||||||||||||||||||||||||||
| 1406 | WSABUF buf; | ||||||||||||||||||||||||||||||||||
| 1407 | buf.buf = const_cast<char*>(static_cast<const char*>(data)); | ||||||||||||||||||||||||||||||||||
| 1408 | buf.len = static_cast<ULONG>(size); | ||||||||||||||||||||||||||||||||||
| 1409 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1410 | DWORD send_flags = flags; | ||||||||||||||||||||||||||||||||||
| 1411 | int result = ::WSASend(s, &buf, 1, | ||||||||||||||||||||||||||||||||||
| 1412 | &bytes_transferred, send_flags, 0, 0); | ||||||||||||||||||||||||||||||||||
| 1413 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 1414 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 1415 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 1416 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 1417 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 1418 | if (result != 0) | ||||||||||||||||||||||||||||||||||
| 1419 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1420 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1421 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||
| 1422 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1423 | #if defined(BOOST_ASIO_HAS_MSG_NOSIGNAL1) | ||||||||||||||||||||||||||||||||||
| 1424 | flags |= MSG_NOSIGNALMSG_NOSIGNAL; | ||||||||||||||||||||||||||||||||||
| 1425 | #endif // defined(BOOST_ASIO_HAS_MSG_NOSIGNAL) | ||||||||||||||||||||||||||||||||||
| 1426 | signed_size_type result = ::send(s, | ||||||||||||||||||||||||||||||||||
| 1427 | static_cast<const char*>(data), size, flags); | ||||||||||||||||||||||||||||||||||
| 1428 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 1429 | return result; | ||||||||||||||||||||||||||||||||||
| 1430 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1431 | } | ||||||||||||||||||||||||||||||||||
| 1432 | |||||||||||||||||||||||||||||||||||
| 1433 | size_t sync_send(socket_type s, state_type state, const buf* bufs, | ||||||||||||||||||||||||||||||||||
| 1434 | size_t count, int flags, bool all_empty, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1435 | { | ||||||||||||||||||||||||||||||||||
| 1436 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 1437 | { | ||||||||||||||||||||||||||||||||||
| 1438 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 1439 | return 0; | ||||||||||||||||||||||||||||||||||
| 1440 | } | ||||||||||||||||||||||||||||||||||
| 1441 | |||||||||||||||||||||||||||||||||||
| 1442 | // A request to write 0 bytes to a stream is a no-op. | ||||||||||||||||||||||||||||||||||
| 1443 | if (all_empty && (state & stream_oriented)) | ||||||||||||||||||||||||||||||||||
| 1444 | { | ||||||||||||||||||||||||||||||||||
| 1445 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1446 | return 0; | ||||||||||||||||||||||||||||||||||
| 1447 | } | ||||||||||||||||||||||||||||||||||
| 1448 | |||||||||||||||||||||||||||||||||||
| 1449 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 1450 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1451 | { | ||||||||||||||||||||||||||||||||||
| 1452 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||
| 1453 | signed_size_type bytes = socket_ops::send(s, bufs, count, flags, ec); | ||||||||||||||||||||||||||||||||||
| 1454 | |||||||||||||||||||||||||||||||||||
| 1455 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1456 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1457 | return bytes; | ||||||||||||||||||||||||||||||||||
| 1458 | |||||||||||||||||||||||||||||||||||
| 1459 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1460 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 1461 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1462 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||
| 1463 | return 0; | ||||||||||||||||||||||||||||||||||
| 1464 | |||||||||||||||||||||||||||||||||||
| 1465 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||
| 1466 | if (socket_ops::poll_write(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||
| 1467 | return 0; | ||||||||||||||||||||||||||||||||||
| 1468 | } | ||||||||||||||||||||||||||||||||||
| 1469 | } | ||||||||||||||||||||||||||||||||||
| 1470 | |||||||||||||||||||||||||||||||||||
| 1471 | size_t sync_send1(socket_type s, state_type state, const void* data, | ||||||||||||||||||||||||||||||||||
| 1472 | size_t size, int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1473 | { | ||||||||||||||||||||||||||||||||||
| 1474 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 1475 | { | ||||||||||||||||||||||||||||||||||
| 1476 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 1477 | return 0; | ||||||||||||||||||||||||||||||||||
| 1478 | } | ||||||||||||||||||||||||||||||||||
| 1479 | |||||||||||||||||||||||||||||||||||
| 1480 | // A request to write 0 bytes to a stream is a no-op. | ||||||||||||||||||||||||||||||||||
| 1481 | if (size == 0 && (state & stream_oriented)) | ||||||||||||||||||||||||||||||||||
| 1482 | { | ||||||||||||||||||||||||||||||||||
| 1483 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1484 | return 0; | ||||||||||||||||||||||||||||||||||
| 1485 | } | ||||||||||||||||||||||||||||||||||
| 1486 | |||||||||||||||||||||||||||||||||||
| 1487 | // Read some data. | ||||||||||||||||||||||||||||||||||
| 1488 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1489 | { | ||||||||||||||||||||||||||||||||||
| 1490 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||
| 1491 | signed_size_type bytes = socket_ops::send1(s, data, size, flags, ec); | ||||||||||||||||||||||||||||||||||
| 1492 | |||||||||||||||||||||||||||||||||||
| 1493 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1494 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1495 | return bytes; | ||||||||||||||||||||||||||||||||||
| 1496 | |||||||||||||||||||||||||||||||||||
| 1497 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1498 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 1499 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1500 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||
| 1501 | return 0; | ||||||||||||||||||||||||||||||||||
| 1502 | |||||||||||||||||||||||||||||||||||
| 1503 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||
| 1504 | if (socket_ops::poll_write(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||
| 1505 | return 0; | ||||||||||||||||||||||||||||||||||
| 1506 | } | ||||||||||||||||||||||||||||||||||
| 1507 | } | ||||||||||||||||||||||||||||||||||
| 1508 | |||||||||||||||||||||||||||||||||||
| 1509 | #if defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1510 | |||||||||||||||||||||||||||||||||||
| 1511 | void complete_iocp_send( | ||||||||||||||||||||||||||||||||||
| 1512 | const weak_cancel_token_type& cancel_token, | ||||||||||||||||||||||||||||||||||
| 1513 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1514 | { | ||||||||||||||||||||||||||||||||||
| 1515 | // Map non-portable errors to their portable counterparts. | ||||||||||||||||||||||||||||||||||
| 1516 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 1517 | { | ||||||||||||||||||||||||||||||||||
| 1518 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||
| 1519 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||
| 1520 | else | ||||||||||||||||||||||||||||||||||
| 1521 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 1522 | } | ||||||||||||||||||||||||||||||||||
| 1523 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 1524 | { | ||||||||||||||||||||||||||||||||||
| 1525 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 1526 | } | ||||||||||||||||||||||||||||||||||
| 1527 | } | ||||||||||||||||||||||||||||||||||
| 1528 | |||||||||||||||||||||||||||||||||||
| 1529 | #else // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1530 | |||||||||||||||||||||||||||||||||||
| 1531 | bool non_blocking_send(socket_type s, | ||||||||||||||||||||||||||||||||||
| 1532 | const buf* bufs, size_t count, int flags, | ||||||||||||||||||||||||||||||||||
| 1533 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||
| 1534 | { | ||||||||||||||||||||||||||||||||||
| 1535 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1536 | { | ||||||||||||||||||||||||||||||||||
| 1537 | // Write some data. | ||||||||||||||||||||||||||||||||||
| 1538 | signed_size_type bytes = socket_ops::send(s, bufs, count, flags, ec); | ||||||||||||||||||||||||||||||||||
| 1539 | |||||||||||||||||||||||||||||||||||
| 1540 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1541 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1542 | { | ||||||||||||||||||||||||||||||||||
| 1543 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||
| 1544 | return true; | ||||||||||||||||||||||||||||||||||
| 1545 | } | ||||||||||||||||||||||||||||||||||
| 1546 | |||||||||||||||||||||||||||||||||||
| 1547 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||
| 1548 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||
| 1549 | continue; | ||||||||||||||||||||||||||||||||||
| 1550 | |||||||||||||||||||||||||||||||||||
| 1551 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||
| 1552 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1553 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 1554 | return false; | ||||||||||||||||||||||||||||||||||
| 1555 | |||||||||||||||||||||||||||||||||||
| 1556 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1557 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1558 | return true; | ||||||||||||||||||||||||||||||||||
| 1559 | } | ||||||||||||||||||||||||||||||||||
| 1560 | } | ||||||||||||||||||||||||||||||||||
| 1561 | |||||||||||||||||||||||||||||||||||
| 1562 | bool non_blocking_send1(socket_type s, | ||||||||||||||||||||||||||||||||||
| 1563 | const void* data, size_t size, int flags, | ||||||||||||||||||||||||||||||||||
| 1564 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||
| 1565 | { | ||||||||||||||||||||||||||||||||||
| 1566 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1567 | { | ||||||||||||||||||||||||||||||||||
| 1568 | // Write some data. | ||||||||||||||||||||||||||||||||||
| 1569 | signed_size_type bytes = socket_ops::send1(s, data, size, flags, ec); | ||||||||||||||||||||||||||||||||||
| 1570 | |||||||||||||||||||||||||||||||||||
| 1571 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1572 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1573 | { | ||||||||||||||||||||||||||||||||||
| 1574 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||
| 1575 | return true; | ||||||||||||||||||||||||||||||||||
| 1576 | } | ||||||||||||||||||||||||||||||||||
| 1577 | |||||||||||||||||||||||||||||||||||
| 1578 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||
| 1579 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||
| 1580 | continue; | ||||||||||||||||||||||||||||||||||
| 1581 | |||||||||||||||||||||||||||||||||||
| 1582 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||
| 1583 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1584 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 1585 | return false; | ||||||||||||||||||||||||||||||||||
| 1586 | |||||||||||||||||||||||||||||||||||
| 1587 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1588 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1589 | return true; | ||||||||||||||||||||||||||||||||||
| 1590 | } | ||||||||||||||||||||||||||||||||||
| 1591 | } | ||||||||||||||||||||||||||||||||||
| 1592 | |||||||||||||||||||||||||||||||||||
| 1593 | #endif // defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1594 | |||||||||||||||||||||||||||||||||||
| 1595 | signed_size_type sendto(socket_type s, const buf* bufs, | ||||||||||||||||||||||||||||||||||
| 1596 | size_t count, int flags, const void* addr, | ||||||||||||||||||||||||||||||||||
| 1597 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1598 | { | ||||||||||||||||||||||||||||||||||
| 1599 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1600 | // Send the data. | ||||||||||||||||||||||||||||||||||
| 1601 | DWORD send_buf_count = static_cast<DWORD>(count); | ||||||||||||||||||||||||||||||||||
| 1602 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1603 | int result = ::WSASendTo(s, const_cast<buf*>(bufs), | ||||||||||||||||||||||||||||||||||
| 1604 | send_buf_count, &bytes_transferred, flags, | ||||||||||||||||||||||||||||||||||
| 1605 | static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||
| 1606 | static_cast<int>(addrlen), 0, 0); | ||||||||||||||||||||||||||||||||||
| 1607 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 1608 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 1609 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 1610 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 1611 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 1612 | if (result != 0) | ||||||||||||||||||||||||||||||||||
| 1613 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1614 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1615 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||
| 1616 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1617 | msghdr msg = msghdr(); | ||||||||||||||||||||||||||||||||||
| 1618 | init_msghdr_msg_name(msg.msg_name, addr); | ||||||||||||||||||||||||||||||||||
| 1619 | msg.msg_namelen = static_cast<int>(addrlen); | ||||||||||||||||||||||||||||||||||
| 1620 | msg.msg_iov = const_cast<buf*>(bufs); | ||||||||||||||||||||||||||||||||||
| 1621 | msg.msg_iovlen = static_cast<int>(count); | ||||||||||||||||||||||||||||||||||
| 1622 | #if defined(BOOST_ASIO_HAS_MSG_NOSIGNAL1) | ||||||||||||||||||||||||||||||||||
| 1623 | flags |= MSG_NOSIGNALMSG_NOSIGNAL; | ||||||||||||||||||||||||||||||||||
| 1624 | #endif // defined(BOOST_ASIO_HAS_MSG_NOSIGNAL) | ||||||||||||||||||||||||||||||||||
| 1625 | signed_size_type result = ::sendmsg(s, &msg, flags); | ||||||||||||||||||||||||||||||||||
| 1626 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 1627 | return result; | ||||||||||||||||||||||||||||||||||
| 1628 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1629 | } | ||||||||||||||||||||||||||||||||||
| 1630 | |||||||||||||||||||||||||||||||||||
| 1631 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||
| 1632 | inline signed_size_type call_sendto(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||
| 1633 | socket_type s, const void* data, size_t size, int flags, | ||||||||||||||||||||||||||||||||||
| 1634 | const void* addr, std::size_t addrlen) | ||||||||||||||||||||||||||||||||||
| 1635 | { | ||||||||||||||||||||||||||||||||||
| 1636 | return ::sendto(s, static_cast<char*>(const_cast<void*>(data)), size, flags, | ||||||||||||||||||||||||||||||||||
| 1637 | static_cast<const socket_addr_type*>(addr), (SockLenType)addrlen); | ||||||||||||||||||||||||||||||||||
| 1638 | } | ||||||||||||||||||||||||||||||||||
| 1639 | |||||||||||||||||||||||||||||||||||
| 1640 | signed_size_type sendto1(socket_type s, const void* data, | ||||||||||||||||||||||||||||||||||
| 1641 | size_t size, int flags, const void* addr, | ||||||||||||||||||||||||||||||||||
| 1642 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1643 | { | ||||||||||||||||||||||||||||||||||
| 1644 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1645 | // Send the data. | ||||||||||||||||||||||||||||||||||
| 1646 | WSABUF buf; | ||||||||||||||||||||||||||||||||||
| 1647 | buf.buf = const_cast<char*>(static_cast<const char*>(data)); | ||||||||||||||||||||||||||||||||||
| 1648 | buf.len = static_cast<ULONG>(size); | ||||||||||||||||||||||||||||||||||
| 1649 | DWORD bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1650 | int result = ::WSASendTo(s, &buf, 1, &bytes_transferred, flags, | ||||||||||||||||||||||||||||||||||
| 1651 | static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||
| 1652 | static_cast<int>(addrlen), 0, 0); | ||||||||||||||||||||||||||||||||||
| 1653 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 1654 | if (ec.value() == ERROR_NETNAME_DELETED) | ||||||||||||||||||||||||||||||||||
| 1655 | ec = boost::asio::error::connection_reset; | ||||||||||||||||||||||||||||||||||
| 1656 | else if (ec.value() == ERROR_PORT_UNREACHABLE) | ||||||||||||||||||||||||||||||||||
| 1657 | ec = boost::asio::error::connection_refused; | ||||||||||||||||||||||||||||||||||
| 1658 | if (result != 0) | ||||||||||||||||||||||||||||||||||
| 1659 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1660 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1661 | return bytes_transferred; | ||||||||||||||||||||||||||||||||||
| 1662 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1663 | #if defined(BOOST_ASIO_HAS_MSG_NOSIGNAL1) | ||||||||||||||||||||||||||||||||||
| 1664 | flags |= MSG_NOSIGNALMSG_NOSIGNAL; | ||||||||||||||||||||||||||||||||||
| 1665 | #endif // defined(BOOST_ASIO_HAS_MSG_NOSIGNAL) | ||||||||||||||||||||||||||||||||||
| 1666 | signed_size_type result = call_sendto(&msghdr::msg_namelen, | ||||||||||||||||||||||||||||||||||
| 1667 | s, data, size, flags, addr, addrlen); | ||||||||||||||||||||||||||||||||||
| 1668 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 1669 | return result; | ||||||||||||||||||||||||||||||||||
| 1670 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1671 | } | ||||||||||||||||||||||||||||||||||
| 1672 | |||||||||||||||||||||||||||||||||||
| 1673 | size_t sync_sendto(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||
| 1674 | const buf* bufs, size_t count, int flags, const void* addr, | ||||||||||||||||||||||||||||||||||
| 1675 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1676 | { | ||||||||||||||||||||||||||||||||||
| 1677 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 1678 | { | ||||||||||||||||||||||||||||||||||
| 1679 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 1680 | return 0; | ||||||||||||||||||||||||||||||||||
| 1681 | } | ||||||||||||||||||||||||||||||||||
| 1682 | |||||||||||||||||||||||||||||||||||
| 1683 | // Write some data. | ||||||||||||||||||||||||||||||||||
| 1684 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1685 | { | ||||||||||||||||||||||||||||||||||
| 1686 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||
| 1687 | signed_size_type bytes = socket_ops::sendto( | ||||||||||||||||||||||||||||||||||
| 1688 | s, bufs, count, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||
| 1689 | |||||||||||||||||||||||||||||||||||
| 1690 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1691 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1692 | return bytes; | ||||||||||||||||||||||||||||||||||
| 1693 | |||||||||||||||||||||||||||||||||||
| 1694 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1695 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 1696 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1697 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||
| 1698 | return 0; | ||||||||||||||||||||||||||||||||||
| 1699 | |||||||||||||||||||||||||||||||||||
| 1700 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||
| 1701 | if (socket_ops::poll_write(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||
| 1702 | return 0; | ||||||||||||||||||||||||||||||||||
| 1703 | } | ||||||||||||||||||||||||||||||||||
| 1704 | } | ||||||||||||||||||||||||||||||||||
| 1705 | |||||||||||||||||||||||||||||||||||
| 1706 | size_t sync_sendto1(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||
| 1707 | const void* data, size_t size, int flags, const void* addr, | ||||||||||||||||||||||||||||||||||
| 1708 | std::size_t addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1709 | { | ||||||||||||||||||||||||||||||||||
| 1710 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 1711 | { | ||||||||||||||||||||||||||||||||||
| 1712 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 1713 | return 0; | ||||||||||||||||||||||||||||||||||
| 1714 | } | ||||||||||||||||||||||||||||||||||
| 1715 | |||||||||||||||||||||||||||||||||||
| 1716 | // Write some data. | ||||||||||||||||||||||||||||||||||
| 1717 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1718 | { | ||||||||||||||||||||||||||||||||||
| 1719 | // Try to complete the operation without blocking. | ||||||||||||||||||||||||||||||||||
| 1720 | signed_size_type bytes = socket_ops::sendto1( | ||||||||||||||||||||||||||||||||||
| 1721 | s, data, size, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||
| 1722 | |||||||||||||||||||||||||||||||||||
| 1723 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1724 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1725 | return bytes; | ||||||||||||||||||||||||||||||||||
| 1726 | |||||||||||||||||||||||||||||||||||
| 1727 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1728 | if ((state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 1729 | || (ec != boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1730 | && ec != boost::asio::error::try_again)) | ||||||||||||||||||||||||||||||||||
| 1731 | return 0; | ||||||||||||||||||||||||||||||||||
| 1732 | |||||||||||||||||||||||||||||||||||
| 1733 | // Wait for socket to become ready. | ||||||||||||||||||||||||||||||||||
| 1734 | if (socket_ops::poll_write(s, 0, -1, ec) < 0) | ||||||||||||||||||||||||||||||||||
| 1735 | return 0; | ||||||||||||||||||||||||||||||||||
| 1736 | } | ||||||||||||||||||||||||||||||||||
| 1737 | } | ||||||||||||||||||||||||||||||||||
| 1738 | |||||||||||||||||||||||||||||||||||
| 1739 | #if !defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1740 | |||||||||||||||||||||||||||||||||||
| 1741 | bool non_blocking_sendto(socket_type s, | ||||||||||||||||||||||||||||||||||
| 1742 | const buf* bufs, size_t count, int flags, | ||||||||||||||||||||||||||||||||||
| 1743 | const void* addr, std::size_t addrlen, | ||||||||||||||||||||||||||||||||||
| 1744 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||
| 1745 | { | ||||||||||||||||||||||||||||||||||
| 1746 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1747 | { | ||||||||||||||||||||||||||||||||||
| 1748 | // Write some data. | ||||||||||||||||||||||||||||||||||
| 1749 | signed_size_type bytes = socket_ops::sendto( | ||||||||||||||||||||||||||||||||||
| 1750 | s, bufs, count, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||
| 1751 | |||||||||||||||||||||||||||||||||||
| 1752 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1753 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1754 | { | ||||||||||||||||||||||||||||||||||
| 1755 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||
| 1756 | return true; | ||||||||||||||||||||||||||||||||||
| 1757 | } | ||||||||||||||||||||||||||||||||||
| 1758 | |||||||||||||||||||||||||||||||||||
| 1759 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||
| 1760 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||
| 1761 | continue; | ||||||||||||||||||||||||||||||||||
| 1762 | |||||||||||||||||||||||||||||||||||
| 1763 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||
| 1764 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1765 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 1766 | return false; | ||||||||||||||||||||||||||||||||||
| 1767 | |||||||||||||||||||||||||||||||||||
| 1768 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1769 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1770 | return true; | ||||||||||||||||||||||||||||||||||
| 1771 | } | ||||||||||||||||||||||||||||||||||
| 1772 | } | ||||||||||||||||||||||||||||||||||
| 1773 | |||||||||||||||||||||||||||||||||||
| 1774 | bool non_blocking_sendto1(socket_type s, | ||||||||||||||||||||||||||||||||||
| 1775 | const void* data, size_t size, int flags, | ||||||||||||||||||||||||||||||||||
| 1776 | const void* addr, std::size_t addrlen, | ||||||||||||||||||||||||||||||||||
| 1777 | boost::system::error_code& ec, size_t& bytes_transferred) | ||||||||||||||||||||||||||||||||||
| 1778 | { | ||||||||||||||||||||||||||||||||||
| 1779 | for (;;) | ||||||||||||||||||||||||||||||||||
| 1780 | { | ||||||||||||||||||||||||||||||||||
| 1781 | // Write some data. | ||||||||||||||||||||||||||||||||||
| 1782 | signed_size_type bytes = socket_ops::sendto1( | ||||||||||||||||||||||||||||||||||
| 1783 | s, data, size, flags, addr, addrlen, ec); | ||||||||||||||||||||||||||||||||||
| 1784 | |||||||||||||||||||||||||||||||||||
| 1785 | // Check if operation succeeded. | ||||||||||||||||||||||||||||||||||
| 1786 | if (bytes >= 0) | ||||||||||||||||||||||||||||||||||
| 1787 | { | ||||||||||||||||||||||||||||||||||
| 1788 | bytes_transferred = bytes; | ||||||||||||||||||||||||||||||||||
| 1789 | return true; | ||||||||||||||||||||||||||||||||||
| 1790 | } | ||||||||||||||||||||||||||||||||||
| 1791 | |||||||||||||||||||||||||||||||||||
| 1792 | // Retry operation if interrupted by signal. | ||||||||||||||||||||||||||||||||||
| 1793 | if (ec == boost::asio::error::interrupted) | ||||||||||||||||||||||||||||||||||
| 1794 | continue; | ||||||||||||||||||||||||||||||||||
| 1795 | |||||||||||||||||||||||||||||||||||
| 1796 | // Check if we need to run the operation again. | ||||||||||||||||||||||||||||||||||
| 1797 | if (ec == boost::asio::error::would_block | ||||||||||||||||||||||||||||||||||
| 1798 | || ec == boost::asio::error::try_again) | ||||||||||||||||||||||||||||||||||
| 1799 | return false; | ||||||||||||||||||||||||||||||||||
| 1800 | |||||||||||||||||||||||||||||||||||
| 1801 | // Operation failed. | ||||||||||||||||||||||||||||||||||
| 1802 | bytes_transferred = 0; | ||||||||||||||||||||||||||||||||||
| 1803 | return true; | ||||||||||||||||||||||||||||||||||
| 1804 | } | ||||||||||||||||||||||||||||||||||
| 1805 | } | ||||||||||||||||||||||||||||||||||
| 1806 | |||||||||||||||||||||||||||||||||||
| 1807 | #endif // !defined(BOOST_ASIO_HAS_IOCP) | ||||||||||||||||||||||||||||||||||
| 1808 | |||||||||||||||||||||||||||||||||||
| 1809 | socket_type socket(int af, int type, int protocol, | ||||||||||||||||||||||||||||||||||
| 1810 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1811 | { | ||||||||||||||||||||||||||||||||||
| 1812 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 1813 | socket_type s = ::WSASocketW(af, type, protocol, 0, 0, WSA_FLAG_OVERLAPPED); | ||||||||||||||||||||||||||||||||||
| 1814 | get_last_error(ec, s == invalid_socket); | ||||||||||||||||||||||||||||||||||
| 1815 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 1816 | return s; | ||||||||||||||||||||||||||||||||||
| 1817 | |||||||||||||||||||||||||||||||||||
| 1818 | if (af == BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||
| 1819 | { | ||||||||||||||||||||||||||||||||||
| 1820 | // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to | ||||||||||||||||||||||||||||||||||
| 1821 | // false. This will only succeed on Windows Vista and later versions of | ||||||||||||||||||||||||||||||||||
| 1822 | // Windows, where a dual-stack IPv4/v6 implementation is available. | ||||||||||||||||||||||||||||||||||
| 1823 | DWORD optval = 0; | ||||||||||||||||||||||||||||||||||
| 1824 | ::setsockopt(s, IPPROTO_IPV6IPPROTO_IPV6, IPV6_V6ONLY26, | ||||||||||||||||||||||||||||||||||
| 1825 | reinterpret_cast<const char*>(&optval), sizeof(optval)); | ||||||||||||||||||||||||||||||||||
| 1826 | } | ||||||||||||||||||||||||||||||||||
| 1827 | |||||||||||||||||||||||||||||||||||
| 1828 | return s; | ||||||||||||||||||||||||||||||||||
| 1829 | #elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) | ||||||||||||||||||||||||||||||||||
| 1830 | socket_type s = ::socket(af, type, protocol); | ||||||||||||||||||||||||||||||||||
| 1831 | get_last_error(ec, s == invalid_socket); | ||||||||||||||||||||||||||||||||||
| 1832 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 1833 | return s; | ||||||||||||||||||||||||||||||||||
| 1834 | |||||||||||||||||||||||||||||||||||
| 1835 | int optval = 1; | ||||||||||||||||||||||||||||||||||
| 1836 | int result = ::setsockopt(s, SOL_SOCKET1, | ||||||||||||||||||||||||||||||||||
| 1837 | SO_NOSIGPIPE, &optval, sizeof(optval)); | ||||||||||||||||||||||||||||||||||
| 1838 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 1839 | if (result != 0) | ||||||||||||||||||||||||||||||||||
| 1840 | { | ||||||||||||||||||||||||||||||||||
| 1841 | ::close(s); | ||||||||||||||||||||||||||||||||||
| 1842 | return invalid_socket; | ||||||||||||||||||||||||||||||||||
| 1843 | } | ||||||||||||||||||||||||||||||||||
| 1844 | |||||||||||||||||||||||||||||||||||
| 1845 | return s; | ||||||||||||||||||||||||||||||||||
| 1846 | #else | ||||||||||||||||||||||||||||||||||
| 1847 | int s = ::socket(af, type, protocol); | ||||||||||||||||||||||||||||||||||
| 1848 | get_last_error(ec, s < 0); | ||||||||||||||||||||||||||||||||||
| 1849 | return s; | ||||||||||||||||||||||||||||||||||
| 1850 | #endif | ||||||||||||||||||||||||||||||||||
| 1851 | } | ||||||||||||||||||||||||||||||||||
| 1852 | |||||||||||||||||||||||||||||||||||
| 1853 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||
| 1854 | inline int call_setsockopt(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||
| 1855 | socket_type s, int level, int optname, | ||||||||||||||||||||||||||||||||||
| 1856 | const void* optval, std::size_t optlen) | ||||||||||||||||||||||||||||||||||
| 1857 | { | ||||||||||||||||||||||||||||||||||
| 1858 | return ::setsockopt(s, level, optname, | ||||||||||||||||||||||||||||||||||
| 1859 | (const char*)optval, (SockLenType)optlen); | ||||||||||||||||||||||||||||||||||
| 1860 | } | ||||||||||||||||||||||||||||||||||
| 1861 | |||||||||||||||||||||||||||||||||||
| 1862 | int setsockopt(socket_type s, state_type& state, int level, int optname, | ||||||||||||||||||||||||||||||||||
| 1863 | const void* optval, std::size_t optlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1864 | { | ||||||||||||||||||||||||||||||||||
| 1865 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 1866 | { | ||||||||||||||||||||||||||||||||||
| 1867 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 1868 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1869 | } | ||||||||||||||||||||||||||||||||||
| 1870 | |||||||||||||||||||||||||||||||||||
| 1871 | if (level == custom_socket_option_level && optname == always_fail_option) | ||||||||||||||||||||||||||||||||||
| 1872 | { | ||||||||||||||||||||||||||||||||||
| 1873 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 1874 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1875 | } | ||||||||||||||||||||||||||||||||||
| 1876 | |||||||||||||||||||||||||||||||||||
| 1877 | if (level == custom_socket_option_level | ||||||||||||||||||||||||||||||||||
| 1878 | && optname == enable_connection_aborted_option) | ||||||||||||||||||||||||||||||||||
| 1879 | { | ||||||||||||||||||||||||||||||||||
| 1880 | if (optlen != sizeof(int)) | ||||||||||||||||||||||||||||||||||
| 1881 | { | ||||||||||||||||||||||||||||||||||
| 1882 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 1883 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1884 | } | ||||||||||||||||||||||||||||||||||
| 1885 | |||||||||||||||||||||||||||||||||||
| 1886 | if (*static_cast<const int*>(optval)) | ||||||||||||||||||||||||||||||||||
| 1887 | state |= enable_connection_aborted; | ||||||||||||||||||||||||||||||||||
| 1888 | else | ||||||||||||||||||||||||||||||||||
| 1889 | state &= ~enable_connection_aborted; | ||||||||||||||||||||||||||||||||||
| 1890 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1891 | return 0; | ||||||||||||||||||||||||||||||||||
| 1892 | } | ||||||||||||||||||||||||||||||||||
| 1893 | |||||||||||||||||||||||||||||||||||
| 1894 | if (level == SOL_SOCKET1 && optname == SO_LINGER13) | ||||||||||||||||||||||||||||||||||
| 1895 | state |= user_set_linger; | ||||||||||||||||||||||||||||||||||
| 1896 | |||||||||||||||||||||||||||||||||||
| 1897 | #if defined(__BORLANDC__) | ||||||||||||||||||||||||||||||||||
| 1898 | // Mysteriously, using the getsockopt and setsockopt functions directly with | ||||||||||||||||||||||||||||||||||
| 1899 | // Borland C++ results in incorrect values being set and read. The bug can be | ||||||||||||||||||||||||||||||||||
| 1900 | // worked around by using function addresses resolved with GetProcAddress. | ||||||||||||||||||||||||||||||||||
| 1901 | if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) | ||||||||||||||||||||||||||||||||||
| 1902 | { | ||||||||||||||||||||||||||||||||||
| 1903 | typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int); | ||||||||||||||||||||||||||||||||||
| 1904 | if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt")) | ||||||||||||||||||||||||||||||||||
| 1905 | { | ||||||||||||||||||||||||||||||||||
| 1906 | int result = sso(s, level, optname, | ||||||||||||||||||||||||||||||||||
| 1907 | reinterpret_cast<const char*>(optval), | ||||||||||||||||||||||||||||||||||
| 1908 | static_cast<int>(optlen)); | ||||||||||||||||||||||||||||||||||
| 1909 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 1910 | return result; | ||||||||||||||||||||||||||||||||||
| 1911 | } | ||||||||||||||||||||||||||||||||||
| 1912 | } | ||||||||||||||||||||||||||||||||||
| 1913 | ec = boost::asio::error::fault; | ||||||||||||||||||||||||||||||||||
| 1914 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1915 | #else // defined(__BORLANDC__) | ||||||||||||||||||||||||||||||||||
| 1916 | int result = call_setsockopt(&msghdr::msg_namelen, | ||||||||||||||||||||||||||||||||||
| 1917 | s, level, optname, optval, optlen); | ||||||||||||||||||||||||||||||||||
| 1918 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 1919 | if (result == 0) | ||||||||||||||||||||||||||||||||||
| 1920 | { | ||||||||||||||||||||||||||||||||||
| 1921 | #if defined(__MACH__) && defined(__APPLE__) \ | ||||||||||||||||||||||||||||||||||
| 1922 | || defined(__NetBSD__) || defined(__FreeBSD__) \ | ||||||||||||||||||||||||||||||||||
| 1923 | || defined(__OpenBSD__) || defined(__QNX__) | ||||||||||||||||||||||||||||||||||
| 1924 | // To implement portable behaviour for SO_REUSEADDR with UDP sockets we | ||||||||||||||||||||||||||||||||||
| 1925 | // need to also set SO_REUSEPORT on BSD-based platforms. | ||||||||||||||||||||||||||||||||||
| 1926 | if ((state & datagram_oriented) | ||||||||||||||||||||||||||||||||||
| 1927 | && level == SOL_SOCKET1 && optname == SO_REUSEADDR2) | ||||||||||||||||||||||||||||||||||
| 1928 | { | ||||||||||||||||||||||||||||||||||
| 1929 | call_setsockopt(&msghdr::msg_namelen, s, | ||||||||||||||||||||||||||||||||||
| 1930 | SOL_SOCKET1, SO_REUSEPORT15, optval, optlen); | ||||||||||||||||||||||||||||||||||
| 1931 | } | ||||||||||||||||||||||||||||||||||
| 1932 | #endif | ||||||||||||||||||||||||||||||||||
| 1933 | } | ||||||||||||||||||||||||||||||||||
| 1934 | |||||||||||||||||||||||||||||||||||
| 1935 | return result; | ||||||||||||||||||||||||||||||||||
| 1936 | #endif // defined(__BORLANDC__) | ||||||||||||||||||||||||||||||||||
| 1937 | } | ||||||||||||||||||||||||||||||||||
| 1938 | |||||||||||||||||||||||||||||||||||
| 1939 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||
| 1940 | inline int call_getsockopt(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||
| 1941 | socket_type s, int level, int optname, | ||||||||||||||||||||||||||||||||||
| 1942 | void* optval, std::size_t* optlen) | ||||||||||||||||||||||||||||||||||
| 1943 | { | ||||||||||||||||||||||||||||||||||
| 1944 | SockLenType tmp_optlen = (SockLenType)*optlen; | ||||||||||||||||||||||||||||||||||
| 1945 | int result = ::getsockopt(s, level, optname, (char*)optval, &tmp_optlen); | ||||||||||||||||||||||||||||||||||
| 1946 | *optlen = (std::size_t)tmp_optlen; | ||||||||||||||||||||||||||||||||||
| 1947 | return result; | ||||||||||||||||||||||||||||||||||
| 1948 | } | ||||||||||||||||||||||||||||||||||
| 1949 | |||||||||||||||||||||||||||||||||||
| 1950 | int getsockopt(socket_type s, state_type state, int level, int optname, | ||||||||||||||||||||||||||||||||||
| 1951 | void* optval, size_t* optlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 1952 | { | ||||||||||||||||||||||||||||||||||
| 1953 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 1954 | { | ||||||||||||||||||||||||||||||||||
| 1955 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 1956 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1957 | } | ||||||||||||||||||||||||||||||||||
| 1958 | |||||||||||||||||||||||||||||||||||
| 1959 | if (level == custom_socket_option_level && optname == always_fail_option) | ||||||||||||||||||||||||||||||||||
| 1960 | { | ||||||||||||||||||||||||||||||||||
| 1961 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 1962 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1963 | } | ||||||||||||||||||||||||||||||||||
| 1964 | |||||||||||||||||||||||||||||||||||
| 1965 | if (level == custom_socket_option_level | ||||||||||||||||||||||||||||||||||
| 1966 | && optname == enable_connection_aborted_option) | ||||||||||||||||||||||||||||||||||
| 1967 | { | ||||||||||||||||||||||||||||||||||
| 1968 | if (*optlen != sizeof(int)) | ||||||||||||||||||||||||||||||||||
| 1969 | { | ||||||||||||||||||||||||||||||||||
| 1970 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 1971 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 1972 | } | ||||||||||||||||||||||||||||||||||
| 1973 | |||||||||||||||||||||||||||||||||||
| 1974 | *static_cast<int*>(optval) = (state & enable_connection_aborted) ? 1 : 0; | ||||||||||||||||||||||||||||||||||
| 1975 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 1976 | return 0; | ||||||||||||||||||||||||||||||||||
| 1977 | } | ||||||||||||||||||||||||||||||||||
| 1978 | |||||||||||||||||||||||||||||||||||
| 1979 | #if defined(__BORLANDC__) | ||||||||||||||||||||||||||||||||||
| 1980 | // Mysteriously, using the getsockopt and setsockopt functions directly with | ||||||||||||||||||||||||||||||||||
| 1981 | // Borland C++ results in incorrect values being set and read. The bug can be | ||||||||||||||||||||||||||||||||||
| 1982 | // worked around by using function addresses resolved with GetProcAddress. | ||||||||||||||||||||||||||||||||||
| 1983 | if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) | ||||||||||||||||||||||||||||||||||
| 1984 | { | ||||||||||||||||||||||||||||||||||
| 1985 | typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*); | ||||||||||||||||||||||||||||||||||
| 1986 | if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt")) | ||||||||||||||||||||||||||||||||||
| 1987 | { | ||||||||||||||||||||||||||||||||||
| 1988 | int tmp_optlen = static_cast<int>(*optlen); | ||||||||||||||||||||||||||||||||||
| 1989 | int result = gso(s, level, optname, | ||||||||||||||||||||||||||||||||||
| 1990 | reinterpret_cast<char*>(optval), &tmp_optlen); | ||||||||||||||||||||||||||||||||||
| 1991 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 1992 | *optlen = static_cast<size_t>(tmp_optlen); | ||||||||||||||||||||||||||||||||||
| 1993 | if (result != 0 && level == IPPROTO_IPV6IPPROTO_IPV6 && optname == IPV6_V6ONLY26 | ||||||||||||||||||||||||||||||||||
| 1994 | && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) | ||||||||||||||||||||||||||||||||||
| 1995 | { | ||||||||||||||||||||||||||||||||||
| 1996 | // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are | ||||||||||||||||||||||||||||||||||
| 1997 | // only supported on Windows Vista and later. To simplify program logic | ||||||||||||||||||||||||||||||||||
| 1998 | // we will fake success of getting this option and specify that the | ||||||||||||||||||||||||||||||||||
| 1999 | // value is non-zero (i.e. true). This corresponds to the behavior of | ||||||||||||||||||||||||||||||||||
| 2000 | // IPv6 sockets on Windows platforms pre-Vista. | ||||||||||||||||||||||||||||||||||
| 2001 | *static_cast<DWORD*>(optval) = 1; | ||||||||||||||||||||||||||||||||||
| 2002 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 2003 | } | ||||||||||||||||||||||||||||||||||
| 2004 | return result; | ||||||||||||||||||||||||||||||||||
| 2005 | } | ||||||||||||||||||||||||||||||||||
| 2006 | } | ||||||||||||||||||||||||||||||||||
| 2007 | ec = boost::asio::error::fault; | ||||||||||||||||||||||||||||||||||
| 2008 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 2009 | #elif defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2010 | int result = call_getsockopt(&msghdr::msg_namelen, | ||||||||||||||||||||||||||||||||||
| 2011 | s, level, optname, optval, optlen); | ||||||||||||||||||||||||||||||||||
| 2012 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 2013 | if (result != 0 && level == IPPROTO_IPV6IPPROTO_IPV6 && optname == IPV6_V6ONLY26 | ||||||||||||||||||||||||||||||||||
| 2014 | && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) | ||||||||||||||||||||||||||||||||||
| 2015 | { | ||||||||||||||||||||||||||||||||||
| 2016 | // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are only | ||||||||||||||||||||||||||||||||||
| 2017 | // supported on Windows Vista and later. To simplify program logic we will | ||||||||||||||||||||||||||||||||||
| 2018 | // fake success of getting this option and specify that the value is | ||||||||||||||||||||||||||||||||||
| 2019 | // non-zero (i.e. true). This corresponds to the behavior of IPv6 sockets | ||||||||||||||||||||||||||||||||||
| 2020 | // on Windows platforms pre-Vista. | ||||||||||||||||||||||||||||||||||
| 2021 | *static_cast<DWORD*>(optval) = 1; | ||||||||||||||||||||||||||||||||||
| 2022 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 2023 | } | ||||||||||||||||||||||||||||||||||
| 2024 | return result; | ||||||||||||||||||||||||||||||||||
| 2025 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2026 | int result = call_getsockopt(&msghdr::msg_namelen, | ||||||||||||||||||||||||||||||||||
| 2027 | s, level, optname, optval, optlen); | ||||||||||||||||||||||||||||||||||
| 2028 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 2029 | #if defined(__linux__1) | ||||||||||||||||||||||||||||||||||
| 2030 | if (result == 0 && level == SOL_SOCKET1 && *optlen == sizeof(int) | ||||||||||||||||||||||||||||||||||
| 2031 | && (optname == SO_SNDBUF7 || optname == SO_RCVBUF8)) | ||||||||||||||||||||||||||||||||||
| 2032 | { | ||||||||||||||||||||||||||||||||||
| 2033 | // On Linux, setting SO_SNDBUF or SO_RCVBUF to N actually causes the kernel | ||||||||||||||||||||||||||||||||||
| 2034 | // to set the buffer size to N*2. Linux puts additional stuff into the | ||||||||||||||||||||||||||||||||||
| 2035 | // buffers so that only about half is actually available to the application. | ||||||||||||||||||||||||||||||||||
| 2036 | // The retrieved value is divided by 2 here to make it appear as though the | ||||||||||||||||||||||||||||||||||
| 2037 | // correct value has been set. | ||||||||||||||||||||||||||||||||||
| 2038 | *static_cast<int*>(optval) /= 2; | ||||||||||||||||||||||||||||||||||
| 2039 | } | ||||||||||||||||||||||||||||||||||
| 2040 | #endif // defined(__linux__) | ||||||||||||||||||||||||||||||||||
| 2041 | return result; | ||||||||||||||||||||||||||||||||||
| 2042 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2043 | } | ||||||||||||||||||||||||||||||||||
| 2044 | |||||||||||||||||||||||||||||||||||
| 2045 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||
| 2046 | inline int call_getpeername(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||
| 2047 | socket_type s, void* addr, std::size_t* addrlen) | ||||||||||||||||||||||||||||||||||
| 2048 | { | ||||||||||||||||||||||||||||||||||
| 2049 | SockLenType tmp_addrlen = (SockLenType)*addrlen; | ||||||||||||||||||||||||||||||||||
| 2050 | int result = ::getpeername(s, | ||||||||||||||||||||||||||||||||||
| 2051 | static_cast<socket_addr_type*>(addr), &tmp_addrlen); | ||||||||||||||||||||||||||||||||||
| 2052 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||
| 2053 | return result; | ||||||||||||||||||||||||||||||||||
| 2054 | } | ||||||||||||||||||||||||||||||||||
| 2055 | |||||||||||||||||||||||||||||||||||
| 2056 | int getpeername(socket_type s, void* addr, std::size_t* addrlen, | ||||||||||||||||||||||||||||||||||
| 2057 | bool cached, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2058 | { | ||||||||||||||||||||||||||||||||||
| 2059 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 2060 | { | ||||||||||||||||||||||||||||||||||
| 2061 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 2062 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 2063 | } | ||||||||||||||||||||||||||||||||||
| 2064 | |||||||||||||||||||||||||||||||||||
| 2065 | #if defined(BOOST_ASIO_WINDOWS) && !defined(BOOST_ASIO_WINDOWS_APP) \ | ||||||||||||||||||||||||||||||||||
| 2066 | || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2067 | if (cached) | ||||||||||||||||||||||||||||||||||
| 2068 | { | ||||||||||||||||||||||||||||||||||
| 2069 | // Check if socket is still connected. | ||||||||||||||||||||||||||||||||||
| 2070 | DWORD connect_time = 0; | ||||||||||||||||||||||||||||||||||
| 2071 | size_t connect_time_len = sizeof(connect_time); | ||||||||||||||||||||||||||||||||||
| 2072 | if (socket_ops::getsockopt(s, 0, SOL_SOCKET1, SO_CONNECT_TIME, | ||||||||||||||||||||||||||||||||||
| 2073 | &connect_time, &connect_time_len, ec) == socket_error_retval) | ||||||||||||||||||||||||||||||||||
| 2074 | { | ||||||||||||||||||||||||||||||||||
| 2075 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 2076 | } | ||||||||||||||||||||||||||||||||||
| 2077 | if (connect_time == 0xFFFFFFFF) | ||||||||||||||||||||||||||||||||||
| 2078 | { | ||||||||||||||||||||||||||||||||||
| 2079 | ec = boost::asio::error::not_connected; | ||||||||||||||||||||||||||||||||||
| 2080 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 2081 | } | ||||||||||||||||||||||||||||||||||
| 2082 | |||||||||||||||||||||||||||||||||||
| 2083 | // The cached value is still valid. | ||||||||||||||||||||||||||||||||||
| 2084 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 2085 | return 0; | ||||||||||||||||||||||||||||||||||
| 2086 | } | ||||||||||||||||||||||||||||||||||
| 2087 | #else // defined(BOOST_ASIO_WINDOWS) && !defined(BOOST_ASIO_WINDOWS_APP) | ||||||||||||||||||||||||||||||||||
| 2088 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2089 | (void)cached; | ||||||||||||||||||||||||||||||||||
| 2090 | #endif // defined(BOOST_ASIO_WINDOWS) && !defined(BOOST_ASIO_WINDOWS_APP) | ||||||||||||||||||||||||||||||||||
| 2091 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2092 | |||||||||||||||||||||||||||||||||||
| 2093 | int result = call_getpeername(&msghdr::msg_namelen, s, addr, addrlen); | ||||||||||||||||||||||||||||||||||
| 2094 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 2095 | return result; | ||||||||||||||||||||||||||||||||||
| 2096 | } | ||||||||||||||||||||||||||||||||||
| 2097 | |||||||||||||||||||||||||||||||||||
| 2098 | template <typename SockLenType> | ||||||||||||||||||||||||||||||||||
| 2099 | inline int call_getsockname(SockLenType msghdr::*, | ||||||||||||||||||||||||||||||||||
| 2100 | socket_type s, void* addr, std::size_t* addrlen) | ||||||||||||||||||||||||||||||||||
| 2101 | { | ||||||||||||||||||||||||||||||||||
| 2102 | SockLenType tmp_addrlen = (SockLenType)*addrlen; | ||||||||||||||||||||||||||||||||||
| 2103 | int result = ::getsockname(s, | ||||||||||||||||||||||||||||||||||
| 2104 | static_cast<socket_addr_type*>(addr), &tmp_addrlen); | ||||||||||||||||||||||||||||||||||
| 2105 | *addrlen = (std::size_t)tmp_addrlen; | ||||||||||||||||||||||||||||||||||
| 2106 | return result; | ||||||||||||||||||||||||||||||||||
| 2107 | } | ||||||||||||||||||||||||||||||||||
| 2108 | |||||||||||||||||||||||||||||||||||
| 2109 | int getsockname(socket_type s, void* addr, | ||||||||||||||||||||||||||||||||||
| 2110 | std::size_t* addrlen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2111 | { | ||||||||||||||||||||||||||||||||||
| 2112 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 2113 | { | ||||||||||||||||||||||||||||||||||
| 2114 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 2115 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 2116 | } | ||||||||||||||||||||||||||||||||||
| 2117 | |||||||||||||||||||||||||||||||||||
| 2118 | int result = call_getsockname(&msghdr::msg_namelen, s, addr, addrlen); | ||||||||||||||||||||||||||||||||||
| 2119 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 2120 | return result; | ||||||||||||||||||||||||||||||||||
| 2121 | } | ||||||||||||||||||||||||||||||||||
| 2122 | |||||||||||||||||||||||||||||||||||
| 2123 | int ioctl(socket_type s, state_type& state, int cmd, | ||||||||||||||||||||||||||||||||||
| 2124 | ioctl_arg_type* arg, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2125 | { | ||||||||||||||||||||||||||||||||||
| 2126 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 2127 | { | ||||||||||||||||||||||||||||||||||
| 2128 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 2129 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 2130 | } | ||||||||||||||||||||||||||||||||||
| 2131 | |||||||||||||||||||||||||||||||||||
| 2132 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2133 | int result = ::ioctlsocket(s, cmd, arg); | ||||||||||||||||||||||||||||||||||
| 2134 | #elif defined(__MACH__) && defined(__APPLE__) \ | ||||||||||||||||||||||||||||||||||
| 2135 | || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) | ||||||||||||||||||||||||||||||||||
| 2136 | int result = ::ioctl(s, static_cast<unsigned int>(cmd), arg); | ||||||||||||||||||||||||||||||||||
| 2137 | #else | ||||||||||||||||||||||||||||||||||
| 2138 | int result = ::ioctl(s, cmd, arg); | ||||||||||||||||||||||||||||||||||
| 2139 | #endif | ||||||||||||||||||||||||||||||||||
| 2140 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 2141 | if (result >= 0) | ||||||||||||||||||||||||||||||||||
| 2142 | { | ||||||||||||||||||||||||||||||||||
| 2143 | // When updating the non-blocking mode we always perform the ioctl syscall, | ||||||||||||||||||||||||||||||||||
| 2144 | // even if the flags would otherwise indicate that the socket is already in | ||||||||||||||||||||||||||||||||||
| 2145 | // the correct state. This ensures that the underlying socket is put into | ||||||||||||||||||||||||||||||||||
| 2146 | // the state that has been requested by the user. If the ioctl syscall was | ||||||||||||||||||||||||||||||||||
| 2147 | // successful then we need to update the flags to match. | ||||||||||||||||||||||||||||||||||
| 2148 | if (cmd == static_cast<int>(FIONBIO0x5421)) | ||||||||||||||||||||||||||||||||||
| 2149 | { | ||||||||||||||||||||||||||||||||||
| 2150 | if (*arg) | ||||||||||||||||||||||||||||||||||
| 2151 | { | ||||||||||||||||||||||||||||||||||
| 2152 | state |= user_set_non_blocking; | ||||||||||||||||||||||||||||||||||
| 2153 | } | ||||||||||||||||||||||||||||||||||
| 2154 | else | ||||||||||||||||||||||||||||||||||
| 2155 | { | ||||||||||||||||||||||||||||||||||
| 2156 | // Clearing the non-blocking mode always overrides any internally-set | ||||||||||||||||||||||||||||||||||
| 2157 | // non-blocking flag. Any subsequent asynchronous operations will need | ||||||||||||||||||||||||||||||||||
| 2158 | // to re-enable non-blocking I/O. | ||||||||||||||||||||||||||||||||||
| 2159 | state &= ~(user_set_non_blocking | internal_non_blocking); | ||||||||||||||||||||||||||||||||||
| 2160 | } | ||||||||||||||||||||||||||||||||||
| 2161 | } | ||||||||||||||||||||||||||||||||||
| 2162 | } | ||||||||||||||||||||||||||||||||||
| 2163 | |||||||||||||||||||||||||||||||||||
| 2164 | return result; | ||||||||||||||||||||||||||||||||||
| 2165 | } | ||||||||||||||||||||||||||||||||||
| 2166 | |||||||||||||||||||||||||||||||||||
| 2167 | int select(int nfds, fd_set* readfds, fd_set* writefds, | ||||||||||||||||||||||||||||||||||
| 2168 | fd_set* exceptfds, timeval* timeout, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2169 | { | ||||||||||||||||||||||||||||||||||
| 2170 | #if defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||
| 2171 | exceptfds = 0; | ||||||||||||||||||||||||||||||||||
| 2172 | #endif // defined(__EMSCRIPTEN__) | ||||||||||||||||||||||||||||||||||
| 2173 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2174 | if (!readfds && !writefds && !exceptfds && timeout) | ||||||||||||||||||||||||||||||||||
| 2175 | { | ||||||||||||||||||||||||||||||||||
| 2176 | DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; | ||||||||||||||||||||||||||||||||||
| 2177 | if (milliseconds == 0) | ||||||||||||||||||||||||||||||||||
| 2178 | milliseconds = 1; // Force context switch. | ||||||||||||||||||||||||||||||||||
| 2179 | ::Sleep(milliseconds); | ||||||||||||||||||||||||||||||||||
| 2180 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 2181 | return 0; | ||||||||||||||||||||||||||||||||||
| 2182 | } | ||||||||||||||||||||||||||||||||||
| 2183 | |||||||||||||||||||||||||||||||||||
| 2184 | // The select() call allows timeout values measured in microseconds, but the | ||||||||||||||||||||||||||||||||||
| 2185 | // system clock (as wrapped by boost::posix_time::microsec_clock) typically | ||||||||||||||||||||||||||||||||||
| 2186 | // has a resolution of 10 milliseconds. This can lead to a spinning select | ||||||||||||||||||||||||||||||||||
| 2187 | // reactor, meaning increased CPU usage, when waiting for the earliest | ||||||||||||||||||||||||||||||||||
| 2188 | // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight | ||||||||||||||||||||||||||||||||||
| 2189 | // spin we'll use a minimum timeout of 1 millisecond. | ||||||||||||||||||||||||||||||||||
| 2190 | if (timeout && timeout->tv_sec == 0 | ||||||||||||||||||||||||||||||||||
| 2191 | && timeout->tv_usec > 0 && timeout->tv_usec < 1000) | ||||||||||||||||||||||||||||||||||
| 2192 | timeout->tv_usec = 1000; | ||||||||||||||||||||||||||||||||||
| 2193 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2194 | |||||||||||||||||||||||||||||||||||
| 2195 | #if defined(__hpux) && defined(__SELECT) | ||||||||||||||||||||||||||||||||||
| 2196 | timespec ts; | ||||||||||||||||||||||||||||||||||
| 2197 | ts.tv_sec = timeout ? timeout->tv_sec : 0; | ||||||||||||||||||||||||||||||||||
| 2198 | ts.tv_nsec = timeout ? timeout->tv_usec * 1000 : 0; | ||||||||||||||||||||||||||||||||||
| 2199 | int result = ::pselect(nfds, readfds, | ||||||||||||||||||||||||||||||||||
| 2200 | writefds, exceptfds, timeout ? &ts : 0, 0); | ||||||||||||||||||||||||||||||||||
| 2201 | #else | ||||||||||||||||||||||||||||||||||
| 2202 | int result = ::select(nfds, readfds, writefds, exceptfds, timeout); | ||||||||||||||||||||||||||||||||||
| 2203 | #endif | ||||||||||||||||||||||||||||||||||
| 2204 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 2205 | return result; | ||||||||||||||||||||||||||||||||||
| 2206 | } | ||||||||||||||||||||||||||||||||||
| 2207 | |||||||||||||||||||||||||||||||||||
| 2208 | int poll_read(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||
| 2209 | int msec, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2210 | { | ||||||||||||||||||||||||||||||||||
| 2211 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 2212 | { | ||||||||||||||||||||||||||||||||||
| 2213 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 2214 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 2215 | } | ||||||||||||||||||||||||||||||||||
| 2216 | |||||||||||||||||||||||||||||||||||
| 2217 | #if defined(BOOST_ASIO_WINDOWS) \ | ||||||||||||||||||||||||||||||||||
| 2218 | || defined(__CYGWIN__) \ | ||||||||||||||||||||||||||||||||||
| 2219 | || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2220 | fd_set fds; | ||||||||||||||||||||||||||||||||||
| 2221 | 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); | ||||||||||||||||||||||||||||||||||
| 2222 | FD_SET(s, &fds)((void) (((&fds)->fds_bits)[((s) / (8 * (int) sizeof ( __fd_mask)))] |= ((__fd_mask) (1UL << ((s) % (8 * (int) sizeof (__fd_mask))))))); | ||||||||||||||||||||||||||||||||||
| 2223 | timeval timeout_obj; | ||||||||||||||||||||||||||||||||||
| 2224 | timeval* timeout; | ||||||||||||||||||||||||||||||||||
| 2225 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 2226 | { | ||||||||||||||||||||||||||||||||||
| 2227 | timeout_obj.tv_sec = 0; | ||||||||||||||||||||||||||||||||||
| 2228 | timeout_obj.tv_usec = 0; | ||||||||||||||||||||||||||||||||||
| 2229 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||
| 2230 | } | ||||||||||||||||||||||||||||||||||
| 2231 | else if (msec >= 0) | ||||||||||||||||||||||||||||||||||
| 2232 | { | ||||||||||||||||||||||||||||||||||
| 2233 | timeout_obj.tv_sec = msec / 1000; | ||||||||||||||||||||||||||||||||||
| 2234 | timeout_obj.tv_usec = (msec % 1000) * 1000; | ||||||||||||||||||||||||||||||||||
| 2235 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||
| 2236 | } | ||||||||||||||||||||||||||||||||||
| 2237 | else | ||||||||||||||||||||||||||||||||||
| 2238 | timeout = 0; | ||||||||||||||||||||||||||||||||||
| 2239 | int result = ::select(s + 1, &fds, 0, 0, timeout); | ||||||||||||||||||||||||||||||||||
| 2240 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 2241 | #else // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||
| 2242 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2243 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2244 | pollfd fds; | ||||||||||||||||||||||||||||||||||
| 2245 | fds.fd = s; | ||||||||||||||||||||||||||||||||||
| 2246 | fds.events = POLLIN0x001; | ||||||||||||||||||||||||||||||||||
| 2247 | fds.revents = 0; | ||||||||||||||||||||||||||||||||||
| 2248 | int timeout = (state & user_set_non_blocking) ? 0 : msec; | ||||||||||||||||||||||||||||||||||
| 2249 | int result = ::poll(&fds, 1, timeout); | ||||||||||||||||||||||||||||||||||
| 2250 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 2251 | #endif // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||
| 2252 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2253 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2254 | if (result == 0) | ||||||||||||||||||||||||||||||||||
| 2255 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 2256 | ec = boost::asio::error::would_block; | ||||||||||||||||||||||||||||||||||
| 2257 | return result; | ||||||||||||||||||||||||||||||||||
| 2258 | } | ||||||||||||||||||||||||||||||||||
| 2259 | |||||||||||||||||||||||||||||||||||
| 2260 | int poll_write(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||
| 2261 | int msec, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2262 | { | ||||||||||||||||||||||||||||||||||
| 2263 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 2264 | { | ||||||||||||||||||||||||||||||||||
| 2265 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 2266 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 2267 | } | ||||||||||||||||||||||||||||||||||
| 2268 | |||||||||||||||||||||||||||||||||||
| 2269 | #if defined(BOOST_ASIO_WINDOWS) \ | ||||||||||||||||||||||||||||||||||
| 2270 | || defined(__CYGWIN__) \ | ||||||||||||||||||||||||||||||||||
| 2271 | || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2272 | fd_set fds; | ||||||||||||||||||||||||||||||||||
| 2273 | 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); | ||||||||||||||||||||||||||||||||||
| 2274 | FD_SET(s, &fds)((void) (((&fds)->fds_bits)[((s) / (8 * (int) sizeof ( __fd_mask)))] |= ((__fd_mask) (1UL << ((s) % (8 * (int) sizeof (__fd_mask))))))); | ||||||||||||||||||||||||||||||||||
| 2275 | timeval timeout_obj; | ||||||||||||||||||||||||||||||||||
| 2276 | timeval* timeout; | ||||||||||||||||||||||||||||||||||
| 2277 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 2278 | { | ||||||||||||||||||||||||||||||||||
| 2279 | timeout_obj.tv_sec = 0; | ||||||||||||||||||||||||||||||||||
| 2280 | timeout_obj.tv_usec = 0; | ||||||||||||||||||||||||||||||||||
| 2281 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||
| 2282 | } | ||||||||||||||||||||||||||||||||||
| 2283 | else if (msec >= 0) | ||||||||||||||||||||||||||||||||||
| 2284 | { | ||||||||||||||||||||||||||||||||||
| 2285 | timeout_obj.tv_sec = msec / 1000; | ||||||||||||||||||||||||||||||||||
| 2286 | timeout_obj.tv_usec = (msec % 1000) * 1000; | ||||||||||||||||||||||||||||||||||
| 2287 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||
| 2288 | } | ||||||||||||||||||||||||||||||||||
| 2289 | else | ||||||||||||||||||||||||||||||||||
| 2290 | timeout = 0; | ||||||||||||||||||||||||||||||||||
| 2291 | int result = ::select(s + 1, 0, &fds, 0, timeout); | ||||||||||||||||||||||||||||||||||
| 2292 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 2293 | #else // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||
| 2294 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2295 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2296 | pollfd fds; | ||||||||||||||||||||||||||||||||||
| 2297 | fds.fd = s; | ||||||||||||||||||||||||||||||||||
| 2298 | fds.events = POLLOUT0x004; | ||||||||||||||||||||||||||||||||||
| 2299 | fds.revents = 0; | ||||||||||||||||||||||||||||||||||
| 2300 | int timeout = (state & user_set_non_blocking) ? 0 : msec; | ||||||||||||||||||||||||||||||||||
| 2301 | int result = ::poll(&fds, 1, timeout); | ||||||||||||||||||||||||||||||||||
| 2302 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 2303 | #endif // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||
| 2304 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2305 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2306 | if (result == 0) | ||||||||||||||||||||||||||||||||||
| 2307 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 2308 | ec = boost::asio::error::would_block; | ||||||||||||||||||||||||||||||||||
| 2309 | return result; | ||||||||||||||||||||||||||||||||||
| 2310 | } | ||||||||||||||||||||||||||||||||||
| 2311 | |||||||||||||||||||||||||||||||||||
| 2312 | int poll_error(socket_type s, state_type state, | ||||||||||||||||||||||||||||||||||
| 2313 | int msec, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2314 | { | ||||||||||||||||||||||||||||||||||
| 2315 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 2316 | { | ||||||||||||||||||||||||||||||||||
| 2317 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 2318 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 2319 | } | ||||||||||||||||||||||||||||||||||
| 2320 | |||||||||||||||||||||||||||||||||||
| 2321 | #if defined(BOOST_ASIO_WINDOWS) \ | ||||||||||||||||||||||||||||||||||
| 2322 | || defined(__CYGWIN__) \ | ||||||||||||||||||||||||||||||||||
| 2323 | || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2324 | fd_set fds; | ||||||||||||||||||||||||||||||||||
| 2325 | 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); | ||||||||||||||||||||||||||||||||||
| 2326 | FD_SET(s, &fds)((void) (((&fds)->fds_bits)[((s) / (8 * (int) sizeof ( __fd_mask)))] |= ((__fd_mask) (1UL << ((s) % (8 * (int) sizeof (__fd_mask))))))); | ||||||||||||||||||||||||||||||||||
| 2327 | timeval timeout_obj; | ||||||||||||||||||||||||||||||||||
| 2328 | timeval* timeout; | ||||||||||||||||||||||||||||||||||
| 2329 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 2330 | { | ||||||||||||||||||||||||||||||||||
| 2331 | timeout_obj.tv_sec = 0; | ||||||||||||||||||||||||||||||||||
| 2332 | timeout_obj.tv_usec = 0; | ||||||||||||||||||||||||||||||||||
| 2333 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||
| 2334 | } | ||||||||||||||||||||||||||||||||||
| 2335 | else if (msec >= 0) | ||||||||||||||||||||||||||||||||||
| 2336 | { | ||||||||||||||||||||||||||||||||||
| 2337 | timeout_obj.tv_sec = msec / 1000; | ||||||||||||||||||||||||||||||||||
| 2338 | timeout_obj.tv_usec = (msec % 1000) * 1000; | ||||||||||||||||||||||||||||||||||
| 2339 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||
| 2340 | } | ||||||||||||||||||||||||||||||||||
| 2341 | else | ||||||||||||||||||||||||||||||||||
| 2342 | timeout = 0; | ||||||||||||||||||||||||||||||||||
| 2343 | int result = ::select(s + 1, 0, 0, &fds, timeout); | ||||||||||||||||||||||||||||||||||
| 2344 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 2345 | #else // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||
| 2346 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2347 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2348 | pollfd fds; | ||||||||||||||||||||||||||||||||||
| 2349 | fds.fd = s; | ||||||||||||||||||||||||||||||||||
| 2350 | fds.events = POLLPRI0x002 | POLLERR0x008 | POLLHUP0x010; | ||||||||||||||||||||||||||||||||||
| 2351 | fds.revents = 0; | ||||||||||||||||||||||||||||||||||
| 2352 | int timeout = (state & user_set_non_blocking) ? 0 : msec; | ||||||||||||||||||||||||||||||||||
| 2353 | int result = ::poll(&fds, 1, timeout); | ||||||||||||||||||||||||||||||||||
| 2354 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 2355 | #endif // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||
| 2356 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2357 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2358 | if (result == 0) | ||||||||||||||||||||||||||||||||||
| 2359 | if (state & user_set_non_blocking) | ||||||||||||||||||||||||||||||||||
| 2360 | ec = boost::asio::error::would_block; | ||||||||||||||||||||||||||||||||||
| 2361 | return result; | ||||||||||||||||||||||||||||||||||
| 2362 | } | ||||||||||||||||||||||||||||||||||
| 2363 | |||||||||||||||||||||||||||||||||||
| 2364 | int poll_connect(socket_type s, int msec, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2365 | { | ||||||||||||||||||||||||||||||||||
| 2366 | if (s == invalid_socket) | ||||||||||||||||||||||||||||||||||
| 2367 | { | ||||||||||||||||||||||||||||||||||
| 2368 | ec = boost::asio::error::bad_descriptor; | ||||||||||||||||||||||||||||||||||
| 2369 | return socket_error_retval; | ||||||||||||||||||||||||||||||||||
| 2370 | } | ||||||||||||||||||||||||||||||||||
| 2371 | |||||||||||||||||||||||||||||||||||
| 2372 | #if defined(BOOST_ASIO_WINDOWS) \ | ||||||||||||||||||||||||||||||||||
| 2373 | || defined(__CYGWIN__) \ | ||||||||||||||||||||||||||||||||||
| 2374 | || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2375 | fd_set write_fds; | ||||||||||||||||||||||||||||||||||
| 2376 | 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); | ||||||||||||||||||||||||||||||||||
| 2377 | 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))))))); | ||||||||||||||||||||||||||||||||||
| 2378 | fd_set except_fds; | ||||||||||||||||||||||||||||||||||
| 2379 | 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); | ||||||||||||||||||||||||||||||||||
| 2380 | 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))))))); | ||||||||||||||||||||||||||||||||||
| 2381 | timeval timeout_obj; | ||||||||||||||||||||||||||||||||||
| 2382 | timeval* timeout; | ||||||||||||||||||||||||||||||||||
| 2383 | if (msec >= 0) | ||||||||||||||||||||||||||||||||||
| 2384 | { | ||||||||||||||||||||||||||||||||||
| 2385 | timeout_obj.tv_sec = msec / 1000; | ||||||||||||||||||||||||||||||||||
| 2386 | timeout_obj.tv_usec = (msec % 1000) * 1000; | ||||||||||||||||||||||||||||||||||
| 2387 | timeout = &timeout_obj; | ||||||||||||||||||||||||||||||||||
| 2388 | } | ||||||||||||||||||||||||||||||||||
| 2389 | else | ||||||||||||||||||||||||||||||||||
| 2390 | timeout = 0; | ||||||||||||||||||||||||||||||||||
| 2391 | int result = ::select(s + 1, 0, &write_fds, &except_fds, timeout); | ||||||||||||||||||||||||||||||||||
| 2392 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 2393 | return result; | ||||||||||||||||||||||||||||||||||
| 2394 | #else // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||
| 2395 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2396 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2397 | pollfd fds; | ||||||||||||||||||||||||||||||||||
| 2398 | fds.fd = s; | ||||||||||||||||||||||||||||||||||
| 2399 | fds.events = POLLOUT0x004; | ||||||||||||||||||||||||||||||||||
| 2400 | fds.revents = 0; | ||||||||||||||||||||||||||||||||||
| 2401 | int result = ::poll(&fds, 1, msec); | ||||||||||||||||||||||||||||||||||
| 2402 | get_last_error(ec, result < 0); | ||||||||||||||||||||||||||||||||||
| 2403 | return result; | ||||||||||||||||||||||||||||||||||
| 2404 | #endif // defined(BOOST_ASIO_WINDOWS) | ||||||||||||||||||||||||||||||||||
| 2405 | // || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2406 | // || defined(__SYMBIAN32__) | ||||||||||||||||||||||||||||||||||
| 2407 | } | ||||||||||||||||||||||||||||||||||
| 2408 | |||||||||||||||||||||||||||||||||||
| 2409 | #endif // !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 2410 | |||||||||||||||||||||||||||||||||||
| 2411 | const char* inet_ntop(int af, const void* src, char* dest, size_t length, | ||||||||||||||||||||||||||||||||||
| 2412 | unsigned long scope_id, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2413 | { | ||||||||||||||||||||||||||||||||||
| 2414 | clear_last_error(); | ||||||||||||||||||||||||||||||||||
| 2415 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 2416 | using namespace std; // For sprintf. | ||||||||||||||||||||||||||||||||||
| 2417 | const unsigned char* bytes = static_cast<const unsigned char*>(src); | ||||||||||||||||||||||||||||||||||
| 2418 | if (af == BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||
| 2419 | { | ||||||||||||||||||||||||||||||||||
| 2420 | sprintf_s(dest, length, "%u.%u.%u.%u", | ||||||||||||||||||||||||||||||||||
| 2421 | bytes[0], bytes[1], bytes[2], bytes[3]); | ||||||||||||||||||||||||||||||||||
| 2422 | return dest; | ||||||||||||||||||||||||||||||||||
| 2423 | } | ||||||||||||||||||||||||||||||||||
| 2424 | else if (af == BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||
| 2425 | { | ||||||||||||||||||||||||||||||||||
| 2426 | size_t n = 0, b = 0, z = 0; | ||||||||||||||||||||||||||||||||||
| 2427 | while (n < length && b < 16) | ||||||||||||||||||||||||||||||||||
| 2428 | { | ||||||||||||||||||||||||||||||||||
| 2429 | if (bytes[b] == 0 && bytes[b + 1] == 0 && z == 0) | ||||||||||||||||||||||||||||||||||
| 2430 | { | ||||||||||||||||||||||||||||||||||
| 2431 | do b += 2; while (b < 16 && bytes[b] == 0 && bytes[b + 1] == 0); | ||||||||||||||||||||||||||||||||||
| 2432 | n += sprintf_s(dest + n, length - n, ":%s", b < 16 ? "" : ":"), ++z; | ||||||||||||||||||||||||||||||||||
| 2433 | } | ||||||||||||||||||||||||||||||||||
| 2434 | else | ||||||||||||||||||||||||||||||||||
| 2435 | { | ||||||||||||||||||||||||||||||||||
| 2436 | n += sprintf_s(dest + n, length - n, "%s%x", b ? ":" : "", | ||||||||||||||||||||||||||||||||||
| 2437 | (static_cast<u_long_type>(bytes[b]) << 8) | bytes[b + 1]); | ||||||||||||||||||||||||||||||||||
| 2438 | b += 2; | ||||||||||||||||||||||||||||||||||
| 2439 | } | ||||||||||||||||||||||||||||||||||
| 2440 | } | ||||||||||||||||||||||||||||||||||
| 2441 | if (scope_id) | ||||||||||||||||||||||||||||||||||
| 2442 | n += sprintf_s(dest + n, length - n, "%%%lu", scope_id); | ||||||||||||||||||||||||||||||||||
| 2443 | return dest; | ||||||||||||||||||||||||||||||||||
| 2444 | } | ||||||||||||||||||||||||||||||||||
| 2445 | else | ||||||||||||||||||||||||||||||||||
| 2446 | { | ||||||||||||||||||||||||||||||||||
| 2447 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||
| 2448 | return 0; | ||||||||||||||||||||||||||||||||||
| 2449 | } | ||||||||||||||||||||||||||||||||||
| 2450 | #elif defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2451 | using namespace std; // For memcpy. | ||||||||||||||||||||||||||||||||||
| 2452 | |||||||||||||||||||||||||||||||||||
| 2453 | if (af != BOOST_ASIO_OS_DEF(AF_INET)2 && af != BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||
| 2454 | { | ||||||||||||||||||||||||||||||||||
| 2455 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||
| 2456 | return 0; | ||||||||||||||||||||||||||||||||||
| 2457 | } | ||||||||||||||||||||||||||||||||||
| 2458 | |||||||||||||||||||||||||||||||||||
| 2459 | union | ||||||||||||||||||||||||||||||||||
| 2460 | { | ||||||||||||||||||||||||||||||||||
| 2461 | socket_addr_type base; | ||||||||||||||||||||||||||||||||||
| 2462 | sockaddr_storage_type storage; | ||||||||||||||||||||||||||||||||||
| 2463 | sockaddr_in4_type v4; | ||||||||||||||||||||||||||||||||||
| 2464 | sockaddr_in6_type v6; | ||||||||||||||||||||||||||||||||||
| 2465 | } address; | ||||||||||||||||||||||||||||||||||
| 2466 | DWORD address_length; | ||||||||||||||||||||||||||||||||||
| 2467 | if (af == BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||
| 2468 | { | ||||||||||||||||||||||||||||||||||
| 2469 | address_length = sizeof(sockaddr_in4_type); | ||||||||||||||||||||||||||||||||||
| 2470 | address.v4.sin_family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||
| 2471 | address.v4.sin_port = 0; | ||||||||||||||||||||||||||||||||||
| 2472 | memcpy(&address.v4.sin_addr, src, sizeof(in4_addr_type)); | ||||||||||||||||||||||||||||||||||
| 2473 | } | ||||||||||||||||||||||||||||||||||
| 2474 | else // AF_INET6 | ||||||||||||||||||||||||||||||||||
| 2475 | { | ||||||||||||||||||||||||||||||||||
| 2476 | address_length = sizeof(sockaddr_in6_type); | ||||||||||||||||||||||||||||||||||
| 2477 | address.v6.sin6_family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||
| 2478 | address.v6.sin6_port = 0; | ||||||||||||||||||||||||||||||||||
| 2479 | address.v6.sin6_flowinfo = 0; | ||||||||||||||||||||||||||||||||||
| 2480 | address.v6.sin6_scope_id = scope_id; | ||||||||||||||||||||||||||||||||||
| 2481 | memcpy(&address.v6.sin6_addr, src, sizeof(in6_addr_type)); | ||||||||||||||||||||||||||||||||||
| 2482 | } | ||||||||||||||||||||||||||||||||||
| 2483 | |||||||||||||||||||||||||||||||||||
| 2484 | DWORD string_length = static_cast<DWORD>(length); | ||||||||||||||||||||||||||||||||||
| 2485 | #if defined(BOOST_NO_ANSI_APIS) || (defined(_MSC_VER) && (_MSC_VER >= 1800)) | ||||||||||||||||||||||||||||||||||
| 2486 | LPWSTR string_buffer = (LPWSTR)_alloca(length * sizeof(WCHAR)); | ||||||||||||||||||||||||||||||||||
| 2487 | int result = ::WSAAddressToStringW(&address.base, | ||||||||||||||||||||||||||||||||||
| 2488 | address_length, 0, string_buffer, &string_length); | ||||||||||||||||||||||||||||||||||
| 2489 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 2490 | ::WideCharToMultiByte(CP_ACP, 0, string_buffer, -1, | ||||||||||||||||||||||||||||||||||
| 2491 | dest, static_cast<int>(length), 0, 0); | ||||||||||||||||||||||||||||||||||
| 2492 | #else | ||||||||||||||||||||||||||||||||||
| 2493 | int result = ::WSAAddressToStringA(&address.base, | ||||||||||||||||||||||||||||||||||
| 2494 | address_length, 0, dest, &string_length); | ||||||||||||||||||||||||||||||||||
| 2495 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 2496 | #endif | ||||||||||||||||||||||||||||||||||
| 2497 | |||||||||||||||||||||||||||||||||||
| 2498 | // Windows may set error code on success. | ||||||||||||||||||||||||||||||||||
| 2499 | if (result != socket_error_retval) | ||||||||||||||||||||||||||||||||||
| 2500 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 2501 | |||||||||||||||||||||||||||||||||||
| 2502 | // Windows may not set an error code on failure. | ||||||||||||||||||||||||||||||||||
| 2503 | else if (result == socket_error_retval && !ec) | ||||||||||||||||||||||||||||||||||
| 2504 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2505 | |||||||||||||||||||||||||||||||||||
| 2506 | return result == socket_error_retval ? 0 : dest; | ||||||||||||||||||||||||||||||||||
| 2507 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2508 | const char* result = ::inet_ntop(af, src, dest, static_cast<int>(length)); | ||||||||||||||||||||||||||||||||||
| 2509 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 2510 | if (result == 0 && !ec) | ||||||||||||||||||||||||||||||||||
| 2511 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2512 | if (result != 0 && af == BOOST_ASIO_OS_DEF(AF_INET6)10 && scope_id != 0) | ||||||||||||||||||||||||||||||||||
| 2513 | { | ||||||||||||||||||||||||||||||||||
| 2514 | using namespace std; // For strcat and sprintf. | ||||||||||||||||||||||||||||||||||
| 2515 | char if_name[(IF_NAMESIZE16 > 21 ? IF_NAMESIZE16 : 21) + 1] = "%"; | ||||||||||||||||||||||||||||||||||
| 2516 | const in6_addr_type* ipv6_address = static_cast<const in6_addr_type*>(src); | ||||||||||||||||||||||||||||||||||
| 2517 | bool is_link_local = ((ipv6_address->s6_addr__in6_u.__u6_addr8[0] == 0xfe) | ||||||||||||||||||||||||||||||||||
| 2518 | && ((ipv6_address->s6_addr__in6_u.__u6_addr8[1] & 0xc0) == 0x80)); | ||||||||||||||||||||||||||||||||||
| 2519 | bool is_multicast_link_local = ((ipv6_address->s6_addr__in6_u.__u6_addr8[0] == 0xff) | ||||||||||||||||||||||||||||||||||
| 2520 | && ((ipv6_address->s6_addr__in6_u.__u6_addr8[1] & 0x0f) == 0x02)); | ||||||||||||||||||||||||||||||||||
| 2521 | if ((!is_link_local && !is_multicast_link_local) | ||||||||||||||||||||||||||||||||||
| 2522 | || if_indextoname(static_cast<unsigned>(scope_id), if_name + 1) == 0) | ||||||||||||||||||||||||||||||||||
| 2523 | #if defined(BOOST_ASIO_HAS_SNPRINTF) | ||||||||||||||||||||||||||||||||||
| 2524 | snprintf(if_name + 1, sizeof(if_name) - 1, "%lu", scope_id); | ||||||||||||||||||||||||||||||||||
| 2525 | #else // defined(BOOST_ASIO_HAS_SNPRINTF) | ||||||||||||||||||||||||||||||||||
| 2526 | sprintf(if_name + 1, "%lu", scope_id); | ||||||||||||||||||||||||||||||||||
| 2527 | #endif // defined(BOOST_ASIO_HAS_SNPRINTF) | ||||||||||||||||||||||||||||||||||
| 2528 | strcat(dest, if_name); | ||||||||||||||||||||||||||||||||||
| 2529 | } | ||||||||||||||||||||||||||||||||||
| 2530 | return result; | ||||||||||||||||||||||||||||||||||
| 2531 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2532 | } | ||||||||||||||||||||||||||||||||||
| 2533 | |||||||||||||||||||||||||||||||||||
| 2534 | int inet_pton(int af, const char* src, void* dest, | ||||||||||||||||||||||||||||||||||
| 2535 | unsigned long* scope_id, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2536 | { | ||||||||||||||||||||||||||||||||||
| 2537 | clear_last_error(); | ||||||||||||||||||||||||||||||||||
| 2538 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 2539 | using namespace std; // For sscanf. | ||||||||||||||||||||||||||||||||||
| 2540 | unsigned char* bytes = static_cast<unsigned char*>(dest); | ||||||||||||||||||||||||||||||||||
| 2541 | if (af == BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||
| 2542 | { | ||||||||||||||||||||||||||||||||||
| 2543 | unsigned int b0, b1, b2, b3; | ||||||||||||||||||||||||||||||||||
| 2544 | if (sscanf_s(src, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) != 4) | ||||||||||||||||||||||||||||||||||
| 2545 | { | ||||||||||||||||||||||||||||||||||
| 2546 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2547 | return -1; | ||||||||||||||||||||||||||||||||||
| 2548 | } | ||||||||||||||||||||||||||||||||||
| 2549 | if (b0 > 255 || b1 > 255 || b2 > 255 || b3 > 255) | ||||||||||||||||||||||||||||||||||
| 2550 | { | ||||||||||||||||||||||||||||||||||
| 2551 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2552 | return -1; | ||||||||||||||||||||||||||||||||||
| 2553 | } | ||||||||||||||||||||||||||||||||||
| 2554 | bytes[0] = static_cast<unsigned char>(b0); | ||||||||||||||||||||||||||||||||||
| 2555 | bytes[1] = static_cast<unsigned char>(b1); | ||||||||||||||||||||||||||||||||||
| 2556 | bytes[2] = static_cast<unsigned char>(b2); | ||||||||||||||||||||||||||||||||||
| 2557 | bytes[3] = static_cast<unsigned char>(b3); | ||||||||||||||||||||||||||||||||||
| 2558 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 2559 | return 1; | ||||||||||||||||||||||||||||||||||
| 2560 | } | ||||||||||||||||||||||||||||||||||
| 2561 | else if (af == BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||
| 2562 | { | ||||||||||||||||||||||||||||||||||
| 2563 | unsigned char* bytes = static_cast<unsigned char*>(dest); | ||||||||||||||||||||||||||||||||||
| 2564 | std::memset(bytes, 0, 16); | ||||||||||||||||||||||||||||||||||
| 2565 | unsigned char back_bytes[16] = { 0 }; | ||||||||||||||||||||||||||||||||||
| 2566 | int num_front_bytes = 0, num_back_bytes = 0; | ||||||||||||||||||||||||||||||||||
| 2567 | const char* p = src; | ||||||||||||||||||||||||||||||||||
| 2568 | |||||||||||||||||||||||||||||||||||
| 2569 | enum { fword, fcolon, bword, scope, done } state = fword; | ||||||||||||||||||||||||||||||||||
| 2570 | unsigned long current_word = 0; | ||||||||||||||||||||||||||||||||||
| 2571 | while (state != done) | ||||||||||||||||||||||||||||||||||
| 2572 | { | ||||||||||||||||||||||||||||||||||
| 2573 | if (current_word > 0xFFFF) | ||||||||||||||||||||||||||||||||||
| 2574 | { | ||||||||||||||||||||||||||||||||||
| 2575 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2576 | return -1; | ||||||||||||||||||||||||||||||||||
| 2577 | } | ||||||||||||||||||||||||||||||||||
| 2578 | |||||||||||||||||||||||||||||||||||
| 2579 | switch (state) | ||||||||||||||||||||||||||||||||||
| 2580 | { | ||||||||||||||||||||||||||||||||||
| 2581 | case fword: | ||||||||||||||||||||||||||||||||||
| 2582 | if (*p >= '0' && *p <= '9') | ||||||||||||||||||||||||||||||||||
| 2583 | current_word = current_word * 16 + *p++ - '0'; | ||||||||||||||||||||||||||||||||||
| 2584 | else if (*p >= 'a' && *p <= 'f') | ||||||||||||||||||||||||||||||||||
| 2585 | current_word = current_word * 16 + *p++ - 'a' + 10; | ||||||||||||||||||||||||||||||||||
| 2586 | else if (*p >= 'A' && *p <= 'F') | ||||||||||||||||||||||||||||||||||
| 2587 | current_word = current_word * 16 + *p++ - 'A' + 10; | ||||||||||||||||||||||||||||||||||
| 2588 | else | ||||||||||||||||||||||||||||||||||
| 2589 | { | ||||||||||||||||||||||||||||||||||
| 2590 | if (num_front_bytes == 16) | ||||||||||||||||||||||||||||||||||
| 2591 | { | ||||||||||||||||||||||||||||||||||
| 2592 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2593 | return -1; | ||||||||||||||||||||||||||||||||||
| 2594 | } | ||||||||||||||||||||||||||||||||||
| 2595 | |||||||||||||||||||||||||||||||||||
| 2596 | bytes[num_front_bytes++] = (current_word >> 8) & 0xFF; | ||||||||||||||||||||||||||||||||||
| 2597 | bytes[num_front_bytes++] = current_word & 0xFF; | ||||||||||||||||||||||||||||||||||
| 2598 | current_word = 0; | ||||||||||||||||||||||||||||||||||
| 2599 | |||||||||||||||||||||||||||||||||||
| 2600 | if (*p == ':') | ||||||||||||||||||||||||||||||||||
| 2601 | state = fcolon, ++p; | ||||||||||||||||||||||||||||||||||
| 2602 | else if (*p == '%') | ||||||||||||||||||||||||||||||||||
| 2603 | state = scope, ++p; | ||||||||||||||||||||||||||||||||||
| 2604 | else if (*p == 0) | ||||||||||||||||||||||||||||||||||
| 2605 | state = done; | ||||||||||||||||||||||||||||||||||
| 2606 | else | ||||||||||||||||||||||||||||||||||
| 2607 | { | ||||||||||||||||||||||||||||||||||
| 2608 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2609 | return -1; | ||||||||||||||||||||||||||||||||||
| 2610 | } | ||||||||||||||||||||||||||||||||||
| 2611 | } | ||||||||||||||||||||||||||||||||||
| 2612 | break; | ||||||||||||||||||||||||||||||||||
| 2613 | |||||||||||||||||||||||||||||||||||
| 2614 | case fcolon: | ||||||||||||||||||||||||||||||||||
| 2615 | if (*p == ':') | ||||||||||||||||||||||||||||||||||
| 2616 | state = bword, ++p; | ||||||||||||||||||||||||||||||||||
| 2617 | else | ||||||||||||||||||||||||||||||||||
| 2618 | state = fword; | ||||||||||||||||||||||||||||||||||
| 2619 | break; | ||||||||||||||||||||||||||||||||||
| 2620 | |||||||||||||||||||||||||||||||||||
| 2621 | case bword: | ||||||||||||||||||||||||||||||||||
| 2622 | if (*p >= '0' && *p <= '9') | ||||||||||||||||||||||||||||||||||
| 2623 | current_word = current_word * 16 + *p++ - '0'; | ||||||||||||||||||||||||||||||||||
| 2624 | else if (*p >= 'a' && *p <= 'f') | ||||||||||||||||||||||||||||||||||
| 2625 | current_word = current_word * 16 + *p++ - 'a' + 10; | ||||||||||||||||||||||||||||||||||
| 2626 | else if (*p >= 'A' && *p <= 'F') | ||||||||||||||||||||||||||||||||||
| 2627 | current_word = current_word * 16 + *p++ - 'A' + 10; | ||||||||||||||||||||||||||||||||||
| 2628 | else | ||||||||||||||||||||||||||||||||||
| 2629 | { | ||||||||||||||||||||||||||||||||||
| 2630 | if (num_front_bytes + num_back_bytes == 16) | ||||||||||||||||||||||||||||||||||
| 2631 | { | ||||||||||||||||||||||||||||||||||
| 2632 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2633 | return -1; | ||||||||||||||||||||||||||||||||||
| 2634 | } | ||||||||||||||||||||||||||||||||||
| 2635 | |||||||||||||||||||||||||||||||||||
| 2636 | back_bytes[num_back_bytes++] = (current_word >> 8) & 0xFF; | ||||||||||||||||||||||||||||||||||
| 2637 | back_bytes[num_back_bytes++] = current_word & 0xFF; | ||||||||||||||||||||||||||||||||||
| 2638 | current_word = 0; | ||||||||||||||||||||||||||||||||||
| 2639 | |||||||||||||||||||||||||||||||||||
| 2640 | if (*p == ':') | ||||||||||||||||||||||||||||||||||
| 2641 | state = bword, ++p; | ||||||||||||||||||||||||||||||||||
| 2642 | else if (*p == '%') | ||||||||||||||||||||||||||||||||||
| 2643 | state = scope, ++p; | ||||||||||||||||||||||||||||||||||
| 2644 | else if (*p == 0) | ||||||||||||||||||||||||||||||||||
| 2645 | state = done; | ||||||||||||||||||||||||||||||||||
| 2646 | else | ||||||||||||||||||||||||||||||||||
| 2647 | { | ||||||||||||||||||||||||||||||||||
| 2648 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2649 | return -1; | ||||||||||||||||||||||||||||||||||
| 2650 | } | ||||||||||||||||||||||||||||||||||
| 2651 | } | ||||||||||||||||||||||||||||||||||
| 2652 | break; | ||||||||||||||||||||||||||||||||||
| 2653 | |||||||||||||||||||||||||||||||||||
| 2654 | case scope: | ||||||||||||||||||||||||||||||||||
| 2655 | if (*p >= '0' && *p <= '9') | ||||||||||||||||||||||||||||||||||
| 2656 | current_word = current_word * 10 + *p++ - '0'; | ||||||||||||||||||||||||||||||||||
| 2657 | else if (*p == 0) | ||||||||||||||||||||||||||||||||||
| 2658 | *scope_id = current_word, state = done; | ||||||||||||||||||||||||||||||||||
| 2659 | else | ||||||||||||||||||||||||||||||||||
| 2660 | { | ||||||||||||||||||||||||||||||||||
| 2661 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2662 | return -1; | ||||||||||||||||||||||||||||||||||
| 2663 | } | ||||||||||||||||||||||||||||||||||
| 2664 | break; | ||||||||||||||||||||||||||||||||||
| 2665 | |||||||||||||||||||||||||||||||||||
| 2666 | default: | ||||||||||||||||||||||||||||||||||
| 2667 | break; | ||||||||||||||||||||||||||||||||||
| 2668 | } | ||||||||||||||||||||||||||||||||||
| 2669 | } | ||||||||||||||||||||||||||||||||||
| 2670 | |||||||||||||||||||||||||||||||||||
| 2671 | for (int i = 0; i < num_back_bytes; ++i) | ||||||||||||||||||||||||||||||||||
| 2672 | bytes[16 - num_back_bytes + i] = back_bytes[i]; | ||||||||||||||||||||||||||||||||||
| 2673 | |||||||||||||||||||||||||||||||||||
| 2674 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 2675 | return 1; | ||||||||||||||||||||||||||||||||||
| 2676 | } | ||||||||||||||||||||||||||||||||||
| 2677 | else | ||||||||||||||||||||||||||||||||||
| 2678 | { | ||||||||||||||||||||||||||||||||||
| 2679 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||
| 2680 | return -1; | ||||||||||||||||||||||||||||||||||
| 2681 | } | ||||||||||||||||||||||||||||||||||
| 2682 | #elif defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2683 | using namespace std; // For memcpy and strcmp. | ||||||||||||||||||||||||||||||||||
| 2684 | |||||||||||||||||||||||||||||||||||
| 2685 | if (af != BOOST_ASIO_OS_DEF(AF_INET)2 && af != BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||
| 2686 | { | ||||||||||||||||||||||||||||||||||
| 2687 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||
| 2688 | return -1; | ||||||||||||||||||||||||||||||||||
| 2689 | } | ||||||||||||||||||||||||||||||||||
| 2690 | |||||||||||||||||||||||||||||||||||
| 2691 | union | ||||||||||||||||||||||||||||||||||
| 2692 | { | ||||||||||||||||||||||||||||||||||
| 2693 | socket_addr_type base; | ||||||||||||||||||||||||||||||||||
| 2694 | sockaddr_storage_type storage; | ||||||||||||||||||||||||||||||||||
| 2695 | sockaddr_in4_type v4; | ||||||||||||||||||||||||||||||||||
| 2696 | sockaddr_in6_type v6; | ||||||||||||||||||||||||||||||||||
| 2697 | } address; | ||||||||||||||||||||||||||||||||||
| 2698 | int address_length = sizeof(sockaddr_storage_type); | ||||||||||||||||||||||||||||||||||
| 2699 | #if defined(BOOST_NO_ANSI_APIS) || (defined(_MSC_VER) && (_MSC_VER >= 1800)) | ||||||||||||||||||||||||||||||||||
| 2700 | int num_wide_chars = static_cast<int>(strlen(src)) + 1; | ||||||||||||||||||||||||||||||||||
| 2701 | LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR)); | ||||||||||||||||||||||||||||||||||
| 2702 | ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars); | ||||||||||||||||||||||||||||||||||
| 2703 | int result = ::WSAStringToAddressW(wide_buffer, | ||||||||||||||||||||||||||||||||||
| 2704 | af, 0, &address.base, &address_length); | ||||||||||||||||||||||||||||||||||
| 2705 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 2706 | #else | ||||||||||||||||||||||||||||||||||
| 2707 | int result = ::WSAStringToAddressA(const_cast<char*>(src), | ||||||||||||||||||||||||||||||||||
| 2708 | af, 0, &address.base, &address_length); | ||||||||||||||||||||||||||||||||||
| 2709 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 2710 | #endif | ||||||||||||||||||||||||||||||||||
| 2711 | |||||||||||||||||||||||||||||||||||
| 2712 | if (af == BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||
| 2713 | { | ||||||||||||||||||||||||||||||||||
| 2714 | if (result != socket_error_retval) | ||||||||||||||||||||||||||||||||||
| 2715 | { | ||||||||||||||||||||||||||||||||||
| 2716 | memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type)); | ||||||||||||||||||||||||||||||||||
| 2717 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 2718 | } | ||||||||||||||||||||||||||||||||||
| 2719 | else if (strcmp(src, "255.255.255.255") == 0) | ||||||||||||||||||||||||||||||||||
| 2720 | { | ||||||||||||||||||||||||||||||||||
| 2721 | static_cast<in4_addr_type*>(dest)->s_addr = INADDR_NONE((in_addr_t) 0xffffffff); | ||||||||||||||||||||||||||||||||||
| 2722 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 2723 | } | ||||||||||||||||||||||||||||||||||
| 2724 | } | ||||||||||||||||||||||||||||||||||
| 2725 | else // AF_INET6 | ||||||||||||||||||||||||||||||||||
| 2726 | { | ||||||||||||||||||||||||||||||||||
| 2727 | if (result != socket_error_retval) | ||||||||||||||||||||||||||||||||||
| 2728 | { | ||||||||||||||||||||||||||||||||||
| 2729 | memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type)); | ||||||||||||||||||||||||||||||||||
| 2730 | if (scope_id) | ||||||||||||||||||||||||||||||||||
| 2731 | *scope_id = address.v6.sin6_scope_id; | ||||||||||||||||||||||||||||||||||
| 2732 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 2733 | } | ||||||||||||||||||||||||||||||||||
| 2734 | } | ||||||||||||||||||||||||||||||||||
| 2735 | |||||||||||||||||||||||||||||||||||
| 2736 | // Windows may not set an error code on failure. | ||||||||||||||||||||||||||||||||||
| 2737 | if (result == socket_error_retval && !ec) | ||||||||||||||||||||||||||||||||||
| 2738 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2739 | |||||||||||||||||||||||||||||||||||
| 2740 | if (result != socket_error_retval) | ||||||||||||||||||||||||||||||||||
| 2741 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 2742 | |||||||||||||||||||||||||||||||||||
| 2743 | return result == socket_error_retval ? -1 : 1; | ||||||||||||||||||||||||||||||||||
| 2744 | #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2745 | using namespace std; // For strchr, memcpy and atoi. | ||||||||||||||||||||||||||||||||||
| 2746 | |||||||||||||||||||||||||||||||||||
| 2747 | // On some platforms, inet_pton fails if an address string contains a scope | ||||||||||||||||||||||||||||||||||
| 2748 | // id. Detect and remove the scope id before passing the string to inet_pton. | ||||||||||||||||||||||||||||||||||
| 2749 | const bool is_v6 = (af == BOOST_ASIO_OS_DEF(AF_INET6)10); | ||||||||||||||||||||||||||||||||||
| 2750 | const char* if_name = is_v6 ? strchr(src, '%') : 0; | ||||||||||||||||||||||||||||||||||
| 2751 | char src_buf[max_addr_v6_str_len + 1]; | ||||||||||||||||||||||||||||||||||
| 2752 | const char* src_ptr = src; | ||||||||||||||||||||||||||||||||||
| 2753 | if (if_name != 0) | ||||||||||||||||||||||||||||||||||
| 2754 | { | ||||||||||||||||||||||||||||||||||
| 2755 | if (if_name - src > max_addr_v6_str_len) | ||||||||||||||||||||||||||||||||||
| 2756 | { | ||||||||||||||||||||||||||||||||||
| 2757 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2758 | return 0; | ||||||||||||||||||||||||||||||||||
| 2759 | } | ||||||||||||||||||||||||||||||||||
| 2760 | memcpy(src_buf, src, if_name - src); | ||||||||||||||||||||||||||||||||||
| 2761 | src_buf[if_name - src] = 0; | ||||||||||||||||||||||||||||||||||
| 2762 | src_ptr = src_buf; | ||||||||||||||||||||||||||||||||||
| 2763 | } | ||||||||||||||||||||||||||||||||||
| 2764 | |||||||||||||||||||||||||||||||||||
| 2765 | int result = ::inet_pton(af, src_ptr, dest); | ||||||||||||||||||||||||||||||||||
| 2766 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 2767 | if (result <= 0 && !ec) | ||||||||||||||||||||||||||||||||||
| 2768 | ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2769 | if (result > 0 && is_v6 && scope_id) | ||||||||||||||||||||||||||||||||||
| 2770 | { | ||||||||||||||||||||||||||||||||||
| 2771 | using namespace std; // For strchr and atoi. | ||||||||||||||||||||||||||||||||||
| 2772 | *scope_id = 0; | ||||||||||||||||||||||||||||||||||
| 2773 | if (if_name != 0) | ||||||||||||||||||||||||||||||||||
| 2774 | { | ||||||||||||||||||||||||||||||||||
| 2775 | in6_addr_type* ipv6_address = static_cast<in6_addr_type*>(dest); | ||||||||||||||||||||||||||||||||||
| 2776 | bool is_link_local = ((ipv6_address->s6_addr__in6_u.__u6_addr8[0] == 0xfe) | ||||||||||||||||||||||||||||||||||
| 2777 | && ((ipv6_address->s6_addr__in6_u.__u6_addr8[1] & 0xc0) == 0x80)); | ||||||||||||||||||||||||||||||||||
| 2778 | bool is_multicast_link_local = ((ipv6_address->s6_addr__in6_u.__u6_addr8[0] == 0xff) | ||||||||||||||||||||||||||||||||||
| 2779 | && ((ipv6_address->s6_addr__in6_u.__u6_addr8[1] & 0x0f) == 0x02)); | ||||||||||||||||||||||||||||||||||
| 2780 | if (is_link_local || is_multicast_link_local) | ||||||||||||||||||||||||||||||||||
| 2781 | *scope_id = if_nametoindex(if_name + 1); | ||||||||||||||||||||||||||||||||||
| 2782 | if (*scope_id == 0) | ||||||||||||||||||||||||||||||||||
| 2783 | *scope_id = atoi(if_name + 1); | ||||||||||||||||||||||||||||||||||
| 2784 | } | ||||||||||||||||||||||||||||||||||
| 2785 | } | ||||||||||||||||||||||||||||||||||
| 2786 | return result; | ||||||||||||||||||||||||||||||||||
| 2787 | #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2788 | } | ||||||||||||||||||||||||||||||||||
| 2789 | |||||||||||||||||||||||||||||||||||
| 2790 | int gethostname(char* name, int namelen, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2791 | { | ||||||||||||||||||||||||||||||||||
| 2792 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 2793 | try | ||||||||||||||||||||||||||||||||||
| 2794 | { | ||||||||||||||||||||||||||||||||||
| 2795 | using namespace Windows::Foundation::Collections; | ||||||||||||||||||||||||||||||||||
| 2796 | using namespace Windows::Networking; | ||||||||||||||||||||||||||||||||||
| 2797 | using namespace Windows::Networking::Connectivity; | ||||||||||||||||||||||||||||||||||
| 2798 | IVectorView<HostName^>^ hostnames = NetworkInformation::GetHostNames(); | ||||||||||||||||||||||||||||||||||
| 2799 | for (unsigned i = 0; i < hostnames->Size; ++i) | ||||||||||||||||||||||||||||||||||
| 2800 | { | ||||||||||||||||||||||||||||||||||
| 2801 | HostName^ hostname = hostnames->GetAt(i); | ||||||||||||||||||||||||||||||||||
| 2802 | if (hostname->Type == HostNameType::DomainName) | ||||||||||||||||||||||||||||||||||
| 2803 | { | ||||||||||||||||||||||||||||||||||
| 2804 | std::wstring_convert<std::codecvt_utf8<wchar_t>> converter; | ||||||||||||||||||||||||||||||||||
| 2805 | std::string raw_name = converter.to_bytes(hostname->RawName->Data()); | ||||||||||||||||||||||||||||||||||
| 2806 | if (namelen > 0 && raw_name.size() < static_cast<std::size_t>(namelen)) | ||||||||||||||||||||||||||||||||||
| 2807 | { | ||||||||||||||||||||||||||||||||||
| 2808 | strcpy_s(name, namelen, raw_name.c_str()); | ||||||||||||||||||||||||||||||||||
| 2809 | return 0; | ||||||||||||||||||||||||||||||||||
| 2810 | } | ||||||||||||||||||||||||||||||||||
| 2811 | } | ||||||||||||||||||||||||||||||||||
| 2812 | } | ||||||||||||||||||||||||||||||||||
| 2813 | return -1; | ||||||||||||||||||||||||||||||||||
| 2814 | } | ||||||||||||||||||||||||||||||||||
| 2815 | catch (Platform::Exception^ e) | ||||||||||||||||||||||||||||||||||
| 2816 | { | ||||||||||||||||||||||||||||||||||
| 2817 | ec = boost::system::error_code(e->HResult, | ||||||||||||||||||||||||||||||||||
| 2818 | boost::system::system_category()); | ||||||||||||||||||||||||||||||||||
| 2819 | return -1; | ||||||||||||||||||||||||||||||||||
| 2820 | } | ||||||||||||||||||||||||||||||||||
| 2821 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 2822 | int result = ::gethostname(name, namelen); | ||||||||||||||||||||||||||||||||||
| 2823 | get_last_error(ec, result != 0); | ||||||||||||||||||||||||||||||||||
| 2824 | return result; | ||||||||||||||||||||||||||||||||||
| 2825 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 2826 | } | ||||||||||||||||||||||||||||||||||
| 2827 | |||||||||||||||||||||||||||||||||||
| 2828 | #if !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 2829 | |||||||||||||||||||||||||||||||||||
| 2830 | #if !defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||
| 2831 | |||||||||||||||||||||||||||||||||||
| 2832 | // The following functions are only needed for emulation of getaddrinfo and | ||||||||||||||||||||||||||||||||||
| 2833 | // getnameinfo. | ||||||||||||||||||||||||||||||||||
| 2834 | |||||||||||||||||||||||||||||||||||
| 2835 | inline boost::system::error_code translate_netdb_error(int error) | ||||||||||||||||||||||||||||||||||
| 2836 | { | ||||||||||||||||||||||||||||||||||
| 2837 | switch (error) | ||||||||||||||||||||||||||||||||||
| 2838 | { | ||||||||||||||||||||||||||||||||||
| 2839 | case 0: | ||||||||||||||||||||||||||||||||||
| 2840 | return boost::system::error_code(); | ||||||||||||||||||||||||||||||||||
| 2841 | case HOST_NOT_FOUND1: | ||||||||||||||||||||||||||||||||||
| 2842 | return boost::asio::error::host_not_found; | ||||||||||||||||||||||||||||||||||
| 2843 | case TRY_AGAIN2: | ||||||||||||||||||||||||||||||||||
| 2844 | return boost::asio::error::host_not_found_try_again; | ||||||||||||||||||||||||||||||||||
| 2845 | case NO_RECOVERY3: | ||||||||||||||||||||||||||||||||||
| 2846 | return boost::asio::error::no_recovery; | ||||||||||||||||||||||||||||||||||
| 2847 | case NO_DATA4: | ||||||||||||||||||||||||||||||||||
| 2848 | return boost::asio::error::no_data; | ||||||||||||||||||||||||||||||||||
| 2849 | default: | ||||||||||||||||||||||||||||||||||
| 2850 | BOOST_ASIO_ASSERT(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); | ||||||||||||||||||||||||||||||||||
| 2851 | return boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 2852 | } | ||||||||||||||||||||||||||||||||||
| 2853 | } | ||||||||||||||||||||||||||||||||||
| 2854 | |||||||||||||||||||||||||||||||||||
| 2855 | inline hostent* gethostbyaddr(const char* addr, int length, int af, | ||||||||||||||||||||||||||||||||||
| 2856 | hostent* result, char* buffer, int buflength, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2857 | { | ||||||||||||||||||||||||||||||||||
| 2858 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2859 | (void)(buffer); | ||||||||||||||||||||||||||||||||||
| 2860 | (void)(buflength); | ||||||||||||||||||||||||||||||||||
| 2861 | hostent* retval = ::gethostbyaddr(addr, length, af); | ||||||||||||||||||||||||||||||||||
| 2862 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||
| 2863 | if (!retval) | ||||||||||||||||||||||||||||||||||
| 2864 | return 0; | ||||||||||||||||||||||||||||||||||
| 2865 | *result = *retval; | ||||||||||||||||||||||||||||||||||
| 2866 | return retval; | ||||||||||||||||||||||||||||||||||
| 2867 | #elif defined(__sun) || defined(__QNX__) | ||||||||||||||||||||||||||||||||||
| 2868 | int error = 0; | ||||||||||||||||||||||||||||||||||
| 2869 | hostent* retval = ::gethostbyaddr_r(addr, length, | ||||||||||||||||||||||||||||||||||
| 2870 | af, result, buffer, buflength, &error); | ||||||||||||||||||||||||||||||||||
| 2871 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||
| 2872 | if (error) | ||||||||||||||||||||||||||||||||||
| 2873 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||
| 2874 | return retval; | ||||||||||||||||||||||||||||||||||
| 2875 | #elif defined(__MACH__) && defined(__APPLE__) | ||||||||||||||||||||||||||||||||||
| 2876 | (void)(buffer); | ||||||||||||||||||||||||||||||||||
| 2877 | (void)(buflength); | ||||||||||||||||||||||||||||||||||
| 2878 | int error = 0; | ||||||||||||||||||||||||||||||||||
| 2879 | hostent* retval = ::getipnodebyaddr(addr, length, af, &error); | ||||||||||||||||||||||||||||||||||
| 2880 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||
| 2881 | if (error) | ||||||||||||||||||||||||||||||||||
| 2882 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||
| 2883 | if (!retval) | ||||||||||||||||||||||||||||||||||
| 2884 | return 0; | ||||||||||||||||||||||||||||||||||
| 2885 | *result = *retval; | ||||||||||||||||||||||||||||||||||
| 2886 | return retval; | ||||||||||||||||||||||||||||||||||
| 2887 | #else | ||||||||||||||||||||||||||||||||||
| 2888 | hostent* retval = 0; | ||||||||||||||||||||||||||||||||||
| 2889 | int error = 0; | ||||||||||||||||||||||||||||||||||
| 2890 | clear_last_error(); | ||||||||||||||||||||||||||||||||||
| 2891 | ::gethostbyaddr_r(addr, length, af, result, | ||||||||||||||||||||||||||||||||||
| 2892 | buffer, buflength, &retval, &error); | ||||||||||||||||||||||||||||||||||
| 2893 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 2894 | if (error) | ||||||||||||||||||||||||||||||||||
| 2895 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||
| 2896 | return retval; | ||||||||||||||||||||||||||||||||||
| 2897 | #endif | ||||||||||||||||||||||||||||||||||
| 2898 | } | ||||||||||||||||||||||||||||||||||
| 2899 | |||||||||||||||||||||||||||||||||||
| 2900 | inline hostent* gethostbyname(const char* name, int af, struct hostent* result, | ||||||||||||||||||||||||||||||||||
| 2901 | char* buffer, int buflength, int ai_flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 2902 | { | ||||||||||||||||||||||||||||||||||
| 2903 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 2904 | (void)(buffer); | ||||||||||||||||||||||||||||||||||
| 2905 | (void)(buflength); | ||||||||||||||||||||||||||||||||||
| 2906 | (void)(ai_flags); | ||||||||||||||||||||||||||||||||||
| 2907 | if (af != BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||
| 2908 | { | ||||||||||||||||||||||||||||||||||
| 2909 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||
| 2910 | return 0; | ||||||||||||||||||||||||||||||||||
| 2911 | } | ||||||||||||||||||||||||||||||||||
| 2912 | hostent* retval = ::gethostbyname(name); | ||||||||||||||||||||||||||||||||||
| 2913 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||
| 2914 | if (!retval) | ||||||||||||||||||||||||||||||||||
| 2915 | return 0; | ||||||||||||||||||||||||||||||||||
| 2916 | *result = *retval; | ||||||||||||||||||||||||||||||||||
| 2917 | return result; | ||||||||||||||||||||||||||||||||||
| 2918 | #elif defined(__sun) || defined(__QNX__) | ||||||||||||||||||||||||||||||||||
| 2919 | (void)(ai_flags); | ||||||||||||||||||||||||||||||||||
| 2920 | if (af != BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||
| 2921 | { | ||||||||||||||||||||||||||||||||||
| 2922 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||
| 2923 | return 0; | ||||||||||||||||||||||||||||||||||
| 2924 | } | ||||||||||||||||||||||||||||||||||
| 2925 | int error = 0; | ||||||||||||||||||||||||||||||||||
| 2926 | hostent* retval = ::gethostbyname_r(name, result, buffer, buflength, &error); | ||||||||||||||||||||||||||||||||||
| 2927 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||
| 2928 | if (error) | ||||||||||||||||||||||||||||||||||
| 2929 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||
| 2930 | return retval; | ||||||||||||||||||||||||||||||||||
| 2931 | #elif defined(__MACH__) && defined(__APPLE__) | ||||||||||||||||||||||||||||||||||
| 2932 | (void)(buffer); | ||||||||||||||||||||||||||||||||||
| 2933 | (void)(buflength); | ||||||||||||||||||||||||||||||||||
| 2934 | int error = 0; | ||||||||||||||||||||||||||||||||||
| 2935 | hostent* retval = ::getipnodebyname(name, af, ai_flags, &error); | ||||||||||||||||||||||||||||||||||
| 2936 | get_last_error(ec, !retval); | ||||||||||||||||||||||||||||||||||
| 2937 | if (error) | ||||||||||||||||||||||||||||||||||
| 2938 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||
| 2939 | if (!retval) | ||||||||||||||||||||||||||||||||||
| 2940 | return 0; | ||||||||||||||||||||||||||||||||||
| 2941 | *result = *retval; | ||||||||||||||||||||||||||||||||||
| 2942 | return retval; | ||||||||||||||||||||||||||||||||||
| 2943 | #else | ||||||||||||||||||||||||||||||||||
| 2944 | (void)(ai_flags); | ||||||||||||||||||||||||||||||||||
| 2945 | if (af != BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||
| 2946 | { | ||||||||||||||||||||||||||||||||||
| 2947 | ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||
| 2948 | return 0; | ||||||||||||||||||||||||||||||||||
| 2949 | } | ||||||||||||||||||||||||||||||||||
| 2950 | hostent* retval = 0; | ||||||||||||||||||||||||||||||||||
| 2951 | int error = 0; | ||||||||||||||||||||||||||||||||||
| 2952 | clear_last_error(); | ||||||||||||||||||||||||||||||||||
| 2953 | ::gethostbyname_r(name, result, buffer, buflength, &retval, &error); | ||||||||||||||||||||||||||||||||||
| 2954 | get_last_error(ec, true); | ||||||||||||||||||||||||||||||||||
| 2955 | if (error) | ||||||||||||||||||||||||||||||||||
| 2956 | ec = translate_netdb_error(error); | ||||||||||||||||||||||||||||||||||
| 2957 | return retval; | ||||||||||||||||||||||||||||||||||
| 2958 | #endif | ||||||||||||||||||||||||||||||||||
| 2959 | } | ||||||||||||||||||||||||||||||||||
| 2960 | |||||||||||||||||||||||||||||||||||
| 2961 | inline void freehostent(hostent* h) | ||||||||||||||||||||||||||||||||||
| 2962 | { | ||||||||||||||||||||||||||||||||||
| 2963 | #if defined(__MACH__) && defined(__APPLE__) | ||||||||||||||||||||||||||||||||||
| 2964 | if (h) | ||||||||||||||||||||||||||||||||||
| 2965 | ::freehostent(h); | ||||||||||||||||||||||||||||||||||
| 2966 | #else | ||||||||||||||||||||||||||||||||||
| 2967 | (void)(h); | ||||||||||||||||||||||||||||||||||
| 2968 | #endif | ||||||||||||||||||||||||||||||||||
| 2969 | } | ||||||||||||||||||||||||||||||||||
| 2970 | |||||||||||||||||||||||||||||||||||
| 2971 | // Emulation of getaddrinfo based on implementation in: | ||||||||||||||||||||||||||||||||||
| 2972 | // Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998. | ||||||||||||||||||||||||||||||||||
| 2973 | |||||||||||||||||||||||||||||||||||
| 2974 | struct gai_search | ||||||||||||||||||||||||||||||||||
| 2975 | { | ||||||||||||||||||||||||||||||||||
| 2976 | const char* host; | ||||||||||||||||||||||||||||||||||
| 2977 | int family; | ||||||||||||||||||||||||||||||||||
| 2978 | }; | ||||||||||||||||||||||||||||||||||
| 2979 | |||||||||||||||||||||||||||||||||||
| 2980 | inline int gai_nsearch(const char* host, | ||||||||||||||||||||||||||||||||||
| 2981 | const addrinfo_type* hints, gai_search (&search)[2]) | ||||||||||||||||||||||||||||||||||
| 2982 | { | ||||||||||||||||||||||||||||||||||
| 2983 | int search_count = 0; | ||||||||||||||||||||||||||||||||||
| 2984 | if (host == 0 || host[0] == '\0') | ||||||||||||||||||||||||||||||||||
| 2985 | { | ||||||||||||||||||||||||||||||||||
| 2986 | if (hints->ai_flags & AI_PASSIVE0x0001) | ||||||||||||||||||||||||||||||||||
| 2987 | { | ||||||||||||||||||||||||||||||||||
| 2988 | // No host and AI_PASSIVE implies wildcard bind. | ||||||||||||||||||||||||||||||||||
| 2989 | switch (hints->ai_family) | ||||||||||||||||||||||||||||||||||
| 2990 | { | ||||||||||||||||||||||||||||||||||
| 2991 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||
| 2992 | search[search_count].host = "0.0.0.0"; | ||||||||||||||||||||||||||||||||||
| 2993 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||
| 2994 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 2995 | break; | ||||||||||||||||||||||||||||||||||
| 2996 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||
| 2997 | search[search_count].host = "0::0"; | ||||||||||||||||||||||||||||||||||
| 2998 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||
| 2999 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 3000 | break; | ||||||||||||||||||||||||||||||||||
| 3001 | case BOOST_ASIO_OS_DEF(AF_UNSPEC)0: | ||||||||||||||||||||||||||||||||||
| 3002 | search[search_count].host = "0::0"; | ||||||||||||||||||||||||||||||||||
| 3003 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||
| 3004 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 3005 | search[search_count].host = "0.0.0.0"; | ||||||||||||||||||||||||||||||||||
| 3006 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||
| 3007 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 3008 | break; | ||||||||||||||||||||||||||||||||||
| 3009 | default: | ||||||||||||||||||||||||||||||||||
| 3010 | break; | ||||||||||||||||||||||||||||||||||
| 3011 | } | ||||||||||||||||||||||||||||||||||
| 3012 | } | ||||||||||||||||||||||||||||||||||
| 3013 | else | ||||||||||||||||||||||||||||||||||
| 3014 | { | ||||||||||||||||||||||||||||||||||
| 3015 | // No host and not AI_PASSIVE means connect to local host. | ||||||||||||||||||||||||||||||||||
| 3016 | switch (hints->ai_family) | ||||||||||||||||||||||||||||||||||
| 3017 | { | ||||||||||||||||||||||||||||||||||
| 3018 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||
| 3019 | search[search_count].host = "localhost"; | ||||||||||||||||||||||||||||||||||
| 3020 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||
| 3021 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 3022 | break; | ||||||||||||||||||||||||||||||||||
| 3023 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||
| 3024 | search[search_count].host = "localhost"; | ||||||||||||||||||||||||||||||||||
| 3025 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||
| 3026 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 3027 | break; | ||||||||||||||||||||||||||||||||||
| 3028 | case BOOST_ASIO_OS_DEF(AF_UNSPEC)0: | ||||||||||||||||||||||||||||||||||
| 3029 | search[search_count].host = "localhost"; | ||||||||||||||||||||||||||||||||||
| 3030 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||
| 3031 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 3032 | search[search_count].host = "localhost"; | ||||||||||||||||||||||||||||||||||
| 3033 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||
| 3034 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 3035 | break; | ||||||||||||||||||||||||||||||||||
| 3036 | default: | ||||||||||||||||||||||||||||||||||
| 3037 | break; | ||||||||||||||||||||||||||||||||||
| 3038 | } | ||||||||||||||||||||||||||||||||||
| 3039 | } | ||||||||||||||||||||||||||||||||||
| 3040 | } | ||||||||||||||||||||||||||||||||||
| 3041 | else | ||||||||||||||||||||||||||||||||||
| 3042 | { | ||||||||||||||||||||||||||||||||||
| 3043 | // Host is specified. | ||||||||||||||||||||||||||||||||||
| 3044 | switch (hints->ai_family) | ||||||||||||||||||||||||||||||||||
| 3045 | { | ||||||||||||||||||||||||||||||||||
| 3046 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||
| 3047 | search[search_count].host = host; | ||||||||||||||||||||||||||||||||||
| 3048 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||
| 3049 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 3050 | break; | ||||||||||||||||||||||||||||||||||
| 3051 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||
| 3052 | search[search_count].host = host; | ||||||||||||||||||||||||||||||||||
| 3053 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||
| 3054 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 3055 | break; | ||||||||||||||||||||||||||||||||||
| 3056 | case BOOST_ASIO_OS_DEF(AF_UNSPEC)0: | ||||||||||||||||||||||||||||||||||
| 3057 | search[search_count].host = host; | ||||||||||||||||||||||||||||||||||
| 3058 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||
| 3059 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 3060 | search[search_count].host = host; | ||||||||||||||||||||||||||||||||||
| 3061 | search[search_count].family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||
| 3062 | ++search_count; | ||||||||||||||||||||||||||||||||||
| 3063 | break; | ||||||||||||||||||||||||||||||||||
| 3064 | default: | ||||||||||||||||||||||||||||||||||
| 3065 | break; | ||||||||||||||||||||||||||||||||||
| 3066 | } | ||||||||||||||||||||||||||||||||||
| 3067 | } | ||||||||||||||||||||||||||||||||||
| 3068 | return search_count; | ||||||||||||||||||||||||||||||||||
| 3069 | } | ||||||||||||||||||||||||||||||||||
| 3070 | |||||||||||||||||||||||||||||||||||
| 3071 | template <typename T> | ||||||||||||||||||||||||||||||||||
| 3072 | inline T* gai_alloc(std::size_t size = sizeof(T)) | ||||||||||||||||||||||||||||||||||
| 3073 | { | ||||||||||||||||||||||||||||||||||
| 3074 | using namespace std; | ||||||||||||||||||||||||||||||||||
| 3075 | T* p = static_cast<T*>(::operator new(size, std::nothrow)); | ||||||||||||||||||||||||||||||||||
| 3076 | if (p) | ||||||||||||||||||||||||||||||||||
| 3077 | memset(p, 0, size); | ||||||||||||||||||||||||||||||||||
| 3078 | return p; | ||||||||||||||||||||||||||||||||||
| 3079 | } | ||||||||||||||||||||||||||||||||||
| 3080 | |||||||||||||||||||||||||||||||||||
| 3081 | inline void gai_free(void* p) | ||||||||||||||||||||||||||||||||||
| 3082 | { | ||||||||||||||||||||||||||||||||||
| 3083 | ::operator delete(p); | ||||||||||||||||||||||||||||||||||
| 3084 | } | ||||||||||||||||||||||||||||||||||
| 3085 | |||||||||||||||||||||||||||||||||||
| 3086 | inline void gai_strcpy(char* target, const char* source, std::size_t max_size) | ||||||||||||||||||||||||||||||||||
| 3087 | { | ||||||||||||||||||||||||||||||||||
| 3088 | using namespace std; | ||||||||||||||||||||||||||||||||||
| 3089 | #if defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
| 3090 | strcpy_s(target, max_size, source); | ||||||||||||||||||||||||||||||||||
| 3091 | #else // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
| 3092 | *target = 0; | ||||||||||||||||||||||||||||||||||
| 3093 | if (max_size > 0) | ||||||||||||||||||||||||||||||||||
| 3094 | strncat(target, source, max_size - 1); | ||||||||||||||||||||||||||||||||||
| 3095 | #endif // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
| 3096 | } | ||||||||||||||||||||||||||||||||||
| 3097 | |||||||||||||||||||||||||||||||||||
| 3098 | enum { gai_clone_flag = 1 << 30 }; | ||||||||||||||||||||||||||||||||||
| 3099 | |||||||||||||||||||||||||||||||||||
| 3100 | inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints, | ||||||||||||||||||||||||||||||||||
| 3101 | const void* addr, int family) | ||||||||||||||||||||||||||||||||||
| 3102 | { | ||||||||||||||||||||||||||||||||||
| 3103 | using namespace std; | ||||||||||||||||||||||||||||||||||
| 3104 | |||||||||||||||||||||||||||||||||||
| 3105 | addrinfo_type* ai = gai_alloc<addrinfo_type>(); | ||||||||||||||||||||||||||||||||||
| 3106 | if (ai == 0) | ||||||||||||||||||||||||||||||||||
| 3107 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||
| 3108 | |||||||||||||||||||||||||||||||||||
| 3109 | ai->ai_next = 0; | ||||||||||||||||||||||||||||||||||
| 3110 | **next = ai; | ||||||||||||||||||||||||||||||||||
| 3111 | *next = &ai->ai_next; | ||||||||||||||||||||||||||||||||||
| 3112 | |||||||||||||||||||||||||||||||||||
| 3113 | ai->ai_canonname = 0; | ||||||||||||||||||||||||||||||||||
| 3114 | ai->ai_socktype = hints->ai_socktype; | ||||||||||||||||||||||||||||||||||
| 3115 | if (ai->ai_socktype == 0) | ||||||||||||||||||||||||||||||||||
| 3116 | ai->ai_flags |= gai_clone_flag; | ||||||||||||||||||||||||||||||||||
| 3117 | ai->ai_protocol = hints->ai_protocol; | ||||||||||||||||||||||||||||||||||
| 3118 | ai->ai_family = family; | ||||||||||||||||||||||||||||||||||
| 3119 | |||||||||||||||||||||||||||||||||||
| 3120 | switch (ai->ai_family) | ||||||||||||||||||||||||||||||||||
| 3121 | { | ||||||||||||||||||||||||||||||||||
| 3122 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||
| 3123 | { | ||||||||||||||||||||||||||||||||||
| 3124 | sockaddr_in4_type* sinptr = gai_alloc<sockaddr_in4_type>(); | ||||||||||||||||||||||||||||||||||
| 3125 | if (sinptr == 0) | ||||||||||||||||||||||||||||||||||
| 3126 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||
| 3127 | sinptr->sin_family = BOOST_ASIO_OS_DEF(AF_INET)2; | ||||||||||||||||||||||||||||||||||
| 3128 | memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type)); | ||||||||||||||||||||||||||||||||||
| 3129 | ai->ai_addr = reinterpret_cast<sockaddr*>(sinptr); | ||||||||||||||||||||||||||||||||||
| 3130 | ai->ai_addrlen = sizeof(sockaddr_in4_type); | ||||||||||||||||||||||||||||||||||
| 3131 | break; | ||||||||||||||||||||||||||||||||||
| 3132 | } | ||||||||||||||||||||||||||||||||||
| 3133 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||
| 3134 | { | ||||||||||||||||||||||||||||||||||
| 3135 | sockaddr_in6_type* sin6ptr = gai_alloc<sockaddr_in6_type>(); | ||||||||||||||||||||||||||||||||||
| 3136 | if (sin6ptr == 0) | ||||||||||||||||||||||||||||||||||
| 3137 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||
| 3138 | sin6ptr->sin6_family = BOOST_ASIO_OS_DEF(AF_INET6)10; | ||||||||||||||||||||||||||||||||||
| 3139 | memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type)); | ||||||||||||||||||||||||||||||||||
| 3140 | ai->ai_addr = reinterpret_cast<sockaddr*>(sin6ptr); | ||||||||||||||||||||||||||||||||||
| 3141 | ai->ai_addrlen = sizeof(sockaddr_in6_type); | ||||||||||||||||||||||||||||||||||
| 3142 | break; | ||||||||||||||||||||||||||||||||||
| 3143 | } | ||||||||||||||||||||||||||||||||||
| 3144 | default: | ||||||||||||||||||||||||||||||||||
| 3145 | break; | ||||||||||||||||||||||||||||||||||
| 3146 | } | ||||||||||||||||||||||||||||||||||
| 3147 | |||||||||||||||||||||||||||||||||||
| 3148 | return 0; | ||||||||||||||||||||||||||||||||||
| 3149 | } | ||||||||||||||||||||||||||||||||||
| 3150 | |||||||||||||||||||||||||||||||||||
| 3151 | inline addrinfo_type* gai_clone(addrinfo_type* ai) | ||||||||||||||||||||||||||||||||||
| 3152 | { | ||||||||||||||||||||||||||||||||||
| 3153 | using namespace std; | ||||||||||||||||||||||||||||||||||
| 3154 | |||||||||||||||||||||||||||||||||||
| 3155 | addrinfo_type* new_ai = gai_alloc<addrinfo_type>(); | ||||||||||||||||||||||||||||||||||
| 3156 | if (new_ai == 0) | ||||||||||||||||||||||||||||||||||
| 3157 | return new_ai; | ||||||||||||||||||||||||||||||||||
| 3158 | |||||||||||||||||||||||||||||||||||
| 3159 | new_ai->ai_next = ai->ai_next; | ||||||||||||||||||||||||||||||||||
| 3160 | ai->ai_next = new_ai; | ||||||||||||||||||||||||||||||||||
| 3161 | |||||||||||||||||||||||||||||||||||
| 3162 | new_ai->ai_flags = 0; | ||||||||||||||||||||||||||||||||||
| 3163 | new_ai->ai_family = ai->ai_family; | ||||||||||||||||||||||||||||||||||
| 3164 | new_ai->ai_socktype = ai->ai_socktype; | ||||||||||||||||||||||||||||||||||
| 3165 | new_ai->ai_protocol = ai->ai_protocol; | ||||||||||||||||||||||||||||||||||
| 3166 | new_ai->ai_canonname = 0; | ||||||||||||||||||||||||||||||||||
| 3167 | new_ai->ai_addrlen = ai->ai_addrlen; | ||||||||||||||||||||||||||||||||||
| 3168 | new_ai->ai_addr = gai_alloc<sockaddr>(ai->ai_addrlen); | ||||||||||||||||||||||||||||||||||
| 3169 | memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen); | ||||||||||||||||||||||||||||||||||
| 3170 | |||||||||||||||||||||||||||||||||||
| 3171 | return new_ai; | ||||||||||||||||||||||||||||||||||
| 3172 | } | ||||||||||||||||||||||||||||||||||
| 3173 | |||||||||||||||||||||||||||||||||||
| 3174 | inline int gai_port(addrinfo_type* aihead, int port, int socktype) | ||||||||||||||||||||||||||||||||||
| 3175 | { | ||||||||||||||||||||||||||||||||||
| 3176 | int num_found = 0; | ||||||||||||||||||||||||||||||||||
| 3177 | |||||||||||||||||||||||||||||||||||
| 3178 | for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next) | ||||||||||||||||||||||||||||||||||
| 3179 | { | ||||||||||||||||||||||||||||||||||
| 3180 | if (ai->ai_flags & gai_clone_flag) | ||||||||||||||||||||||||||||||||||
| 3181 | { | ||||||||||||||||||||||||||||||||||
| 3182 | if (ai->ai_socktype != 0) | ||||||||||||||||||||||||||||||||||
| 3183 | { | ||||||||||||||||||||||||||||||||||
| 3184 | ai = gai_clone(ai); | ||||||||||||||||||||||||||||||||||
| 3185 | if (ai == 0) | ||||||||||||||||||||||||||||||||||
| 3186 | return -1; | ||||||||||||||||||||||||||||||||||
| 3187 | // ai now points to newly cloned entry. | ||||||||||||||||||||||||||||||||||
| 3188 | } | ||||||||||||||||||||||||||||||||||
| 3189 | } | ||||||||||||||||||||||||||||||||||
| 3190 | else if (ai->ai_socktype != socktype) | ||||||||||||||||||||||||||||||||||
| 3191 | { | ||||||||||||||||||||||||||||||||||
| 3192 | // Ignore if mismatch on socket type. | ||||||||||||||||||||||||||||||||||
| 3193 | continue; | ||||||||||||||||||||||||||||||||||
| 3194 | } | ||||||||||||||||||||||||||||||||||
| 3195 | |||||||||||||||||||||||||||||||||||
| 3196 | ai->ai_socktype = socktype; | ||||||||||||||||||||||||||||||||||
| 3197 | |||||||||||||||||||||||||||||||||||
| 3198 | switch (ai->ai_family) | ||||||||||||||||||||||||||||||||||
| 3199 | { | ||||||||||||||||||||||||||||||||||
| 3200 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||
| 3201 | { | ||||||||||||||||||||||||||||||||||
| 3202 | sockaddr_in4_type* sinptr = | ||||||||||||||||||||||||||||||||||
| 3203 | reinterpret_cast<sockaddr_in4_type*>(ai->ai_addr); | ||||||||||||||||||||||||||||||||||
| 3204 | sinptr->sin_port = port; | ||||||||||||||||||||||||||||||||||
| 3205 | ++num_found; | ||||||||||||||||||||||||||||||||||
| 3206 | break; | ||||||||||||||||||||||||||||||||||
| 3207 | } | ||||||||||||||||||||||||||||||||||
| 3208 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||
| 3209 | { | ||||||||||||||||||||||||||||||||||
| 3210 | sockaddr_in6_type* sin6ptr = | ||||||||||||||||||||||||||||||||||
| 3211 | reinterpret_cast<sockaddr_in6_type*>(ai->ai_addr); | ||||||||||||||||||||||||||||||||||
| 3212 | sin6ptr->sin6_port = port; | ||||||||||||||||||||||||||||||||||
| 3213 | ++num_found; | ||||||||||||||||||||||||||||||||||
| 3214 | break; | ||||||||||||||||||||||||||||||||||
| 3215 | } | ||||||||||||||||||||||||||||||||||
| 3216 | default: | ||||||||||||||||||||||||||||||||||
| 3217 | break; | ||||||||||||||||||||||||||||||||||
| 3218 | } | ||||||||||||||||||||||||||||||||||
| 3219 | } | ||||||||||||||||||||||||||||||||||
| 3220 | |||||||||||||||||||||||||||||||||||
| 3221 | return num_found; | ||||||||||||||||||||||||||||||||||
| 3222 | } | ||||||||||||||||||||||||||||||||||
| 3223 | |||||||||||||||||||||||||||||||||||
| 3224 | inline int gai_serv(addrinfo_type* aihead, | ||||||||||||||||||||||||||||||||||
| 3225 | const addrinfo_type* hints, const char* serv) | ||||||||||||||||||||||||||||||||||
| 3226 | { | ||||||||||||||||||||||||||||||||||
| 3227 | using namespace std; | ||||||||||||||||||||||||||||||||||
| 3228 | |||||||||||||||||||||||||||||||||||
| 3229 | int num_found = 0; | ||||||||||||||||||||||||||||||||||
| 3230 | |||||||||||||||||||||||||||||||||||
| 3231 | if ( | ||||||||||||||||||||||||||||||||||
| 3232 | #if defined(AI_NUMERICSERV0x0400) | ||||||||||||||||||||||||||||||||||
| 3233 | (hints->ai_flags & AI_NUMERICSERV0x0400) || | ||||||||||||||||||||||||||||||||||
| 3234 | #endif | ||||||||||||||||||||||||||||||||||
| 3235 | isdigit(static_cast<unsigned char>(serv[0]))) | ||||||||||||||||||||||||||||||||||
| 3236 | { | ||||||||||||||||||||||||||||||||||
| 3237 | int port = htons(atoi(serv))__bswap_16 (atoi(serv)); | ||||||||||||||||||||||||||||||||||
| 3238 | if (hints->ai_socktype) | ||||||||||||||||||||||||||||||||||
| 3239 | { | ||||||||||||||||||||||||||||||||||
| 3240 | // Caller specifies socket type. | ||||||||||||||||||||||||||||||||||
| 3241 | int rc = gai_port(aihead, port, hints->ai_socktype); | ||||||||||||||||||||||||||||||||||
| 3242 | if (rc < 0) | ||||||||||||||||||||||||||||||||||
| 3243 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||
| 3244 | num_found += rc; | ||||||||||||||||||||||||||||||||||
| 3245 | } | ||||||||||||||||||||||||||||||||||
| 3246 | else | ||||||||||||||||||||||||||||||||||
| 3247 | { | ||||||||||||||||||||||||||||||||||
| 3248 | // Caller does not specify socket type. | ||||||||||||||||||||||||||||||||||
| 3249 | int rc = gai_port(aihead, port, SOCK_STREAMSOCK_STREAM); | ||||||||||||||||||||||||||||||||||
| 3250 | if (rc < 0) | ||||||||||||||||||||||||||||||||||
| 3251 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||
| 3252 | num_found += rc; | ||||||||||||||||||||||||||||||||||
| 3253 | rc = gai_port(aihead, port, SOCK_DGRAMSOCK_DGRAM); | ||||||||||||||||||||||||||||||||||
| 3254 | if (rc < 0) | ||||||||||||||||||||||||||||||||||
| 3255 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||
| 3256 | num_found += rc; | ||||||||||||||||||||||||||||||||||
| 3257 | } | ||||||||||||||||||||||||||||||||||
| 3258 | } | ||||||||||||||||||||||||||||||||||
| 3259 | else | ||||||||||||||||||||||||||||||||||
| 3260 | { | ||||||||||||||||||||||||||||||||||
| 3261 | // Try service name with TCP first, then UDP. | ||||||||||||||||||||||||||||||||||
| 3262 | if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAMSOCK_STREAM) | ||||||||||||||||||||||||||||||||||
| 3263 | { | ||||||||||||||||||||||||||||||||||
| 3264 | servent* sptr = getservbyname(serv, "tcp"); | ||||||||||||||||||||||||||||||||||
| 3265 | if (sptr != 0) | ||||||||||||||||||||||||||||||||||
| 3266 | { | ||||||||||||||||||||||||||||||||||
| 3267 | int rc = gai_port(aihead, sptr->s_port, SOCK_STREAMSOCK_STREAM); | ||||||||||||||||||||||||||||||||||
| 3268 | if (rc < 0) | ||||||||||||||||||||||||||||||||||
| 3269 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||
| 3270 | num_found += rc; | ||||||||||||||||||||||||||||||||||
| 3271 | } | ||||||||||||||||||||||||||||||||||
| 3272 | } | ||||||||||||||||||||||||||||||||||
| 3273 | if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAMSOCK_DGRAM) | ||||||||||||||||||||||||||||||||||
| 3274 | { | ||||||||||||||||||||||||||||||||||
| 3275 | servent* sptr = getservbyname(serv, "udp"); | ||||||||||||||||||||||||||||||||||
| 3276 | if (sptr != 0) | ||||||||||||||||||||||||||||||||||
| 3277 | { | ||||||||||||||||||||||||||||||||||
| 3278 | int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAMSOCK_DGRAM); | ||||||||||||||||||||||||||||||||||
| 3279 | if (rc < 0) | ||||||||||||||||||||||||||||||||||
| 3280 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||
| 3281 | num_found += rc; | ||||||||||||||||||||||||||||||||||
| 3282 | } | ||||||||||||||||||||||||||||||||||
| 3283 | } | ||||||||||||||||||||||||||||||||||
| 3284 | } | ||||||||||||||||||||||||||||||||||
| 3285 | |||||||||||||||||||||||||||||||||||
| 3286 | if (num_found == 0) | ||||||||||||||||||||||||||||||||||
| 3287 | { | ||||||||||||||||||||||||||||||||||
| 3288 | if (hints->ai_socktype == 0) | ||||||||||||||||||||||||||||||||||
| 3289 | { | ||||||||||||||||||||||||||||||||||
| 3290 | // All calls to getservbyname() failed. | ||||||||||||||||||||||||||||||||||
| 3291 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||
| 3292 | } | ||||||||||||||||||||||||||||||||||
| 3293 | else | ||||||||||||||||||||||||||||||||||
| 3294 | { | ||||||||||||||||||||||||||||||||||
| 3295 | // Service not supported for socket type. | ||||||||||||||||||||||||||||||||||
| 3296 | return EAI_SERVICE-8; | ||||||||||||||||||||||||||||||||||
| 3297 | } | ||||||||||||||||||||||||||||||||||
| 3298 | } | ||||||||||||||||||||||||||||||||||
| 3299 | |||||||||||||||||||||||||||||||||||
| 3300 | return 0; | ||||||||||||||||||||||||||||||||||
| 3301 | } | ||||||||||||||||||||||||||||||||||
| 3302 | |||||||||||||||||||||||||||||||||||
| 3303 | inline int gai_echeck(const char* host, const char* service, | ||||||||||||||||||||||||||||||||||
| 3304 | int flags, int family, int socktype, int protocol) | ||||||||||||||||||||||||||||||||||
| 3305 | { | ||||||||||||||||||||||||||||||||||
| 3306 | (void)(flags); | ||||||||||||||||||||||||||||||||||
| 3307 | (void)(protocol); | ||||||||||||||||||||||||||||||||||
| 3308 | |||||||||||||||||||||||||||||||||||
| 3309 | // Host or service must be specified. | ||||||||||||||||||||||||||||||||||
| 3310 | if (host == 0 || host[0] == '\0') | ||||||||||||||||||||||||||||||||||
| 3311 | if (service == 0 || service[0] == '\0') | ||||||||||||||||||||||||||||||||||
| 3312 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||
| 3313 | |||||||||||||||||||||||||||||||||||
| 3314 | // Check combination of family and socket type. | ||||||||||||||||||||||||||||||||||
| 3315 | switch (family) | ||||||||||||||||||||||||||||||||||
| 3316 | { | ||||||||||||||||||||||||||||||||||
| 3317 | case BOOST_ASIO_OS_DEF(AF_UNSPEC)0: | ||||||||||||||||||||||||||||||||||
| 3318 | break; | ||||||||||||||||||||||||||||||||||
| 3319 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||
| 3320 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||
| 3321 | if (service != 0 && service[0] != '\0') | ||||||||||||||||||||||||||||||||||
| 3322 | if (socktype != 0 && socktype != SOCK_STREAMSOCK_STREAM && socktype != SOCK_DGRAMSOCK_DGRAM) | ||||||||||||||||||||||||||||||||||
| 3323 | return EAI_SOCKTYPE-7; | ||||||||||||||||||||||||||||||||||
| 3324 | break; | ||||||||||||||||||||||||||||||||||
| 3325 | default: | ||||||||||||||||||||||||||||||||||
| 3326 | return EAI_FAMILY-6; | ||||||||||||||||||||||||||||||||||
| 3327 | } | ||||||||||||||||||||||||||||||||||
| 3328 | |||||||||||||||||||||||||||||||||||
| 3329 | return 0; | ||||||||||||||||||||||||||||||||||
| 3330 | } | ||||||||||||||||||||||||||||||||||
| 3331 | |||||||||||||||||||||||||||||||||||
| 3332 | inline void freeaddrinfo_emulation(addrinfo_type* aihead) | ||||||||||||||||||||||||||||||||||
| 3333 | { | ||||||||||||||||||||||||||||||||||
| 3334 | addrinfo_type* ai = aihead; | ||||||||||||||||||||||||||||||||||
| 3335 | while (ai) | ||||||||||||||||||||||||||||||||||
| 3336 | { | ||||||||||||||||||||||||||||||||||
| 3337 | gai_free(ai->ai_addr); | ||||||||||||||||||||||||||||||||||
| 3338 | gai_free(ai->ai_canonname); | ||||||||||||||||||||||||||||||||||
| 3339 | addrinfo_type* ainext = ai->ai_next; | ||||||||||||||||||||||||||||||||||
| 3340 | gai_free(ai); | ||||||||||||||||||||||||||||||||||
| 3341 | ai = ainext; | ||||||||||||||||||||||||||||||||||
| 3342 | } | ||||||||||||||||||||||||||||||||||
| 3343 | } | ||||||||||||||||||||||||||||||||||
| 3344 | |||||||||||||||||||||||||||||||||||
| 3345 | inline int getaddrinfo_emulation(const char* host, const char* service, | ||||||||||||||||||||||||||||||||||
| 3346 | const addrinfo_type* hintsp, addrinfo_type** result) | ||||||||||||||||||||||||||||||||||
| 3347 | { | ||||||||||||||||||||||||||||||||||
| 3348 | // Set up linked list of addrinfo structures. | ||||||||||||||||||||||||||||||||||
| 3349 | addrinfo_type* aihead = 0; | ||||||||||||||||||||||||||||||||||
| 3350 | addrinfo_type** ainext = &aihead; | ||||||||||||||||||||||||||||||||||
| 3351 | char* canon = 0; | ||||||||||||||||||||||||||||||||||
| 3352 | |||||||||||||||||||||||||||||||||||
| 3353 | // Supply default hints if not specified by caller. | ||||||||||||||||||||||||||||||||||
| 3354 | addrinfo_type hints = addrinfo_type(); | ||||||||||||||||||||||||||||||||||
| 3355 | hints.ai_family = BOOST_ASIO_OS_DEF(AF_UNSPEC)0; | ||||||||||||||||||||||||||||||||||
| 3356 | if (hintsp) | ||||||||||||||||||||||||||||||||||
| 3357 | hints = *hintsp; | ||||||||||||||||||||||||||||||||||
| 3358 | |||||||||||||||||||||||||||||||||||
| 3359 | // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED | ||||||||||||||||||||||||||||||||||
| 3360 | // and AI_ALL flags. | ||||||||||||||||||||||||||||||||||
| 3361 | #if defined(AI_V4MAPPED0x0008) | ||||||||||||||||||||||||||||||||||
| 3362 | if (hints.ai_family != BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||
| 3363 | hints.ai_flags &= ~AI_V4MAPPED0x0008; | ||||||||||||||||||||||||||||||||||
| 3364 | #endif | ||||||||||||||||||||||||||||||||||
| 3365 | #if defined(AI_ALL0x0010) | ||||||||||||||||||||||||||||||||||
| 3366 | if (hints.ai_family != BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||
| 3367 | hints.ai_flags &= ~AI_ALL0x0010; | ||||||||||||||||||||||||||||||||||
| 3368 | #endif | ||||||||||||||||||||||||||||||||||
| 3369 | |||||||||||||||||||||||||||||||||||
| 3370 | // Basic error checking. | ||||||||||||||||||||||||||||||||||
| 3371 | int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family, | ||||||||||||||||||||||||||||||||||
| 3372 | hints.ai_socktype, hints.ai_protocol); | ||||||||||||||||||||||||||||||||||
| 3373 | if (rc != 0) | ||||||||||||||||||||||||||||||||||
| 3374 | { | ||||||||||||||||||||||||||||||||||
| 3375 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||
| 3376 | return rc; | ||||||||||||||||||||||||||||||||||
| 3377 | } | ||||||||||||||||||||||||||||||||||
| 3378 | |||||||||||||||||||||||||||||||||||
| 3379 | gai_search search[2]; | ||||||||||||||||||||||||||||||||||
| 3380 | int search_count = gai_nsearch(host, &hints, search); | ||||||||||||||||||||||||||||||||||
| 3381 | for (gai_search* sptr = search; sptr < search + search_count; ++sptr) | ||||||||||||||||||||||||||||||||||
| 3382 | { | ||||||||||||||||||||||||||||||||||
| 3383 | // Check for IPv4 dotted decimal string. | ||||||||||||||||||||||||||||||||||
| 3384 | in4_addr_type inaddr; | ||||||||||||||||||||||||||||||||||
| 3385 | boost::system::error_code ec; | ||||||||||||||||||||||||||||||||||
| 3386 | if (socket_ops::inet_pton(BOOST_ASIO_OS_DEF(AF_INET)2, | ||||||||||||||||||||||||||||||||||
| 3387 | sptr->host, &inaddr, 0, ec) == 1) | ||||||||||||||||||||||||||||||||||
| 3388 | { | ||||||||||||||||||||||||||||||||||
| 3389 | if (hints.ai_family != BOOST_ASIO_OS_DEF(AF_UNSPEC)0 | ||||||||||||||||||||||||||||||||||
| 3390 | && hints.ai_family != BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||
| 3391 | { | ||||||||||||||||||||||||||||||||||
| 3392 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||
| 3393 | gai_free(canon); | ||||||||||||||||||||||||||||||||||
| 3394 | return EAI_FAMILY-6; | ||||||||||||||||||||||||||||||||||
| 3395 | } | ||||||||||||||||||||||||||||||||||
| 3396 | if (sptr->family == BOOST_ASIO_OS_DEF(AF_INET)2) | ||||||||||||||||||||||||||||||||||
| 3397 | { | ||||||||||||||||||||||||||||||||||
| 3398 | rc = gai_aistruct(&ainext, &hints, &inaddr, BOOST_ASIO_OS_DEF(AF_INET)2); | ||||||||||||||||||||||||||||||||||
| 3399 | if (rc != 0) | ||||||||||||||||||||||||||||||||||
| 3400 | { | ||||||||||||||||||||||||||||||||||
| 3401 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||
| 3402 | gai_free(canon); | ||||||||||||||||||||||||||||||||||
| 3403 | return rc; | ||||||||||||||||||||||||||||||||||
| 3404 | } | ||||||||||||||||||||||||||||||||||
| 3405 | } | ||||||||||||||||||||||||||||||||||
| 3406 | continue; | ||||||||||||||||||||||||||||||||||
| 3407 | } | ||||||||||||||||||||||||||||||||||
| 3408 | |||||||||||||||||||||||||||||||||||
| 3409 | // Check for IPv6 hex string. | ||||||||||||||||||||||||||||||||||
| 3410 | in6_addr_type in6addr; | ||||||||||||||||||||||||||||||||||
| 3411 | if (socket_ops::inet_pton(BOOST_ASIO_OS_DEF(AF_INET6)10, | ||||||||||||||||||||||||||||||||||
| 3412 | sptr->host, &in6addr, 0, ec) == 1) | ||||||||||||||||||||||||||||||||||
| 3413 | { | ||||||||||||||||||||||||||||||||||
| 3414 | if (hints.ai_family != BOOST_ASIO_OS_DEF(AF_UNSPEC)0 | ||||||||||||||||||||||||||||||||||
| 3415 | && hints.ai_family != BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||
| 3416 | { | ||||||||||||||||||||||||||||||||||
| 3417 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||
| 3418 | gai_free(canon); | ||||||||||||||||||||||||||||||||||
| 3419 | return EAI_FAMILY-6; | ||||||||||||||||||||||||||||||||||
| 3420 | } | ||||||||||||||||||||||||||||||||||
| 3421 | if (sptr->family == BOOST_ASIO_OS_DEF(AF_INET6)10) | ||||||||||||||||||||||||||||||||||
| 3422 | { | ||||||||||||||||||||||||||||||||||
| 3423 | rc = gai_aistruct(&ainext, &hints, &in6addr, | ||||||||||||||||||||||||||||||||||
| 3424 | BOOST_ASIO_OS_DEF(AF_INET6)10); | ||||||||||||||||||||||||||||||||||
| 3425 | if (rc != 0) | ||||||||||||||||||||||||||||||||||
| 3426 | { | ||||||||||||||||||||||||||||||||||
| 3427 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||
| 3428 | gai_free(canon); | ||||||||||||||||||||||||||||||||||
| 3429 | return rc; | ||||||||||||||||||||||||||||||||||
| 3430 | } | ||||||||||||||||||||||||||||||||||
| 3431 | } | ||||||||||||||||||||||||||||||||||
| 3432 | continue; | ||||||||||||||||||||||||||||||||||
| 3433 | } | ||||||||||||||||||||||||||||||||||
| 3434 | |||||||||||||||||||||||||||||||||||
| 3435 | // Look up hostname. | ||||||||||||||||||||||||||||||||||
| 3436 | hostent hent; | ||||||||||||||||||||||||||||||||||
| 3437 | char hbuf[8192] = ""; | ||||||||||||||||||||||||||||||||||
| 3438 | hostent* hptr = socket_ops::gethostbyname(sptr->host, | ||||||||||||||||||||||||||||||||||
| 3439 | sptr->family, &hent, hbuf, sizeof(hbuf), hints.ai_flags, ec); | ||||||||||||||||||||||||||||||||||
| 3440 | if (hptr == 0) | ||||||||||||||||||||||||||||||||||
| 3441 | { | ||||||||||||||||||||||||||||||||||
| 3442 | if (search_count == 2) | ||||||||||||||||||||||||||||||||||
| 3443 | { | ||||||||||||||||||||||||||||||||||
| 3444 | // Failure is OK if there are multiple searches. | ||||||||||||||||||||||||||||||||||
| 3445 | continue; | ||||||||||||||||||||||||||||||||||
| 3446 | } | ||||||||||||||||||||||||||||||||||
| 3447 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||
| 3448 | gai_free(canon); | ||||||||||||||||||||||||||||||||||
| 3449 | if (ec == boost::asio::error::host_not_found) | ||||||||||||||||||||||||||||||||||
| 3450 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||
| 3451 | if (ec == boost::asio::error::host_not_found_try_again) | ||||||||||||||||||||||||||||||||||
| 3452 | return EAI_AGAIN-3; | ||||||||||||||||||||||||||||||||||
| 3453 | if (ec == boost::asio::error::no_recovery) | ||||||||||||||||||||||||||||||||||
| 3454 | return EAI_FAIL-4; | ||||||||||||||||||||||||||||||||||
| 3455 | if (ec == boost::asio::error::no_data) | ||||||||||||||||||||||||||||||||||
| 3456 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||
| 3457 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||
| 3458 | } | ||||||||||||||||||||||||||||||||||
| 3459 | |||||||||||||||||||||||||||||||||||
| 3460 | // Check for address family mismatch if one was specified. | ||||||||||||||||||||||||||||||||||
| 3461 | if (hints.ai_family != BOOST_ASIO_OS_DEF(AF_UNSPEC)0 | ||||||||||||||||||||||||||||||||||
| 3462 | && hints.ai_family != hptr->h_addrtype) | ||||||||||||||||||||||||||||||||||
| 3463 | { | ||||||||||||||||||||||||||||||||||
| 3464 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||
| 3465 | gai_free(canon); | ||||||||||||||||||||||||||||||||||
| 3466 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||
| 3467 | return EAI_FAMILY-6; | ||||||||||||||||||||||||||||||||||
| 3468 | } | ||||||||||||||||||||||||||||||||||
| 3469 | |||||||||||||||||||||||||||||||||||
| 3470 | // Save canonical name first time. | ||||||||||||||||||||||||||||||||||
| 3471 | if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0] | ||||||||||||||||||||||||||||||||||
| 3472 | && (hints.ai_flags & AI_CANONNAME0x0002) && canon == 0) | ||||||||||||||||||||||||||||||||||
| 3473 | { | ||||||||||||||||||||||||||||||||||
| 3474 | std::size_t canon_len = strlen(hptr->h_name) + 1; | ||||||||||||||||||||||||||||||||||
| 3475 | canon = gai_alloc<char>(canon_len); | ||||||||||||||||||||||||||||||||||
| 3476 | if (canon == 0) | ||||||||||||||||||||||||||||||||||
| 3477 | { | ||||||||||||||||||||||||||||||||||
| 3478 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||
| 3479 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||
| 3480 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||
| 3481 | } | ||||||||||||||||||||||||||||||||||
| 3482 | gai_strcpy(canon, hptr->h_name, canon_len); | ||||||||||||||||||||||||||||||||||
| 3483 | } | ||||||||||||||||||||||||||||||||||
| 3484 | |||||||||||||||||||||||||||||||||||
| 3485 | // Create an addrinfo structure for each returned address. | ||||||||||||||||||||||||||||||||||
| 3486 | for (char** ap = hptr->h_addr_list; *ap; ++ap) | ||||||||||||||||||||||||||||||||||
| 3487 | { | ||||||||||||||||||||||||||||||||||
| 3488 | rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype); | ||||||||||||||||||||||||||||||||||
| 3489 | if (rc != 0) | ||||||||||||||||||||||||||||||||||
| 3490 | { | ||||||||||||||||||||||||||||||||||
| 3491 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||
| 3492 | gai_free(canon); | ||||||||||||||||||||||||||||||||||
| 3493 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||
| 3494 | return EAI_FAMILY-6; | ||||||||||||||||||||||||||||||||||
| 3495 | } | ||||||||||||||||||||||||||||||||||
| 3496 | } | ||||||||||||||||||||||||||||||||||
| 3497 | |||||||||||||||||||||||||||||||||||
| 3498 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||
| 3499 | } | ||||||||||||||||||||||||||||||||||
| 3500 | |||||||||||||||||||||||||||||||||||
| 3501 | // Check if we found anything. | ||||||||||||||||||||||||||||||||||
| 3502 | if (aihead == 0) | ||||||||||||||||||||||||||||||||||
| 3503 | { | ||||||||||||||||||||||||||||||||||
| 3504 | gai_free(canon); | ||||||||||||||||||||||||||||||||||
| 3505 | return EAI_NONAME-2; | ||||||||||||||||||||||||||||||||||
| 3506 | } | ||||||||||||||||||||||||||||||||||
| 3507 | |||||||||||||||||||||||||||||||||||
| 3508 | // Return canonical name in first entry. | ||||||||||||||||||||||||||||||||||
| 3509 | if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME0x0002)) | ||||||||||||||||||||||||||||||||||
| 3510 | { | ||||||||||||||||||||||||||||||||||
| 3511 | if (canon) | ||||||||||||||||||||||||||||||||||
| 3512 | { | ||||||||||||||||||||||||||||||||||
| 3513 | aihead->ai_canonname = canon; | ||||||||||||||||||||||||||||||||||
| 3514 | canon = 0; | ||||||||||||||||||||||||||||||||||
| 3515 | } | ||||||||||||||||||||||||||||||||||
| 3516 | else | ||||||||||||||||||||||||||||||||||
| 3517 | { | ||||||||||||||||||||||||||||||||||
| 3518 | std::size_t canonname_len = strlen(search[0].host) + 1; | ||||||||||||||||||||||||||||||||||
| 3519 | aihead->ai_canonname = gai_alloc<char>(canonname_len); | ||||||||||||||||||||||||||||||||||
| 3520 | if (aihead->ai_canonname == 0) | ||||||||||||||||||||||||||||||||||
| 3521 | { | ||||||||||||||||||||||||||||||||||
| 3522 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||
| 3523 | return EAI_MEMORY-10; | ||||||||||||||||||||||||||||||||||
| 3524 | } | ||||||||||||||||||||||||||||||||||
| 3525 | gai_strcpy(aihead->ai_canonname, search[0].host, canonname_len); | ||||||||||||||||||||||||||||||||||
| 3526 | } | ||||||||||||||||||||||||||||||||||
| 3527 | } | ||||||||||||||||||||||||||||||||||
| 3528 | gai_free(canon); | ||||||||||||||||||||||||||||||||||
| 3529 | |||||||||||||||||||||||||||||||||||
| 3530 | // Process the service name. | ||||||||||||||||||||||||||||||||||
| 3531 | if (service != 0 && service[0] != '\0') | ||||||||||||||||||||||||||||||||||
| 3532 | { | ||||||||||||||||||||||||||||||||||
| 3533 | rc = gai_serv(aihead, &hints, service); | ||||||||||||||||||||||||||||||||||
| 3534 | if (rc != 0) | ||||||||||||||||||||||||||||||||||
| 3535 | { | ||||||||||||||||||||||||||||||||||
| 3536 | freeaddrinfo_emulation(aihead); | ||||||||||||||||||||||||||||||||||
| 3537 | return rc; | ||||||||||||||||||||||||||||||||||
| 3538 | } | ||||||||||||||||||||||||||||||||||
| 3539 | } | ||||||||||||||||||||||||||||||||||
| 3540 | |||||||||||||||||||||||||||||||||||
| 3541 | // Return result to caller. | ||||||||||||||||||||||||||||||||||
| 3542 | *result = aihead; | ||||||||||||||||||||||||||||||||||
| 3543 | return 0; | ||||||||||||||||||||||||||||||||||
| 3544 | } | ||||||||||||||||||||||||||||||||||
| 3545 | |||||||||||||||||||||||||||||||||||
| 3546 | inline boost::system::error_code getnameinfo_emulation( | ||||||||||||||||||||||||||||||||||
| 3547 | const socket_addr_type* sa, std::size_t salen, char* host, | ||||||||||||||||||||||||||||||||||
| 3548 | std::size_t hostlen, char* serv, std::size_t servlen, int flags, | ||||||||||||||||||||||||||||||||||
| 3549 | boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 3550 | { | ||||||||||||||||||||||||||||||||||
| 3551 | using namespace std; | ||||||||||||||||||||||||||||||||||
| 3552 | |||||||||||||||||||||||||||||||||||
| 3553 | const char* addr; | ||||||||||||||||||||||||||||||||||
| 3554 | size_t addr_len; | ||||||||||||||||||||||||||||||||||
| 3555 | unsigned short port; | ||||||||||||||||||||||||||||||||||
| 3556 | switch (sa->sa_family) | ||||||||||||||||||||||||||||||||||
| 3557 | { | ||||||||||||||||||||||||||||||||||
| 3558 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||
| 3559 | if (salen != sizeof(sockaddr_in4_type)) | ||||||||||||||||||||||||||||||||||
| 3560 | { | ||||||||||||||||||||||||||||||||||
| 3561 | return ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 3562 | } | ||||||||||||||||||||||||||||||||||
| 3563 | addr = reinterpret_cast<const char*>( | ||||||||||||||||||||||||||||||||||
| 3564 | &reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_addr); | ||||||||||||||||||||||||||||||||||
| 3565 | addr_len = sizeof(in4_addr_type); | ||||||||||||||||||||||||||||||||||
| 3566 | port = reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_port; | ||||||||||||||||||||||||||||||||||
| 3567 | break; | ||||||||||||||||||||||||||||||||||
| 3568 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||
| 3569 | if (salen != sizeof(sockaddr_in6_type)) | ||||||||||||||||||||||||||||||||||
| 3570 | { | ||||||||||||||||||||||||||||||||||
| 3571 | return ec = boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 3572 | } | ||||||||||||||||||||||||||||||||||
| 3573 | addr = reinterpret_cast<const char*>( | ||||||||||||||||||||||||||||||||||
| 3574 | &reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_addr); | ||||||||||||||||||||||||||||||||||
| 3575 | addr_len = sizeof(in6_addr_type); | ||||||||||||||||||||||||||||||||||
| 3576 | port = reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_port; | ||||||||||||||||||||||||||||||||||
| 3577 | break; | ||||||||||||||||||||||||||||||||||
| 3578 | default: | ||||||||||||||||||||||||||||||||||
| 3579 | return ec = boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||
| 3580 | } | ||||||||||||||||||||||||||||||||||
| 3581 | |||||||||||||||||||||||||||||||||||
| 3582 | if (host && hostlen > 0) | ||||||||||||||||||||||||||||||||||
| 3583 | { | ||||||||||||||||||||||||||||||||||
| 3584 | if (flags & NI_NUMERICHOST1) | ||||||||||||||||||||||||||||||||||
| 3585 | { | ||||||||||||||||||||||||||||||||||
| 3586 | if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0) | ||||||||||||||||||||||||||||||||||
| 3587 | { | ||||||||||||||||||||||||||||||||||
| 3588 | return ec; | ||||||||||||||||||||||||||||||||||
| 3589 | } | ||||||||||||||||||||||||||||||||||
| 3590 | } | ||||||||||||||||||||||||||||||||||
| 3591 | else | ||||||||||||||||||||||||||||||||||
| 3592 | { | ||||||||||||||||||||||||||||||||||
| 3593 | hostent hent; | ||||||||||||||||||||||||||||||||||
| 3594 | char hbuf[8192] = ""; | ||||||||||||||||||||||||||||||||||
| 3595 | hostent* hptr = socket_ops::gethostbyaddr(addr, | ||||||||||||||||||||||||||||||||||
| 3596 | static_cast<int>(addr_len), sa->sa_family, | ||||||||||||||||||||||||||||||||||
| 3597 | &hent, hbuf, sizeof(hbuf), ec); | ||||||||||||||||||||||||||||||||||
| 3598 | if (hptr && hptr->h_name && hptr->h_name[0] != '\0') | ||||||||||||||||||||||||||||||||||
| 3599 | { | ||||||||||||||||||||||||||||||||||
| 3600 | if (flags & NI_NOFQDN4) | ||||||||||||||||||||||||||||||||||
| 3601 | { | ||||||||||||||||||||||||||||||||||
| 3602 | char* dot = strchr(hptr->h_name, '.'); | ||||||||||||||||||||||||||||||||||
| 3603 | if (dot) | ||||||||||||||||||||||||||||||||||
| 3604 | { | ||||||||||||||||||||||||||||||||||
| 3605 | *dot = 0; | ||||||||||||||||||||||||||||||||||
| 3606 | } | ||||||||||||||||||||||||||||||||||
| 3607 | } | ||||||||||||||||||||||||||||||||||
| 3608 | gai_strcpy(host, hptr->h_name, hostlen); | ||||||||||||||||||||||||||||||||||
| 3609 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||
| 3610 | } | ||||||||||||||||||||||||||||||||||
| 3611 | else | ||||||||||||||||||||||||||||||||||
| 3612 | { | ||||||||||||||||||||||||||||||||||
| 3613 | socket_ops::freehostent(hptr); | ||||||||||||||||||||||||||||||||||
| 3614 | if (flags & NI_NAMEREQD8) | ||||||||||||||||||||||||||||||||||
| 3615 | { | ||||||||||||||||||||||||||||||||||
| 3616 | return ec = boost::asio::error::host_not_found; | ||||||||||||||||||||||||||||||||||
| 3617 | } | ||||||||||||||||||||||||||||||||||
| 3618 | if (socket_ops::inet_ntop(sa->sa_family, | ||||||||||||||||||||||||||||||||||
| 3619 | addr, host, hostlen, 0, ec) == 0) | ||||||||||||||||||||||||||||||||||
| 3620 | { | ||||||||||||||||||||||||||||||||||
| 3621 | return ec; | ||||||||||||||||||||||||||||||||||
| 3622 | } | ||||||||||||||||||||||||||||||||||
| 3623 | } | ||||||||||||||||||||||||||||||||||
| 3624 | } | ||||||||||||||||||||||||||||||||||
| 3625 | } | ||||||||||||||||||||||||||||||||||
| 3626 | |||||||||||||||||||||||||||||||||||
| 3627 | if (serv && servlen > 0) | ||||||||||||||||||||||||||||||||||
| 3628 | { | ||||||||||||||||||||||||||||||||||
| 3629 | if (flags & NI_NUMERICSERV2) | ||||||||||||||||||||||||||||||||||
| 3630 | { | ||||||||||||||||||||||||||||||||||
| 3631 | if (servlen < 6) | ||||||||||||||||||||||||||||||||||
| 3632 | { | ||||||||||||||||||||||||||||||||||
| 3633 | return ec = boost::asio::error::no_buffer_space; | ||||||||||||||||||||||||||||||||||
| 3634 | } | ||||||||||||||||||||||||||||||||||
| 3635 | #if defined(BOOST_ASIO_HAS_SNPRINTF) | ||||||||||||||||||||||||||||||||||
| 3636 | snprintf(serv, servlen, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||
| 3637 | #elif defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
| 3638 | sprintf_s(serv, servlen, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||
| 3639 | #else // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
| 3640 | sprintf(serv, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||
| 3641 | #endif // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
| 3642 | } | ||||||||||||||||||||||||||||||||||
| 3643 | else | ||||||||||||||||||||||||||||||||||
| 3644 | { | ||||||||||||||||||||||||||||||||||
| 3645 | #if defined(BOOST_ASIO_HAS_PTHREADS1) | ||||||||||||||||||||||||||||||||||
| 3646 | static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER{ { 0, 0, 0, 0, PTHREAD_MUTEX_TIMED_NP, 0, 0, { __null, __null } } }; | ||||||||||||||||||||||||||||||||||
| 3647 | ::pthread_mutex_lock(&mutex); | ||||||||||||||||||||||||||||||||||
| 3648 | #endif // defined(BOOST_ASIO_HAS_PTHREADS) | ||||||||||||||||||||||||||||||||||
| 3649 | servent* sptr = ::getservbyport(port, (flags & NI_DGRAM16) ? "udp" : 0); | ||||||||||||||||||||||||||||||||||
| 3650 | if (sptr && sptr->s_name && sptr->s_name[0] != '\0') | ||||||||||||||||||||||||||||||||||
| 3651 | { | ||||||||||||||||||||||||||||||||||
| 3652 | gai_strcpy(serv, sptr->s_name, servlen); | ||||||||||||||||||||||||||||||||||
| 3653 | } | ||||||||||||||||||||||||||||||||||
| 3654 | else | ||||||||||||||||||||||||||||||||||
| 3655 | { | ||||||||||||||||||||||||||||||||||
| 3656 | if (servlen < 6) | ||||||||||||||||||||||||||||||||||
| 3657 | { | ||||||||||||||||||||||||||||||||||
| 3658 | return ec = boost::asio::error::no_buffer_space; | ||||||||||||||||||||||||||||||||||
| 3659 | } | ||||||||||||||||||||||||||||||||||
| 3660 | #if defined(BOOST_ASIO_HAS_SNPRINTF) | ||||||||||||||||||||||||||||||||||
| 3661 | snprintf(serv, servlen, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||
| 3662 | #elif defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
| 3663 | sprintf_s(serv, servlen, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||
| 3664 | #else // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
| 3665 | sprintf(serv, "%u", ntohs(port)__bswap_16 (port)); | ||||||||||||||||||||||||||||||||||
| 3666 | #endif // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
| 3667 | } | ||||||||||||||||||||||||||||||||||
| 3668 | #if defined(BOOST_ASIO_HAS_PTHREADS1) | ||||||||||||||||||||||||||||||||||
| 3669 | ::pthread_mutex_unlock(&mutex); | ||||||||||||||||||||||||||||||||||
| 3670 | #endif // defined(BOOST_ASIO_HAS_PTHREADS) | ||||||||||||||||||||||||||||||||||
| 3671 | } | ||||||||||||||||||||||||||||||||||
| 3672 | } | ||||||||||||||||||||||||||||||||||
| 3673 | |||||||||||||||||||||||||||||||||||
| 3674 | boost::asio::error::clear(ec); | ||||||||||||||||||||||||||||||||||
| 3675 | return ec; | ||||||||||||||||||||||||||||||||||
| 3676 | } | ||||||||||||||||||||||||||||||||||
| 3677 | |||||||||||||||||||||||||||||||||||
| 3678 | #endif // !defined(BOOST_ASIO_HAS_GETADDRINFO) | ||||||||||||||||||||||||||||||||||
| 3679 | |||||||||||||||||||||||||||||||||||
| 3680 | inline boost::system::error_code translate_addrinfo_error(int error) | ||||||||||||||||||||||||||||||||||
| 3681 | { | ||||||||||||||||||||||||||||||||||
| 3682 | switch (error) | ||||||||||||||||||||||||||||||||||
| 3683 | { | ||||||||||||||||||||||||||||||||||
| 3684 | case 0: | ||||||||||||||||||||||||||||||||||
| 3685 | return boost::system::error_code(); | ||||||||||||||||||||||||||||||||||
| 3686 | case EAI_AGAIN-3: | ||||||||||||||||||||||||||||||||||
| 3687 | return boost::asio::error::host_not_found_try_again; | ||||||||||||||||||||||||||||||||||
| 3688 | case EAI_BADFLAGS-1: | ||||||||||||||||||||||||||||||||||
| 3689 | return boost::asio::error::invalid_argument; | ||||||||||||||||||||||||||||||||||
| 3690 | case EAI_FAIL-4: | ||||||||||||||||||||||||||||||||||
| 3691 | return boost::asio::error::no_recovery; | ||||||||||||||||||||||||||||||||||
| 3692 | case EAI_FAMILY-6: | ||||||||||||||||||||||||||||||||||
| 3693 | return boost::asio::error::address_family_not_supported; | ||||||||||||||||||||||||||||||||||
| 3694 | case EAI_MEMORY-10: | ||||||||||||||||||||||||||||||||||
| 3695 | return boost::asio::error::no_memory; | ||||||||||||||||||||||||||||||||||
| 3696 | case EAI_NONAME-2: | ||||||||||||||||||||||||||||||||||
| 3697 | #if defined(EAI_ADDRFAMILY-9) | ||||||||||||||||||||||||||||||||||
| 3698 | case EAI_ADDRFAMILY-9: | ||||||||||||||||||||||||||||||||||
| 3699 | #endif | ||||||||||||||||||||||||||||||||||
| 3700 | #if defined(EAI_NODATA-5) && (EAI_NODATA-5 != EAI_NONAME-2) | ||||||||||||||||||||||||||||||||||
| 3701 | case EAI_NODATA-5: | ||||||||||||||||||||||||||||||||||
| 3702 | #endif | ||||||||||||||||||||||||||||||||||
| 3703 | return boost::asio::error::host_not_found; | ||||||||||||||||||||||||||||||||||
| 3704 | case EAI_SERVICE-8: | ||||||||||||||||||||||||||||||||||
| 3705 | return boost::asio::error::service_not_found; | ||||||||||||||||||||||||||||||||||
| 3706 | case EAI_SOCKTYPE-7: | ||||||||||||||||||||||||||||||||||
| 3707 | return boost::asio::error::socket_type_not_supported; | ||||||||||||||||||||||||||||||||||
| 3708 | default: // Possibly the non-portable EAI_SYSTEM. | ||||||||||||||||||||||||||||||||||
| 3709 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 3710 | return boost::system::error_code( | ||||||||||||||||||||||||||||||||||
| 3711 | WSAGetLastError(), boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||
| 3712 | #else | ||||||||||||||||||||||||||||||||||
| 3713 | return boost::system::error_code( | ||||||||||||||||||||||||||||||||||
| 3714 | errno(*__errno_location ()), boost::asio::error::get_system_category()); | ||||||||||||||||||||||||||||||||||
| 3715 | #endif | ||||||||||||||||||||||||||||||||||
| 3716 | } | ||||||||||||||||||||||||||||||||||
| 3717 | } | ||||||||||||||||||||||||||||||||||
| 3718 | |||||||||||||||||||||||||||||||||||
| 3719 | boost::system::error_code getaddrinfo(const char* host, | ||||||||||||||||||||||||||||||||||
| 3720 | const char* service, const addrinfo_type& hints, | ||||||||||||||||||||||||||||||||||
| 3721 | addrinfo_type** result, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 3722 | { | ||||||||||||||||||||||||||||||||||
| 3723 | host = (host && *host) ? host : 0; | ||||||||||||||||||||||||||||||||||
| 3724 | service = (service && *service) ? service : 0; | ||||||||||||||||||||||||||||||||||
| 3725 | clear_last_error(); | ||||||||||||||||||||||||||||||||||
| 3726 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 3727 | # if defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||
| 3728 | // Building for Windows XP, Windows Server 2003, or later. | ||||||||||||||||||||||||||||||||||
| 3729 | int error = ::getaddrinfo(host, service, &hints, result); | ||||||||||||||||||||||||||||||||||
| 3730 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||
| 3731 | # else | ||||||||||||||||||||||||||||||||||
| 3732 | // Building for Windows 2000 or earlier. | ||||||||||||||||||||||||||||||||||
| 3733 | typedef int (WSAAPI *gai_t)(const char*, | ||||||||||||||||||||||||||||||||||
| 3734 | const char*, const addrinfo_type*, addrinfo_type**); | ||||||||||||||||||||||||||||||||||
| 3735 | if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) | ||||||||||||||||||||||||||||||||||
| 3736 | { | ||||||||||||||||||||||||||||||||||
| 3737 | if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) | ||||||||||||||||||||||||||||||||||
| 3738 | { | ||||||||||||||||||||||||||||||||||
| 3739 | int error = gai(host, service, &hints, result); | ||||||||||||||||||||||||||||||||||
| 3740 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||
| 3741 | } | ||||||||||||||||||||||||||||||||||
| 3742 | } | ||||||||||||||||||||||||||||||||||
| 3743 | int error = getaddrinfo_emulation(host, service, &hints, result); | ||||||||||||||||||||||||||||||||||
| 3744 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||
| 3745 | # endif | ||||||||||||||||||||||||||||||||||
| 3746 | #elif !defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||
| 3747 | int error = getaddrinfo_emulation(host, service, &hints, result); | ||||||||||||||||||||||||||||||||||
| 3748 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||
| 3749 | #else | ||||||||||||||||||||||||||||||||||
| 3750 | int error = ::getaddrinfo(host, service, &hints, result); | ||||||||||||||||||||||||||||||||||
| 3751 | #if defined(__MACH__) && defined(__APPLE__) | ||||||||||||||||||||||||||||||||||
| 3752 | using namespace std; // For isdigit and atoi. | ||||||||||||||||||||||||||||||||||
| 3753 | if (error == 0 && service && isdigit(static_cast<unsigned char>(service[0]))) | ||||||||||||||||||||||||||||||||||
| 3754 | { | ||||||||||||||||||||||||||||||||||
| 3755 | u_short_type port = host_to_network_short(atoi(service)); | ||||||||||||||||||||||||||||||||||
| 3756 | for (addrinfo_type* ai = *result; ai; ai = ai->ai_next) | ||||||||||||||||||||||||||||||||||
| 3757 | { | ||||||||||||||||||||||||||||||||||
| 3758 | switch (ai->ai_family) | ||||||||||||||||||||||||||||||||||
| 3759 | { | ||||||||||||||||||||||||||||||||||
| 3760 | case BOOST_ASIO_OS_DEF(AF_INET)2: | ||||||||||||||||||||||||||||||||||
| 3761 | { | ||||||||||||||||||||||||||||||||||
| 3762 | sockaddr_in4_type* sinptr = | ||||||||||||||||||||||||||||||||||
| 3763 | reinterpret_cast<sockaddr_in4_type*>(ai->ai_addr); | ||||||||||||||||||||||||||||||||||
| 3764 | if (sinptr->sin_port == 0) | ||||||||||||||||||||||||||||||||||
| 3765 | sinptr->sin_port = port; | ||||||||||||||||||||||||||||||||||
| 3766 | break; | ||||||||||||||||||||||||||||||||||
| 3767 | } | ||||||||||||||||||||||||||||||||||
| 3768 | case BOOST_ASIO_OS_DEF(AF_INET6)10: | ||||||||||||||||||||||||||||||||||
| 3769 | { | ||||||||||||||||||||||||||||||||||
| 3770 | sockaddr_in6_type* sin6ptr = | ||||||||||||||||||||||||||||||||||
| 3771 | reinterpret_cast<sockaddr_in6_type*>(ai->ai_addr); | ||||||||||||||||||||||||||||||||||
| 3772 | if (sin6ptr->sin6_port == 0) | ||||||||||||||||||||||||||||||||||
| 3773 | sin6ptr->sin6_port = port; | ||||||||||||||||||||||||||||||||||
| 3774 | break; | ||||||||||||||||||||||||||||||||||
| 3775 | } | ||||||||||||||||||||||||||||||||||
| 3776 | default: | ||||||||||||||||||||||||||||||||||
| 3777 | break; | ||||||||||||||||||||||||||||||||||
| 3778 | } | ||||||||||||||||||||||||||||||||||
| 3779 | } | ||||||||||||||||||||||||||||||||||
| 3780 | } | ||||||||||||||||||||||||||||||||||
| 3781 | #endif | ||||||||||||||||||||||||||||||||||
| 3782 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||
| 3783 | #endif | ||||||||||||||||||||||||||||||||||
| 3784 | } | ||||||||||||||||||||||||||||||||||
| 3785 | |||||||||||||||||||||||||||||||||||
| 3786 | boost::system::error_code background_getaddrinfo( | ||||||||||||||||||||||||||||||||||
| 3787 | const weak_cancel_token_type& cancel_token, const char* host, | ||||||||||||||||||||||||||||||||||
| 3788 | const char* service, const addrinfo_type& hints, | ||||||||||||||||||||||||||||||||||
| 3789 | addrinfo_type** result, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 3790 | { | ||||||||||||||||||||||||||||||||||
| 3791 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||
| 3792 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||
| 3793 | else | ||||||||||||||||||||||||||||||||||
| 3794 | socket_ops::getaddrinfo(host, service, hints, result, ec); | ||||||||||||||||||||||||||||||||||
| 3795 | return ec; | ||||||||||||||||||||||||||||||||||
| 3796 | } | ||||||||||||||||||||||||||||||||||
| 3797 | |||||||||||||||||||||||||||||||||||
| 3798 | void freeaddrinfo(addrinfo_type* ai) | ||||||||||||||||||||||||||||||||||
| 3799 | { | ||||||||||||||||||||||||||||||||||
| 3800 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 3801 | # if defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||
| 3802 | // Building for Windows XP, Windows Server 2003, or later. | ||||||||||||||||||||||||||||||||||
| 3803 | ::freeaddrinfo(ai); | ||||||||||||||||||||||||||||||||||
| 3804 | # else | ||||||||||||||||||||||||||||||||||
| 3805 | // Building for Windows 2000 or earlier. | ||||||||||||||||||||||||||||||||||
| 3806 | typedef int (WSAAPI *fai_t)(addrinfo_type*); | ||||||||||||||||||||||||||||||||||
| 3807 | if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) | ||||||||||||||||||||||||||||||||||
| 3808 | { | ||||||||||||||||||||||||||||||||||
| 3809 | if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) | ||||||||||||||||||||||||||||||||||
| 3810 | { | ||||||||||||||||||||||||||||||||||
| 3811 | fai(ai); | ||||||||||||||||||||||||||||||||||
| 3812 | return; | ||||||||||||||||||||||||||||||||||
| 3813 | } | ||||||||||||||||||||||||||||||||||
| 3814 | } | ||||||||||||||||||||||||||||||||||
| 3815 | freeaddrinfo_emulation(ai); | ||||||||||||||||||||||||||||||||||
| 3816 | # endif | ||||||||||||||||||||||||||||||||||
| 3817 | #elif !defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||
| 3818 | freeaddrinfo_emulation(ai); | ||||||||||||||||||||||||||||||||||
| 3819 | #else | ||||||||||||||||||||||||||||||||||
| 3820 | ::freeaddrinfo(ai); | ||||||||||||||||||||||||||||||||||
| 3821 | #endif | ||||||||||||||||||||||||||||||||||
| 3822 | } | ||||||||||||||||||||||||||||||||||
| 3823 | |||||||||||||||||||||||||||||||||||
| 3824 | boost::system::error_code getnameinfo(const void* addr, | ||||||||||||||||||||||||||||||||||
| 3825 | std::size_t addrlen, char* host, std::size_t hostlen, | ||||||||||||||||||||||||||||||||||
| 3826 | char* serv, std::size_t servlen, int flags, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 3827 | { | ||||||||||||||||||||||||||||||||||
| 3828 | #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) | ||||||||||||||||||||||||||||||||||
| 3829 | # if defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||
| 3830 | // Building for Windows XP, Windows Server 2003, or later. | ||||||||||||||||||||||||||||||||||
| 3831 | clear_last_error(); | ||||||||||||||||||||||||||||||||||
| 3832 | int error = ::getnameinfo(static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||
| 3833 | static_cast<socklen_t>(addrlen), host, static_cast<DWORD>(hostlen), | ||||||||||||||||||||||||||||||||||
| 3834 | serv, static_cast<DWORD>(servlen), flags); | ||||||||||||||||||||||||||||||||||
| 3835 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||
| 3836 | # else | ||||||||||||||||||||||||||||||||||
| 3837 | // Building for Windows 2000 or earlier. | ||||||||||||||||||||||||||||||||||
| 3838 | typedef int (WSAAPI *gni_t)(const socket_addr_type*, | ||||||||||||||||||||||||||||||||||
| 3839 | int, char*, DWORD, char*, DWORD, int); | ||||||||||||||||||||||||||||||||||
| 3840 | if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) | ||||||||||||||||||||||||||||||||||
| 3841 | { | ||||||||||||||||||||||||||||||||||
| 3842 | if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo")) | ||||||||||||||||||||||||||||||||||
| 3843 | { | ||||||||||||||||||||||||||||||||||
| 3844 | clear_last_error(); | ||||||||||||||||||||||||||||||||||
| 3845 | int error = gni(static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||
| 3846 | static_cast<int>(addrlen), host, static_cast<DWORD>(hostlen), | ||||||||||||||||||||||||||||||||||
| 3847 | serv, static_cast<DWORD>(servlen), flags); | ||||||||||||||||||||||||||||||||||
| 3848 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||
| 3849 | } | ||||||||||||||||||||||||||||||||||
| 3850 | } | ||||||||||||||||||||||||||||||||||
| 3851 | clear_last_error(); | ||||||||||||||||||||||||||||||||||
| 3852 | return getnameinfo_emulation(static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||
| 3853 | addrlen, host, hostlen, serv, servlen, flags, ec); | ||||||||||||||||||||||||||||||||||
| 3854 | # endif | ||||||||||||||||||||||||||||||||||
| 3855 | #elif !defined(BOOST_ASIO_HAS_GETADDRINFO1) | ||||||||||||||||||||||||||||||||||
| 3856 | using namespace std; // For memcpy. | ||||||||||||||||||||||||||||||||||
| 3857 | sockaddr_storage_type tmp_addr; | ||||||||||||||||||||||||||||||||||
| 3858 | memcpy(&tmp_addr, addr, addrlen); | ||||||||||||||||||||||||||||||||||
| 3859 | addr = &tmp_addr; | ||||||||||||||||||||||||||||||||||
| 3860 | clear_last_error(); | ||||||||||||||||||||||||||||||||||
| 3861 | return getnameinfo_emulation(static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||
| 3862 | addrlen, host, hostlen, serv, servlen, flags, ec); | ||||||||||||||||||||||||||||||||||
| 3863 | #else | ||||||||||||||||||||||||||||||||||
| 3864 | clear_last_error(); | ||||||||||||||||||||||||||||||||||
| 3865 | int error = ::getnameinfo(static_cast<const socket_addr_type*>(addr), | ||||||||||||||||||||||||||||||||||
| 3866 | addrlen, host, hostlen, serv, servlen, flags); | ||||||||||||||||||||||||||||||||||
| 3867 | return ec = translate_addrinfo_error(error); | ||||||||||||||||||||||||||||||||||
| 3868 | #endif | ||||||||||||||||||||||||||||||||||
| 3869 | } | ||||||||||||||||||||||||||||||||||
| 3870 | |||||||||||||||||||||||||||||||||||
| 3871 | boost::system::error_code sync_getnameinfo(const void* addr, | ||||||||||||||||||||||||||||||||||
| 3872 | std::size_t addrlen, char* host, std::size_t hostlen, char* serv, | ||||||||||||||||||||||||||||||||||
| 3873 | std::size_t servlen, int sock_type, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 3874 | { | ||||||||||||||||||||||||||||||||||
| 3875 | // First try resolving with the service name. If that fails try resolving | ||||||||||||||||||||||||||||||||||
| 3876 | // but allow the service to be returned as a number. | ||||||||||||||||||||||||||||||||||
| 3877 | int flags = (sock_type == SOCK_DGRAMSOCK_DGRAM) ? NI_DGRAM16 : 0; | ||||||||||||||||||||||||||||||||||
| 3878 | socket_ops::getnameinfo(addr, addrlen, host, | ||||||||||||||||||||||||||||||||||
| 3879 | hostlen, serv, servlen, flags, ec); | ||||||||||||||||||||||||||||||||||
| 3880 | if (ec) | ||||||||||||||||||||||||||||||||||
| 3881 | { | ||||||||||||||||||||||||||||||||||
| 3882 | socket_ops::getnameinfo(addr, addrlen, host, hostlen, | ||||||||||||||||||||||||||||||||||
| 3883 | serv, servlen, flags | NI_NUMERICSERV2, ec); | ||||||||||||||||||||||||||||||||||
| 3884 | } | ||||||||||||||||||||||||||||||||||
| 3885 | |||||||||||||||||||||||||||||||||||
| 3886 | return ec; | ||||||||||||||||||||||||||||||||||
| 3887 | } | ||||||||||||||||||||||||||||||||||
| 3888 | |||||||||||||||||||||||||||||||||||
| 3889 | boost::system::error_code background_getnameinfo( | ||||||||||||||||||||||||||||||||||
| 3890 | const weak_cancel_token_type& cancel_token, | ||||||||||||||||||||||||||||||||||
| 3891 | const void* addr, std::size_t addrlen, | ||||||||||||||||||||||||||||||||||
| 3892 | char* host, std::size_t hostlen, char* serv, | ||||||||||||||||||||||||||||||||||
| 3893 | std::size_t servlen, int sock_type, boost::system::error_code& ec) | ||||||||||||||||||||||||||||||||||
| 3894 | { | ||||||||||||||||||||||||||||||||||
| 3895 | if (cancel_token.expired()) | ||||||||||||||||||||||||||||||||||
| 3896 | { | ||||||||||||||||||||||||||||||||||
| 3897 | ec = boost::asio::error::operation_aborted; | ||||||||||||||||||||||||||||||||||
| 3898 | } | ||||||||||||||||||||||||||||||||||
| 3899 | else | ||||||||||||||||||||||||||||||||||
| 3900 | { | ||||||||||||||||||||||||||||||||||
| 3901 | // First try resolving with the service name. If that fails try resolving | ||||||||||||||||||||||||||||||||||
| 3902 | // but allow the service to be returned as a number. | ||||||||||||||||||||||||||||||||||
| 3903 | int flags = (sock_type == SOCK_DGRAMSOCK_DGRAM) ? NI_DGRAM16 : 0; | ||||||||||||||||||||||||||||||||||
| 3904 | socket_ops::getnameinfo(addr, addrlen, host, | ||||||||||||||||||||||||||||||||||
| 3905 | hostlen, serv, servlen, flags, ec); | ||||||||||||||||||||||||||||||||||
| 3906 | if (ec) | ||||||||||||||||||||||||||||||||||
| 3907 | { | ||||||||||||||||||||||||||||||||||
| 3908 | socket_ops::getnameinfo(addr, addrlen, host, hostlen, | ||||||||||||||||||||||||||||||||||
| 3909 | serv, servlen, flags | NI_NUMERICSERV2, ec); | ||||||||||||||||||||||||||||||||||
| 3910 | } | ||||||||||||||||||||||||||||||||||
| 3911 | } | ||||||||||||||||||||||||||||||||||
| 3912 | |||||||||||||||||||||||||||||||||||
| 3913 | return ec; | ||||||||||||||||||||||||||||||||||
| 3914 | } | ||||||||||||||||||||||||||||||||||
| 3915 | |||||||||||||||||||||||||||||||||||
| 3916 | #endif // !defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3917 | |||||||||||||||||||||||||||||||||||
| 3918 | u_long_type network_to_host_long(u_long_type value) | ||||||||||||||||||||||||||||||||||
| 3919 | { | ||||||||||||||||||||||||||||||||||
| 3920 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3921 | unsigned char* value_p = reinterpret_cast<unsigned char*>(&value); | ||||||||||||||||||||||||||||||||||
| 3922 | u_long_type result = (static_cast<u_long_type>(value_p[0]) << 24) | ||||||||||||||||||||||||||||||||||
| 3923 | | (static_cast<u_long_type>(value_p[1]) << 16) | ||||||||||||||||||||||||||||||||||
| 3924 | | (static_cast<u_long_type>(value_p[2]) << 8) | ||||||||||||||||||||||||||||||||||
| 3925 | | static_cast<u_long_type>(value_p[3]); | ||||||||||||||||||||||||||||||||||
| 3926 | return result; | ||||||||||||||||||||||||||||||||||
| 3927 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3928 | return ntohl(value)__bswap_32 (value); | ||||||||||||||||||||||||||||||||||
| 3929 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3930 | } | ||||||||||||||||||||||||||||||||||
| 3931 | |||||||||||||||||||||||||||||||||||
| 3932 | u_long_type host_to_network_long(u_long_type value) | ||||||||||||||||||||||||||||||||||
| 3933 | { | ||||||||||||||||||||||||||||||||||
| 3934 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3935 | u_long_type result; | ||||||||||||||||||||||||||||||||||
| 3936 | unsigned char* result_p = reinterpret_cast<unsigned char*>(&result); | ||||||||||||||||||||||||||||||||||
| 3937 | result_p[0] = static_cast<unsigned char>((value >> 24) & 0xFF); | ||||||||||||||||||||||||||||||||||
| 3938 | result_p[1] = static_cast<unsigned char>((value >> 16) & 0xFF); | ||||||||||||||||||||||||||||||||||
| 3939 | result_p[2] = static_cast<unsigned char>((value >> 8) & 0xFF); | ||||||||||||||||||||||||||||||||||
| 3940 | result_p[3] = static_cast<unsigned char>(value & 0xFF); | ||||||||||||||||||||||||||||||||||
| 3941 | return result; | ||||||||||||||||||||||||||||||||||
| 3942 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3943 | return htonl(value)__bswap_32 (value); | ||||||||||||||||||||||||||||||||||
| 3944 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3945 | } | ||||||||||||||||||||||||||||||||||
| 3946 | |||||||||||||||||||||||||||||||||||
| 3947 | u_short_type network_to_host_short(u_short_type value) | ||||||||||||||||||||||||||||||||||
| 3948 | { | ||||||||||||||||||||||||||||||||||
| 3949 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3950 | unsigned char* value_p = reinterpret_cast<unsigned char*>(&value); | ||||||||||||||||||||||||||||||||||
| 3951 | u_short_type result = (static_cast<u_short_type>(value_p[0]) << 8) | ||||||||||||||||||||||||||||||||||
| 3952 | | static_cast<u_short_type>(value_p[1]); | ||||||||||||||||||||||||||||||||||
| 3953 | return result; | ||||||||||||||||||||||||||||||||||
| 3954 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3955 | return ntohs(value)__bswap_16 (value); | ||||||||||||||||||||||||||||||||||
| 3956 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3957 | } | ||||||||||||||||||||||||||||||||||
| 3958 | |||||||||||||||||||||||||||||||||||
| 3959 | u_short_type host_to_network_short(u_short_type value) | ||||||||||||||||||||||||||||||||||
| 3960 | { | ||||||||||||||||||||||||||||||||||
| 3961 | #if defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3962 | u_short_type result; | ||||||||||||||||||||||||||||||||||
| 3963 | unsigned char* result_p = reinterpret_cast<unsigned char*>(&result); | ||||||||||||||||||||||||||||||||||
| 3964 | result_p[0] = static_cast<unsigned char>((value >> 8) & 0xFF); | ||||||||||||||||||||||||||||||||||
| 3965 | result_p[1] = static_cast<unsigned char>(value & 0xFF); | ||||||||||||||||||||||||||||||||||
| 3966 | return result; | ||||||||||||||||||||||||||||||||||
| 3967 | #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3968 | return htons(value)__bswap_16 (value); | ||||||||||||||||||||||||||||||||||
| 3969 | #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) | ||||||||||||||||||||||||||||||||||
| 3970 | } | ||||||||||||||||||||||||||||||||||
| 3971 | |||||||||||||||||||||||||||||||||||
| 3972 | } // namespace socket_ops | ||||||||||||||||||||||||||||||||||
| 3973 | } // namespace detail | ||||||||||||||||||||||||||||||||||
| 3974 | } // namespace asio | ||||||||||||||||||||||||||||||||||
| 3975 | } // namespace boost | ||||||||||||||||||||||||||||||||||
| 3976 | |||||||||||||||||||||||||||||||||||
| 3977 | #include <boost/asio/detail/pop_options.hpp> | ||||||||||||||||||||||||||||||||||
| 3978 | |||||||||||||||||||||||||||||||||||
| 3979 | #endif // BOOST_ASIO_DETAIL_SOCKET_OPS_IPP |