Files
c3c/lib/std/net/socket_private.c3
Christopher Coverdale c10d449e43 Add local TcpSocketPair (#2526)
* Add extern fn socketpair() to posix
* Add extern fn getsockname() for local socketpair loopback in windows
* Add local TcpSocketPair
* Add unit test for TcpSocketPair
* Add implicit wsa startup

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-10-27 13:16:14 +01:00

130 lines
3.0 KiB
Plaintext

module std::net @if(os::SUPPORTS_INET);
import std::time, libc, std::os;
import std::core::env;
import std::net::os;
macro void? apply_sockoptions(sockfd, options) @private
{
Socket sock = { .sock = sockfd };
foreach (o : options) sock.set_option(o, true)!;
}
fn Socket? connect_from_addrinfo(AddrInfo* addrinfo, SocketOption[] options) @private
{
$if env::WIN32:
os::start_wsa()!;
$endif
@loop_over_ai(addrinfo; NativeSocket sockfd, AddrInfo* ai)
{
apply_sockoptions(sockfd, options)!;
int errcode = os::connect(sockfd, ai.ai_addr, ai.ai_addrlen);
// Keep the first successful connection.
if (!errcode) return new_socket(sockfd, ai);
};
return os::socket_error()?;
}
fn bool last_error_is_delayed_connect()
{
$switch:
$case env::WIN32:
switch (win32::wsaGetLastError())
{
case wsa::EWOULDBLOCK:
case wsa::EINPROGRESS: return true;
default: return false;
}
$default:
Errno err = libc::errno();
return err == errno::EINPROGRESS || err == errno::EAGAIN || err == errno::EWOULDBLOCK;
$endswitch
}
fn Socket? connect_with_timeout_from_addrinfo(AddrInfo* addrinfo, SocketOption[] options, Duration timeout) @private
{
$if env::WIN32:
os::start_wsa()!;
$endif
Clock c = 0;
@loop_over_ai(addrinfo; NativeSocket sockfd, AddrInfo* ai)
{
apply_sockoptions(sockfd, options)!;
sockfd.set_non_blocking(true)!;
int errcode = os::connect(sockfd, ai.ai_addr, ai.ai_addrlen);
if (!errcode)
{
// It worked, restore blocking.
sockfd.set_non_blocking(false)!;
return new_socket(sockfd, ai);
}
if (last_error_is_delayed_connect())
{
Duration timeout_left = timeout;
if (c)
{
Duration to_remove = c.to_now().to_duration();
if (to_remove >= timeout_left)
{
return CONNECTION_TIMED_OUT?;
}
timeout_left -= to_remove;
}
else
{
c = clock::now();
}
Poll poll_request = { sockfd, SUBSCRIBE_ANY_WRITE, 0 };
if (!poll((&poll_request)[:1], timeout_left)!)
{
return CONNECTION_TIMED_OUT?;
}
if (poll_request.revents & POLL_EVENT_WRITE)
{
sockfd.set_non_blocking(false)!;
return new_socket(sockfd, ai);
}
}
};
return os::socket_error()?;
}
fn Socket? connect_async_from_addrinfo(AddrInfo* addrinfo, SocketOption[] options) @private
{
$if env::WIN32:
os::start_wsa()!;
$endif
@loop_over_ai(addrinfo; NativeSocket sockfd, AddrInfo* ai)
{
apply_sockoptions(sockfd, options)!;
sockfd.set_non_blocking(true)!;
int errcode = os::connect(sockfd, ai.ai_addr, ai.ai_addrlen);
if (!errcode || last_error_is_delayed_connect())
{
// Keep the first successful connection.
return new_socket(sockfd, ai);
}
};
return os::socket_error()?;
}
macro void @network_loop_over_ai(network, host, port; @body(fd, ai)) @private
{
$if env::WIN32:
os::start_wsa()!;
$endif
AddrInfo* ai = network.addrinfo(host, port)!;
AddrInfo* first = ai;
defer os::freeaddrinfo(first);
while (ai)
{
NativeSocket sockfd = os::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol);
if (sockfd > 0)
{
@body(sockfd, ai);
}
ai = ai.ai_next;
}
}