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-2024 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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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("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("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, "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 , "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, "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_service.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, "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, "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, "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, "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, "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, "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, "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, "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 , "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, "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 , "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, "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, "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, "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 , "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, "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, "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 , "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, "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 , "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, "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, "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, "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 , "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, "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, "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, "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, "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, "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, "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, "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, "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, "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, "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, "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 , "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, "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, "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, "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, "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, "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, "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, "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 , "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, "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, "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, "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 , "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, "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, "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, "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, "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, "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, "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 , "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, "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 , "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, "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, "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, "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 , "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, "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, "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, "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, "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, "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, "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, "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, "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, "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, "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, "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 , "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, "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, "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, "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; static ::testing::TestInfo* const test_info_ __attribute__((unused) ); }; ::testing::TestInfo* const MtTcpListenerMgrTest_basics_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "basics", nullptr, nullptr, ::testing::internal::CodeLocation ("mt_tcp_listener_mgr_unittests.cc", 759), (::testing::internal ::GetTypeId<MtTcpListenerMgrTest>()), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetSetUpCaseOrSuite ("mt_tcp_listener_mgr_unittests.cc", 759), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetTearDownCaseOrSuite ("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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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, "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 , "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 , "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, "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, "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, "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 , "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, "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, "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 , "mt_tcp_listener_mgr_unittests.cc", 790, gtest_ar.failure_message ()) = ::testing::Message(); } catch (...) { return ::testing:: internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "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, "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 , "mt_tcp_listener_mgr_unittests.cc", 790, gtest_ar.failure_message ()) = ::testing::Message(); } catch (...) { return ::testing:: internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "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, "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 , "mt_tcp_listener_mgr_unittests.cc", 790, gtest_ar.failure_message ()) = ::testing::Message(); } catch (...) { return ::testing:: internal::AssertHelper(::testing::TestPartResult::kFatalFailure , "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, "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 , "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, "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 , "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, "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 , "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, "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, "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, "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, "mt_tcp_listener_mgr_unittests.cc" , 808, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "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, "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, "mt_tcp_listener_mgr_unittests.cc" , 808, gtest_ar.failure_message()) = ::testing::Message(); } catch (...) { return ::testing::internal::AssertHelper(::testing:: TestPartResult::kFatalFailure, "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, "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 , "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, "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 , "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, "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, "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 , "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, "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 , "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, "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 , "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, "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, "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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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 , "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 , "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 , "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, "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, "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, "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, "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 , "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, "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 , "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 , "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, "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, "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, "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 , "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, "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 , "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 , "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, "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, "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, "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 , "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, "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 , "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 , "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, "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, "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; static ::testing::TestInfo* const test_info_ __attribute__((unused) ); }; ::testing::TestInfo* const MtTcpListenerMgrTest_oneByOne_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "oneByOne", nullptr, nullptr, ::testing::internal::CodeLocation ("mt_tcp_listener_mgr_unittests.cc", 876), (::testing::internal ::GetTypeId<MtTcpListenerMgrTest>()), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetSetUpCaseOrSuite ("mt_tcp_listener_mgr_unittests.cc", 876), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetTearDownCaseOrSuite ("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; static ::testing::TestInfo* const test_info_ __attribute__((unused) ); }; ::testing::TestInfo* const MtTcpListenerMgrTest_oneByFour_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "oneByFour", nullptr, nullptr, ::testing::internal::CodeLocation ("mt_tcp_listener_mgr_unittests.cc", 883), (::testing::internal ::GetTypeId<MtTcpListenerMgrTest>()), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetSetUpCaseOrSuite ("mt_tcp_listener_mgr_unittests.cc", 883), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetTearDownCaseOrSuite ("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; static ::testing::TestInfo* const test_info_ __attribute__((unused) ); }; ::testing::TestInfo* const MtTcpListenerMgrTest_fourByOne_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "fourByOne", nullptr, nullptr, ::testing::internal::CodeLocation ("mt_tcp_listener_mgr_unittests.cc", 890), (::testing::internal ::GetTypeId<MtTcpListenerMgrTest>()), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetSetUpCaseOrSuite ("mt_tcp_listener_mgr_unittests.cc", 890), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetTearDownCaseOrSuite ("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; static ::testing::TestInfo* const test_info_ __attribute__((unused) ); }; ::testing::TestInfo* const MtTcpListenerMgrTest_fourByFour_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "fourByFour", nullptr, nullptr, ::testing::internal::CodeLocation ("mt_tcp_listener_mgr_unittests.cc", 897), (::testing::internal ::GetTypeId<MtTcpListenerMgrTest>()), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetSetUpCaseOrSuite ("mt_tcp_listener_mgr_unittests.cc", 897), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetTearDownCaseOrSuite ("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; static ::testing::TestInfo* const test_info_ __attribute__((unused) ); }; ::testing::TestInfo* const MtTcpListenerMgrTest_fourByEight_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "fourByEight", nullptr, nullptr, ::testing::internal::CodeLocation ("mt_tcp_listener_mgr_unittests.cc", 904), (::testing::internal ::GetTypeId<MtTcpListenerMgrTest>()), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetSetUpCaseOrSuite ("mt_tcp_listener_mgr_unittests.cc", 904), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetTearDownCaseOrSuite ("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; static ::testing::TestInfo* const test_info_ __attribute__((unused) ); }; ::testing::TestInfo* const MtTcpListenerMgrTest_sixByEighteen_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "sixByEighteen", nullptr, nullptr, ::testing::internal::CodeLocation ("mt_tcp_listener_mgr_unittests.cc", 911), (::testing::internal ::GetTypeId<MtTcpListenerMgrTest>()), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetSetUpCaseOrSuite ("mt_tcp_listener_mgr_unittests.cc", 911), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetTearDownCaseOrSuite ("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; static ::testing::TestInfo* const test_info_ __attribute__((unused) ); }; ::testing::TestInfo* const MtTcpListenerMgrTest_pauseAndResume_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "pauseAndResume", nullptr, nullptr, ::testing::internal::CodeLocation ("mt_tcp_listener_mgr_unittests.cc", 919), (::testing::internal ::GetTypeId<MtTcpListenerMgrTest>()), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetSetUpCaseOrSuite ("mt_tcp_listener_mgr_unittests.cc", 919), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetTearDownCaseOrSuite ("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 ; static ::testing::TestInfo* const test_info_ __attribute__( (unused)); }; ::testing::TestInfo* const MtTcpListenerMgrTest_tls_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "tls", nullptr, nullptr, ::testing::internal::CodeLocation( "mt_tcp_listener_mgr_unittests.cc", 927), (::testing::internal ::GetTypeId<MtTcpListenerMgrTest>()), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetSetUpCaseOrSuite ("mt_tcp_listener_mgr_unittests.cc", 927), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetTearDownCaseOrSuite ("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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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 , "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, "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 , "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 , "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, "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, "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 , "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, "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 , "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, "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 , "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, "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, "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, "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 , "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, "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 , "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 , "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, "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, "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; static ::testing::TestInfo* const test_info_ __attribute__((unused) ); }; ::testing::TestInfo* const MtTcpListenerMgrTest_idleTimeout_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "MtTcpListenerMgrTest" , "idleTimeout", nullptr, nullptr, ::testing::internal::CodeLocation ("mt_tcp_listener_mgr_unittests.cc", 966), (::testing::internal ::GetTypeId<MtTcpListenerMgrTest>()), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetSetUpCaseOrSuite ("mt_tcp_listener_mgr_unittests.cc", 966), ::testing::internal ::SuiteApiResolver< MtTcpListenerMgrTest>::GetTearDownCaseOrSuite ("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 , "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 , "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, "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 , "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, "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, "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 , "mt_tcp_listener_mgr_unittests.cc", 984, gtest_ar.failure_message ()) = ::testing::Message(); | |||
985 | } | |||
986 | ||||
987 | } // end of anonymous namespace |
1 | // Copyright (C) 2022-2024 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::address::from_string(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, "./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 socket_cb( |
140 | [this](boost::system::error_code ec, size_t /*length */) { |
141 | if (ec) { |
142 | handshake_failed_ = true; |
143 | done_callback_(); |
144 | } else { |
145 | sendNextRequest(); |
146 | } |
147 | }); |
148 | |
149 | tls_socket_->handshake(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 , "./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, "./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, "./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, "./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-2024 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 | #if BOOST_VERSION108300 < 106600 |
77 | return (socket_.native()); |
78 | #else |
79 | return (socket_.native_handle()); |
80 | #endif |
81 | } |
82 | |
83 | /// \brief Return protocol of socket |
84 | virtual int getProtocol() const { |
85 | return (IPPROTO_TCPIPPROTO_TCP); |
86 | } |
87 | |
88 | /// \brief Is "open()" synchronous? |
89 | /// |
90 | /// Indicates that the opening of a TCP socket is asynchronous. |
91 | virtual bool isOpenSynchronous() const { |
92 | return (false); |
93 | } |
94 | |
95 | /// \brief Checks if the connection is usable. |
96 | /// |
97 | /// The connection is usable if the socket is open and the peer has not |
98 | /// closed its connection. |
99 | /// |
100 | /// \return true if the connection is usable. |
101 | bool isUsable() const { |
102 | // If the socket is open it doesn't mean that it is still usable. The connection |
103 | // could have been closed on the other end. We have to check if we can still |
104 | // use this socket. |
105 | if (socket_.is_open()) { |
106 | // Remember the current non blocking setting. |
107 | const bool non_blocking_orig = socket_.non_blocking(); |
108 | // Set the socket to non blocking mode. We're going to test if the socket |
109 | // returns would_block status on the attempt to read from it. |
110 | socket_.non_blocking(true); |
111 | |
112 | boost::system::error_code ec; |
113 | char data[2]; |
114 | |
115 | // Use receive with message peek flag to avoid removing the data awaiting |
116 | // to be read. |
117 | socket_.receive(boost::asio::buffer(data, sizeof(data)), |
118 | boost::asio::socket_base::message_peek, |
119 | ec); |
120 | |
121 | // Revert the original non_blocking flag on the socket. |
122 | socket_.non_blocking(non_blocking_orig); |
123 | |
124 | // If the connection is alive we'd typically get would_block status code. |
125 | // If there are any data that haven't been read we may also get success |
126 | // status. We're guessing that try_again may also be returned by some |
127 | // implementations in some situations. Any other error code indicates a |
128 | // problem with the connection so we assume that the connection has been |
129 | // closed. |
130 | return (!ec || (ec.value() == boost::asio::error::try_again) || |
131 | (ec.value() == boost::asio::error::would_block)); |
132 | } |
133 | |
134 | return (false); |
135 | } |
136 | |
137 | /// \brief Open Socket |
138 | /// |
139 | /// Opens the TCP socket. This is an asynchronous operation, completion of |
140 | /// which will be signalled via a call to the callback function. |
141 | /// |
142 | /// \param endpoint Endpoint to which the socket will connect. |
143 | /// \param callback Callback object. |
144 | virtual void open(const IOEndpoint* endpoint, C& callback); |
145 | |
146 | /// \brief Send Asynchronously |
147 | /// |
148 | /// Calls the underlying socket's async_send() method to send a packet of |
149 | /// data asynchronously to the remote endpoint. The callback will be called |
150 | /// on completion. |
151 | /// |
152 | /// \param data Data to send |
153 | /// \param length Length of data to send |
154 | /// \param endpoint Target of the send. (Unused for a TCP socket because |
155 | /// that was determined when the connection was opened.) |
156 | /// \param callback Callback object. |
157 | /// \throw BufferTooLarge on attempt to send a buffer larger than 64kB. |
158 | virtual void asyncSend(const void* data, size_t length, |
159 | const IOEndpoint* endpoint, C& callback); |
160 | |
161 | /// \brief Send Asynchronously without count. |
162 | /// |
163 | /// This variant of the method sends data over the TCP socket without |
164 | /// preceding the data with a data count. Eventually, we should migrate |
165 | /// the virtual method to not insert the count but there are existing |
166 | /// classes using the count. Once this migration is done, the existing |
167 | /// virtual method should be replaced by this method. |
168 | /// |
169 | /// \param data Data to send |
170 | /// \param length Length of data to send |
171 | /// \param callback Callback object. |
172 | /// \throw BufferTooLarge on attempt to send a buffer larger than 64kB. |
173 | void asyncSend(const void* data, size_t length, C& callback); |
174 | |
175 | /// \brief Receive Asynchronously |
176 | /// |
177 | /// Calls the underlying socket's async_receive() method to read a packet |
178 | /// of data from a remote endpoint. Arrival of the data is signalled via a |
179 | /// call to the callback function. |
180 | /// |
181 | /// \param data Buffer to receive incoming message |
182 | /// \param length Length of the data buffer |
183 | /// \param offset Offset into buffer where data is to be put |
184 | /// \param endpoint Source of the communication |
185 | /// \param callback Callback object |
186 | virtual void asyncReceive(void* data, size_t length, size_t offset, |
187 | IOEndpoint* endpoint, C& callback); |
188 | |
189 | /// \brief Process received data packet |
190 | /// |
191 | /// See the description of IOAsioSocket::receiveComplete for a complete |
192 | /// description of this method. |
193 | /// |
194 | /// \param staging Pointer to the start of the staging buffer. |
195 | /// \param length Amount of data in the staging buffer. |
196 | /// \param cumulative Amount of data received before the staging buffer is |
197 | /// processed. |
198 | /// \param offset Unused. |
199 | /// \param expected unused. |
200 | /// \param buff Output buffer. Data in the staging buffer is be copied |
201 | /// to this output buffer in the call. |
202 | /// |
203 | /// \return Always true |
204 | virtual bool processReceivedData(const void* staging, size_t length, |
205 | size_t& cumulative, size_t& offset, |
206 | size_t& expected, |
207 | isc::util::OutputBufferPtr& buff); |
208 | |
209 | /// \brief Cancel I/O On Socket |
210 | virtual void cancel(); |
211 | |
212 | /// \brief Close socket |
213 | virtual void close(); |
214 | |
215 | /// \brief Returns reference to the underlying ASIO socket. |
216 | /// |
217 | /// \return Reference to underlying ASIO socket. |
218 | virtual boost::asio::ip::tcp::socket& getASIOSocket() const { |
219 | return (socket_); |
220 | } |
221 | |
222 | private: |
223 | |
224 | /// @brief The IO service used to handle events. |
225 | IOServicePtr io_service_; |
226 | |
227 | /// Two variables to hold the socket - a socket and a pointer to it. This |
228 | /// handles the case where a socket is passed to the TCPSocket on |
229 | /// construction, or where it is asked to manage its own socket. |
230 | |
231 | /// Pointer to own socket |
232 | std::unique_ptr<boost::asio::ip::tcp::socket> socket_ptr_; |
233 | |
234 | /// Socket |
235 | boost::asio::ip::tcp::socket& socket_; |
236 | |
237 | /// @todo Remove temporary buffer |
238 | /// The current implementation copies the buffer passed to asyncSend() into |
239 | /// a temporary buffer and precedes it with a two-byte count field. As |
240 | /// ASIO should really be just about sending and receiving data, the TCP |
241 | /// code should not do this. If the protocol using this requires a two-byte |
242 | /// count, it should add it before calling this code. (This may be best |
243 | /// achieved by altering isc::dns::buffer to have pairs of methods: |
244 | /// getLength()/getTCPLength(), getData()/getTCPData(), with the getTCPXxx() |
245 | /// methods taking into account a two-byte count field.) |
246 | /// |
247 | /// The option of sending the data in two operations, the count followed by |
248 | /// the data was discounted as that would lead to two callbacks which would |
249 | /// cause problems with the stackless coroutine code. |
250 | |
251 | /// Send buffer |
252 | isc::util::OutputBufferPtr send_buffer_; |
253 | }; |
254 | |
255 | // Constructor - caller manages socket |
256 | |
257 | template <typename C> |
258 | TCPSocket<C>::TCPSocket(boost::asio::ip::tcp::socket& socket) : |
259 | socket_ptr_(), socket_(socket), send_buffer_() { |
260 | } |
261 | |
262 | // Constructor - create socket on the fly |
263 | |
264 | template <typename C> |
265 | TCPSocket<C>::TCPSocket(const IOServicePtr& io_service) : io_service_(io_service), |
266 | socket_ptr_(new boost::asio::ip::tcp::socket(io_service_->getInternalIOService())), |
267 | socket_(*socket_ptr_) { |
268 | } |
269 | |
270 | // Destructor. |
271 | |
272 | template <typename C> |
273 | TCPSocket<C>::~TCPSocket() { |
274 | close(); |
275 | } |
276 | |
277 | // Open the socket. |
278 | |
279 | template <typename C> void |
280 | TCPSocket<C>::open(const IOEndpoint* endpoint, C& callback) { |
281 | // If socket is open on this end but has been closed by the peer, |
282 | // we need to reconnect. |
283 | if (socket_.is_open() && !isUsable()) { |
284 | close(); |
285 | } |
286 | // Ignore opens on already-open socket. Don't throw a failure because |
287 | // of uncertainties as to what precedes when using asynchronous I/O. |
288 | // Also allows us a treat a passed-in socket as a self-managed socket. |
289 | if (!socket_.is_open()) { |
290 | if (endpoint->getFamily() == AF_INET2) { |
291 | socket_.open(boost::asio::ip::tcp::v4()); |
292 | } else { |
293 | socket_.open(boost::asio::ip::tcp::v6()); |
294 | } |
295 | |
296 | // Set options on the socket: |
297 | |
298 | // Reuse address - allow the socket to bind to a port even if the port |
299 | // is in the TIMED_WAIT state. |
300 | socket_.set_option(boost::asio::socket_base::reuse_address(true)); |
301 | } |
302 | |
303 | // Upconvert to a TCPEndpoint. We need to do this because although |
304 | // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it does not |
305 | // contain a method for getting at the underlying endpoint type - that is in |
306 | /// the derived class and the two classes differ on return type. |
307 | 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" << ":" << 307 << " (" << "endpoint->getProtocol() == IPPROTO_TCP" << ") failed"; throw isc::Unexpected("../../../../src/lib/asiolink/tcp_socket.h" , 307, oss__.str().c_str()); } while (1); }}; |
308 | const TCPEndpoint* tcp_endpoint = |
309 | static_cast<const TCPEndpoint*>(endpoint); |
310 | |
311 | // Connect to the remote endpoint. On success, the handler will be |
312 | // called (with one argument - the length argument will default to |
313 | // zero). |
314 | socket_.async_connect(tcp_endpoint->getASIOEndpoint(), callback); |
315 | } |
316 | |
317 | // Send a message. Should never do this if the socket is not open, so throw |
318 | // an exception if this is the case. |
319 | |
320 | template <typename C> void |
321 | TCPSocket<C>::asyncSend(const void* data, size_t length, C& callback) { |
322 | if (socket_.is_open()) { |
323 | |
324 | try { |
325 | send_buffer_.reset(new isc::util::OutputBuffer(length)); |
326 | send_buffer_->writeData(data, length); |
327 | |
328 | // Send the data. |
329 | socket_.async_send(boost::asio::buffer(send_buffer_->getData(), |
330 | send_buffer_->getLength()), |
331 | callback); |
332 | } catch (const boost::numeric::bad_numeric_cast&) { |
333 | isc_throw(BufferTooLarge,do { std::ostringstream oss__; oss__ << "attempt to send buffer larger than 64kB" ; throw BufferTooLarge("../../../../src/lib/asiolink/tcp_socket.h" , 334, oss__.str().c_str()); } while (1) |
334 | "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" , 334, oss__.str().c_str()); } while (1); |
335 | } |
336 | |
337 | } else { |
338 | 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" , 339, oss__.str().c_str()); } while (1) |
339 | "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" , 339, oss__.str().c_str()); } while (1); |
340 | } |
341 | } |
342 | |
343 | template <typename C> void |
344 | TCPSocket<C>::asyncSend(const void* data, size_t length, |
345 | const IOEndpoint*, C& callback) { |
346 | if (socket_.is_open()) { |
347 | |
348 | /// Need to copy the data into a temporary buffer and precede it with |
349 | /// a two-byte count field. |
350 | /// @todo arrange for the buffer passed to be preceded by the count |
351 | try { |
352 | /// Ensure it fits into 16 bits |
353 | uint16_t count = boost::numeric_cast<uint16_t>(length); |
354 | |
355 | /// Copy data into a buffer preceded by the count field. |
356 | send_buffer_.reset(new isc::util::OutputBuffer(length + 2)); |
357 | send_buffer_->writeUint16(count); |
358 | send_buffer_->writeData(data, length); |
359 | |
360 | /// ... and send it |
361 | socket_.async_send(boost::asio::buffer(send_buffer_->getData(), |
362 | send_buffer_->getLength()), callback); |
363 | } catch (const boost::numeric::bad_numeric_cast&) { |
364 | isc_throw(BufferTooLarge,do { std::ostringstream oss__; oss__ << "attempt to send buffer larger than 64kB" ; throw BufferTooLarge("../../../../src/lib/asiolink/tcp_socket.h" , 365, oss__.str().c_str()); } while (1) |
365 | "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" , 365, oss__.str().c_str()); } while (1); |
366 | } |
367 | |
368 | } else { |
369 | 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" , 370, oss__.str().c_str()); } while (1) |
370 | "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" , 370, oss__.str().c_str()); } while (1); |
371 | } |
372 | } |
373 | |
374 | // Receive a message. Note that the "offset" argument is used as an index |
375 | // into the buffer in order to decide where to put the data. It is up to the |
376 | // caller to initialize the data to zero |
377 | template <typename C> void |
378 | TCPSocket<C>::asyncReceive(void* data, size_t length, size_t offset, |
379 | IOEndpoint* endpoint, C& callback) { |
380 | if (socket_.is_open()) { |
381 | // Upconvert to a TCPEndpoint. We need to do this because although |
382 | // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it |
383 | // does not contain a method for getting at the underlying endpoint |
384 | // type - that is in the derived class and the two classes differ on |
385 | // return type. |
386 | 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" << ":" << 386 << " (" << "endpoint->getProtocol() == IPPROTO_TCP" << ") failed"; throw isc::Unexpected("../../../../src/lib/asiolink/tcp_socket.h" , 386, oss__.str().c_str()); } while (1); }}; |
387 | TCPEndpoint* tcp_endpoint = static_cast<TCPEndpoint*>(endpoint); |
388 | |
389 | // Write the endpoint details from the communications link. Ideally |
390 | // we should make IOEndpoint assignable, but this runs in to all sorts |
391 | // of problems concerning the management of the underlying Boost |
392 | // endpoint (e.g. if it is not self-managed, is the copied one |
393 | // self-managed?) The most pragmatic solution is to let Boost take care |
394 | // of everything and copy details of the underlying endpoint. |
395 | tcp_endpoint->getASIOEndpoint() = socket_.remote_endpoint(); |
396 | |
397 | // Ensure we can write into the buffer and if so, set the pointer to |
398 | // where the data will be written. |
399 | if (offset >= length) { |
400 | 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" , 401, oss__.str().c_str()); } while (1) |
401 | "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" , 401, oss__.str().c_str()); } while (1); |
402 | } |
403 | void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset); |
404 | |
405 | // ... and kick off the read. |
406 | socket_.async_receive(boost::asio::buffer(buffer_start, length - offset), callback); |
407 | |
408 | } else { |
409 | 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" , 410, oss__.str().c_str()); } while (1) |
410 | "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" , 410, oss__.str().c_str()); } while (1); |
411 | } |
412 | } |
413 | |
414 | // Is the receive complete? |
415 | |
416 | template <typename C> bool |
417 | TCPSocket<C>::processReceivedData(const void* staging, size_t length, |
418 | size_t& cumulative, size_t& offset, |
419 | size_t& expected, |
420 | isc::util::OutputBufferPtr& outbuff) { |
421 | // Point to the data in the staging buffer and note how much there is. |
422 | const uint8_t* data = static_cast<const uint8_t*>(staging); |
423 | size_t data_length = length; |
424 | |
425 | // Is the number is "expected" valid? It won't be unless we have received |
426 | // at least two bytes of data in total for this set of receives. |
427 | if (cumulative < 2) { |
428 | |
429 | // "expected" is not valid. Did this read give us enough data to |
430 | // work it out? |
431 | cumulative += length; |
432 | if (cumulative < 2) { |
433 | |
434 | // Nope, still not valid. This must have been the first packet and |
435 | // was only one byte long. Tell the fetch code to read the next |
436 | // packet into the staging buffer beyond the data that is already |
437 | // there so that the next time we are called we have a complete |
438 | // TCP count. |
439 | offset = cumulative; |
440 | return (false); |
441 | } |
442 | |
443 | // Have enough data to interpret the packet count, so do so now. |
444 | expected = isc::util::readUint16(data, cumulative); |
445 | |
446 | // We have two bytes less of data to process. Point to the start of the |
447 | // data and adjust the packet size. Note that at this point, |
448 | // "cumulative" is the true amount of data in the staging buffer, not |
449 | // "length". |
450 | data += 2; |
451 | data_length = cumulative - 2; |
452 | } else { |
453 | |
454 | // Update total amount of data received. |
455 | cumulative += length; |
456 | } |
457 | |
458 | // Regardless of anything else, the next read goes into the start of the |
459 | // staging buffer. |
460 | offset = 0; |
461 | |
462 | // Work out how much data we still have to put in the output buffer. (This |
463 | // could be zero if we have just interpreted the TCP count and that was |
464 | // set to zero.) |
465 | if (expected >= outbuff->getLength()) { |
466 | |
467 | // Still need data in the output packet. Copy what we can from the |
468 | // staging buffer to the output buffer. |
469 | size_t copy_amount = std::min(expected - outbuff->getLength(), data_length); |
470 | outbuff->writeData(data, copy_amount); |
471 | } |
472 | |
473 | // We can now say if we have all the data. |
474 | return (expected == outbuff->getLength()); |
475 | } |
476 | |
477 | // Cancel I/O on the socket. No-op if the socket is not open. |
478 | |
479 | template <typename C> void |
480 | TCPSocket<C>::cancel() { |
481 | if (socket_.is_open()) { |
482 | socket_.cancel(); |
483 | } |
484 | } |
485 | |
486 | // Close the socket down. Can only do this if the socket is open and we are |
487 | // managing it ourself. |
488 | |
489 | template <typename C> void |
490 | TCPSocket<C>::close() { |
491 | if (socket_.is_open() && socket_ptr_) { |
492 | socket_.close(); |
493 | } |
494 | } |
495 | |
496 | } // namespace asiolink |
497 | } // namespace isc |
498 | |
499 | #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,::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> |
497 | std::size_t))::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> 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,::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> |
571 | std::size_t))::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> 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,::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> |
749 | std::size_t))::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> 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,::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> |
825 | std::size_t))::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> 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,::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> |
957 | std::size_t))::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> 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,::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> |
1093 | std::size_t))::boost::asio::completion_token_for<void (boost::system::error_code , std::size_t)> 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))::boost::asio::completion_token_for<void (boost::system::error_code )> |
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))::boost::asio::completion_token_for<void (boost::system::error_code )> |
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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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 (::std::source_location::current()); (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)); | ||||||||||||||||||||||||||||||||||
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)); | ||||||||||||||||||||||||||||||||||
3637 | #elif defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
3638 | sprintf_s(serv, servlen, "%u", ntohs(port)); | ||||||||||||||||||||||||||||||||||
3639 | #else // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
3640 | sprintf(serv, "%u", ntohs(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, { 0, 0 } } }; | ||||||||||||||||||||||||||||||||||
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)); | ||||||||||||||||||||||||||||||||||
3662 | #elif defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
3663 | sprintf_s(serv, servlen, "%u", ntohs(port)); | ||||||||||||||||||||||||||||||||||
3664 | #else // defined(BOOST_ASIO_HAS_SECURE_RTL) | ||||||||||||||||||||||||||||||||||
3665 | sprintf(serv, "%u", ntohs(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); | ||||||||||||||||||||||||||||||||||
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); | ||||||||||||||||||||||||||||||||||
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); | ||||||||||||||||||||||||||||||||||
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); | ||||||||||||||||||||||||||||||||||
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 |