Skip to content

Commit a58e26c

Browse files
committed
Network: Code cleaning
1 parent 25390c5 commit a58e26c

4 files changed

Lines changed: 172 additions & 8 deletions

File tree

modules/Network/Client.mpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module;
66
# include <winsock2.h>
77
#elif defined(OS_LINUX) or defined(OS_MACOS)
88
# include <sys/socket.h>
9+
# include <cerrno>
910
#endif
1011

1112
export module CppUtils.Network.Client;
@@ -33,9 +34,15 @@ export namespace CppUtils::Network
3334
if (nativeHandle() == -1)
3435
#endif
3536
throw std::runtime_error{"CppUtils::Network::Client::connect: Invalid socket handle"};
36-
throwErrno(
37-
::connect(nativeHandle(), reinterpret_cast<sockaddr*>(std::addressof(address.storage)), address.length),
38-
"CppUtils::Network::Client::connect");
37+
if (::connect(nativeHandle(), reinterpret_cast<sockaddr*>(std::addressof(address.storage)), address.length) < 0)
38+
{
39+
#if defined(OS_WINDOWS)
40+
if (WSAGetLastError() != WSAEWOULDBLOCK)
41+
#elif defined(OS_LINUX) or defined(OS_MACOS)
42+
if (errno != EINPROGRESS)
43+
#endif
44+
throwErrno(-1, "CppUtils::Network::Client::connect");
45+
}
3946
}
4047
};
4148
}

modules/Network/Server.mpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export namespace CppUtils::Network
5353
return std::nullopt;
5454
throw std::runtime_error{"CppUtils::Network::Server::accept: Accept failed"};
5555
}
56-
#elif defined(OS_LINUX)
56+
#elif defined(OS_LINUX) or defined(OS_MACOS)
5757
if (clientSocket < 0)
5858
{
5959
if (errno == EAGAIN or errno == EWOULDBLOCK)

modules/Network/Socket.mpp

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,26 @@ export namespace CppUtils::Network
243243
return result > 0;
244244
}
245245

246+
template<class T>
247+
[[nodiscard]] inline auto getSocketOption(int level, int option) const -> T
248+
{
249+
auto value = T{};
250+
if (not getSocketOption(m_socket, level, option, std::addressof(value), sizeof(T)))
251+
throw std::runtime_error{"CppUtils::Network::Socket::getSocketOption: Failed to get socket option"};
252+
return value;
253+
}
254+
255+
[[nodiscard]] inline auto hasPendingError() const -> std::optional<int>
256+
{
257+
auto error = 0;
258+
if (getSocketOption(m_socket, SOL_SOCKET, SO_ERROR, std::addressof(error), sizeof(error)))
259+
{
260+
if (error != 0)
261+
return error;
262+
}
263+
return std::nullopt;
264+
}
265+
246266
private:
247267
inline auto setSocketOption(NativeSocket socket, int level, int option, const void* value, std::size_t valueSize) const noexcept -> bool
248268
{
@@ -253,6 +273,17 @@ export namespace CppUtils::Network
253273
#endif
254274
}
255275

276+
[[nodiscard]] inline auto getSocketOption(NativeSocket socket, int level, int option, void* value, std::size_t valueSize) const noexcept -> bool
277+
{
278+
#if defined(OS_WINDOWS)
279+
auto length = static_cast<int>(valueSize);
280+
return ::getsockopt(socket, level, option, reinterpret_cast<char*>(value), std::addressof(length)) == 0;
281+
#elif defined(OS_LINUX) or defined(OS_MACOS)
282+
auto length = static_cast<socklen_t>(valueSize);
283+
return ::getsockopt(socket, level, option, value, std::addressof(length)) == 0;
284+
#endif
285+
}
286+
256287
protected:
257288
[[nodiscard]] inline auto nativeHandle() const noexcept -> NativeSocket
258289
{
@@ -374,9 +405,7 @@ export namespace CppUtils::Network
374405
#if defined(OS_WINDOWS)
375406
if (WSAGetLastError() == WSAEWOULDBLOCK)
376407
#elif defined(OS_LINUX) or defined(OS_MACOS)
377-
if (errno == EAGAIN)
378-
continue;
379-
if (errno == EWOULDBLOCK)
408+
if (errno == EAGAIN or errno == EWOULDBLOCK)
380409
#endif
381410
return std::nullopt;
382411
throw makeReceiveError();
@@ -407,7 +436,7 @@ export namespace CppUtils::Network
407436
#elif defined(OS_LINUX) or defined(OS_MACOS)
408437
if (errno == EAGAIN or errno == EWOULDBLOCK)
409438
#endif
410-
continue;
439+
return std::nullopt;
411440
throw makeReceiveError();
412441
}
413442
if (bytesReceived == 0)

