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; } }