| 175 | bool socket::is_open() const { return mFd != -1; } |
| 176 | |
| 177 | error_code socket::connect(const endpoint &ep) { |
| 178 | // Clean up existing |
| 179 | close_fd(); |
| 180 | |
| 181 | #ifdef FL_IS_WIN |
| 182 | if (!initialize_winsock()) { |
| 183 | return error_code(errc::unknown, "winsock init failed"); |
| 184 | } |
| 185 | #endif |
| 186 | |
| 187 | // Resolve hostname |
| 188 | struct addrinfo hints {}; |
| 189 | struct addrinfo *result = nullptr; |
| 190 | |
| 191 | hints.ai_family = AF_INET; |
| 192 | hints.ai_socktype = SOCK_STREAM; |
| 193 | hints.ai_protocol = IPPROTO_TCP; |
| 194 | |
| 195 | char portStr[16]; |
| 196 | fl::snprintf(portStr, sizeof(portStr), "%u", ep.port); |
| 197 | |
| 198 | int ret = getaddrinfo(ep.host.c_str(), portStr, &hints, &result); |
| 199 | if (ret != 0 || result == nullptr) { |
| 200 | return error_code(errc::host_not_found, "getaddrinfo failed"); |
| 201 | } |
| 202 | |
| 203 | error_code ec(errc::connection_refused, "no addresses succeeded"); |
| 204 | |
| 205 | for (struct addrinfo *addr = result; addr != nullptr; addr = addr->ai_next) { |
| 206 | int sock = |
| 207 | plat_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); |
| 208 | if (sock < 0) |
| 209 | continue; |
| 210 | |
| 211 | mFd = sock; |
| 212 | // Keep socket BLOCKING for connect() — non-blocking connect |
| 213 | // returns EINPROGRESS/WSAEWOULDBLOCK which is racy: subsequent |
| 214 | // send() can fail with ENOTCONN if the TCP handshake hasn't |
| 215 | // completed yet. A blocking connect on loopback completes in |
| 216 | // microseconds; on real networks, the timeout is the OS default |
| 217 | // (typically 75-120 s), which is acceptable for a "synchronous |
| 218 | // connect" API. |
| 219 | |
| 220 | ret = plat_connect(mFd, addr->ai_addr, |
| 221 | static_cast<socklen_t>(addr->ai_addrlen)); |
| 222 | |
| 223 | if (ret == 0) { |
| 224 | // Connected — now switch to non-blocking for all I/O |
| 225 | set_nonblocking(mFd, true); |
| 226 | mNonBlocking = true; |
| 227 | ec = error_code(); |
| 228 | break; |
| 229 | } |
| 230 | |
| 231 | // Failed — try next address |
| 232 | plat_close(mFd); |
| 233 | mFd = -1; |
| 234 | } |
no test coverage detected