tests/Network/Network.mpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,5 +220,133 @@ export namespace CppUtils::UnitTest::Network
220220
if (auto serverResult = serverListening.get(); not serverResult.has_value())
221221
throw serverResult.error();
222222
});
223+
224+
suite.addTest("isReadyForRead: Data available", [&] {
225+
auto serverisReady = std::atomic_bool{false};
226+
227+
auto serverListening = CppUtils::Thread::tryAsync([&] {
228+
auto server = CppUtils::Network::Server{};
229+
server.setReuseAddress(true);
230+
server.bind(8'080);
231+
server.listen();
232+
serverisReady.store(true, std::memory_order_release);
233+
serverisReady.notify_all();
234+
235+
auto clientSocket = server.accept().value();
236+
clientSocket.send(123);
237+
});
238+
239+
auto clientConnecting = CppUtils::Thread::tryAsync([&] {
240+
serverisReady.wait(false);
241+
auto client = CppUtils::Network::Client{};
242+
client.connect("", 8'080);
243+
client.setBlocking(false);
244+
245+
suite.expect(client.isReadyForRead(5s));
246+
auto data = client.receive<int>();
247+
suite.expect(data.has_value());
248+
suite.expectEqual(data.value(), 123);
249+
});
250+
251+
if (auto clientResult = clientConnecting.get(); not clientResult.has_value())
252+
throw clientResult.error();
253+
if (auto serverResult = serverListening.get(); not serverResult.has_value())
254+
throw serverResult.error();
255+
});
256+
257+
suite.addTest("isReadyForRead: No data (timeout)", [&] {
258+
auto serverisReady = std::atomic_bool{false};
259+
260+
auto serverListening = CppUtils::Thread::tryAsync([&] {
261+
auto server = CppUtils::Network::Server{};
262+
server.setReuseAddress(true);
263+
server.bind(8'080);
264+
server.listen();
265+
serverisReady.store(true, std::memory_order_release);
266+
serverisReady.notify_all();
267+
268+
[[maybe_unused]] auto clientSocket = server.accept().value();
269+
std::this_thread::sleep_for(1s);
270+
});
271+
272+
auto clientConnecting = CppUtils::Thread::tryAsync([&] {
273+
serverisReady.wait(false);
274+
auto client = CppUtils::Network::Client{};
275+
client.connect("", 8'080);
276+
client.setBlocking(false);
277+
std::this_thread::sleep_for(50ms);
278+
suite.expect(not client.isReadyForRead(100ms));
279+
});
280+
281+
if (auto clientResult = clientConnecting.get(); not clientResult.has_value())
282+
throw clientResult.error();
283+
if (auto serverResult = serverListening.get(); not serverResult.has_value())
284+
throw serverResult.error();
285+
});
286+
287+
suite.addTest("hasPendingError: No error", [&] {
288+
auto serverisReady = std::atomic_bool{false};
289+
290+
auto serverListening = CppUtils::Thread::tryAsync([&] {
291+
auto server = CppUtils::Network::Server{};
292+
server.setReuseAddress(true);
293+
server.bind(8'080);
294+
server.listen();
295+
serverisReady.store(true, std::memory_order_release);
296+
serverisReady.notify_all();
297+
298+
[[maybe_unused]] auto clientSocket = server.accept().value();
299+
});
300+
301+
auto clientConnecting = CppUtils::Thread::tryAsync([&] {
302+
serverisReady.wait(false);
303+
auto client = CppUtils::Network::Client{};
304+
client.connect("", 8'080);
305+
306+
suite.expect(not client.hasPendingError().has_value());
307+
});
308+
309+
if (auto clientResult = clientConnecting.get(); not clientResult.has_value())
310+
throw clientResult.error();
311+
if (auto serverResult = serverListening.get(); not serverResult.has_value())
312+
throw serverResult.error();
313+
});
314+
315+
suite.addTest("hasPendingError: With error", [&] {
316+
auto serverisReady = std::atomic_bool{false};
317+
auto serverClosedConnection = std::atomic_bool{false};
318+
319+
auto serverListening = CppUtils::Thread::tryAsync([&] {
320+
auto server = CppUtils::Network::Server{};
321+
server.setReuseAddress(true);
322+
server.bind(8'080);
323+
server.listen();
324+
serverisReady.store(true, std::memory_order_release);
325+
serverisReady.notify_all();
326+
327+
[[maybe_unused]] auto clientSocket = server.accept().value();
328+
serverClosedConnection.store(true, std::memory_order_release);
329+
serverClosedConnection.notify_all();
330+
});
331+
332+
auto clientConnecting = CppUtils::Thread::tryAsync([&] {
333+
serverisReady.wait(false);
334+
auto client = CppUtils::Network::Client{};
335+
client.connect("", 8'080);
336+
337+
serverClosedConnection.wait(false);
338+
suite.template expectThrow<std::runtime_error>([&] {
339+
client.send(1);
340+
});
341+
342+
std::this_thread::sleep_for(50ms);
343+
suite.expect(client.hasPendingError().has_value());
344+
});
345+
346+
if (auto clientResult = clientConnecting.get(); not clientResult.has_value())
347+
throw clientResult.error();
348+
if (auto serverResult = serverListening.get(); not serverResult.has_value())
349+
throw serverResult.error();
350+
});
223351
}};
224352
}

0 commit comments

Comments
 (0)