sockrecvfrom: poll again when a readable socket has nothing to read
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run

sockrecvfrom() polled once and then read in a loop for as long as the
read failed with EAGAIN. Over TLS a socket is readable when only a part
of a record has come, and the read fails with EAGAIN until the rest
does: a client sending half a record and then nothing kept a thread
taking all of a CPU, and the read never timed out. On EAGAIN the socket
is now polled again, the read repeated at once only for EINTR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-22 16:03:21 +03:00
parent 94a83d126f
commit 99795d8dfe

View File

@ -60,15 +60,21 @@ int sockrecvfrom(struct clientparam *param, SOCKET sock, struct sockaddr * sin,
fds.fd = sock;
fds.events = POLLIN;
if(conf.timetoexit) return EOF;
res = param?param->srv->so._poll(param->sostate, &fds, 1, to):so._poll(so.state, &fds, 1, to);
if (res<1) return 0;
sasize = sizeof(PROXYSOCKADDRTYPE);
/* sin NULL for a stream socket: nothing to learn the address into */
do {
res = param?param->srv->so._recvfrom(param->sostate, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, sin? &sasize : NULL):so._recvfrom(so.state, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, sin? &sasize : NULL);
} while (res < 0 && (errno == EAGAIN || errno == EINTR));
return res;
for(;;){
if(conf.timetoexit) return EOF;
res = param?param->srv->so._poll(param->sostate, &fds, 1, to):so._poll(so.state, &fds, 1, to);
if (res<1) return 0;
sasize = sizeof(PROXYSOCKADDRTYPE);
/* sin NULL for a stream socket: nothing to learn the address into */
do {
res = param?param->srv->so._recvfrom(param->sostate, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, sin? &sasize : NULL):so._recvfrom(so.state, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, sin? &sasize : NULL);
} while (res < 0 && errno == EINTR);
/* readable and still nothing to read: over TLS a record not yet
whole. Read again, without polling, it would only be the same until
the rest came, a loop taking all of a CPU and never timing out: the
socket is polled for it instead. */
if(res >= 0 || errno != EAGAIN) return res;
}
}
int sockgetcharcli(struct clientparam * param, int timeosec, int timeousec){