HTTP connect: re-arm the select() set on each wait, and skip failed sockets

select() clears the sockets that aren't ready, so after one 0.5 s slice
with no connection the loop waited on an empty set and every connect
slower than that ran out the whole timeout. A socket that turns writable
because its connect failed was also taken as connected.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-13 02:24:05 +02:00
1 parent 664f6ff5ad
commit 847625359d
1 file changed
+12 -3
+12 -3
View File
@@ -172,6 +172,7 @@ bool Connection::Connect(int maxTries, double timeout, bool *cancelConnect) {
continue;
}
// There is at least 1 socket candidate.
const fd_set allFds = fds;
int selectResult = 0;
long timeoutHalfSeconds = floor(2 * timeout);
@@ -187,6 +188,8 @@ bool Connection::Connect(int maxTries, double timeout, bool *cancelConnect) {
}
--timeoutHalfSeconds;
// select() clears the sockets that aren't ready, so each wait needs a fresh copy.
fds = allFds;
selectResult = select(maxfd, nullptr, &fds, nullptr, &tv);
if (cancelConnect && *cancelConnect) {
WARN_LOG(Log::HTTP, "connect: cancelled (1): %s:%d", host_.c_str(), port_);
@@ -196,15 +199,21 @@ bool Connection::Connect(int maxTries, double timeout, bool *cancelConnect) {
if (selectResult > 0) {
// Something connected. Pick the first one that did (if multiple.)
for (int sock : sockets) {
if ((intptr_t)sock_ == -1 && FD_ISSET(sock, &fds)) {
// Writable can also mean the attempt failed (refused, say).
int soError = 0;
socklen_t soErrorLen = sizeof(soError);
bool connected = FD_ISSET(sock, &fds) && getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&soError, &soErrorLen) == 0 && soError == 0;
if ((intptr_t)sock_ == -1 && connected) {
sock_ = sock;
} else {
closesocket(sock);
}
}
// Great, now we're good to go.
return true;
if ((intptr_t)sock_ != -1) {
// Great, now we're good to go.
return true;
}
} else {
// Fail. Close all the sockets.
for (int sock : sockets) {