socket: introduce SOCK_EAGAIN() and use it

To contain the logic of checking for both `EWOULDBLOCK` and/or `EAGAIN`
depending on platform/availability. Also to avoid checking for both if
they mapp to the same value, and to avoid PP guards around use.

This also ensures `EAGAIN` is consistently not checked on Windows, where
headers defined it, but `SOCKERRNO` never returns it, because curl maps
it to `WSAGetLastError()`.

If they map to the same value, checking them both in an `if` expression
trips GCC warning `-Wlogical-op` (the same way it triggers duplicate
case value error in `switch`).

Also:
- replace two `switch()` statements with the new macro.
- tests/server/sws: make two outliers use the new macro that were only
  checking for `EWOULDBLOCK` before this patch, in `connect_to()`.
- move variables to the left-side of expressions, where missing.
- rustls: use a variant of this macro that uses raw `EWOULDBLOCK`.
  Tried tracing it back to the origins, but I couldn't figure out if
  this is working as expected on all supported Windows versions in
  Rust. It seems to be using `GetLastError()`, according to
  https://docs.rs/system_error/0.2.0/system_error/, which would be
  probably incorrect.

Notes:
- it's probably a good idea to assign `SOCKERRNO` to a variable before
  passing it to this macro.

Cherry-picked from #21893

Closes #21992
This commit is contained in:
Viktor Szakats 2026-06-08 01:21:44 +02:00
parent 7c51a33877
commit 879a1514c3
No known key found for this signature in database
7 changed files with 63 additions and 95 deletions

View file

@ -939,32 +939,19 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
static CURLcode socket_connect_result(struct Curl_easy *data,
const char *ipaddress, int error)
{
switch(error) {
case SOCKEINPROGRESS:
case SOCKEWOULDBLOCK:
#ifdef EAGAIN
#if (EAGAIN) != (SOCKEWOULDBLOCK)
/* On some platforms EAGAIN and EWOULDBLOCK are the
* same value, and on others they are different, hence
* the odd #if
*/
case EAGAIN:
#endif
#endif
if(error == SOCKEINPROGRESS || SOCK_EAGAIN(error))
return CURLE_OK;
default:
/* unknown error, fallthrough and try another address! */
{
VERBOSE(char buffer[STRERROR_LEN]);
infof(data, "Immediate connect fail for %s: %s", ipaddress,
curlx_strerror(error, buffer, sizeof(buffer)));
NOVERBOSE((void)ipaddress);
}
data->state.os_errno = error;
/* connect failed */
return CURLE_COULDNT_CONNECT;
/* unknown error, fallthrough and try another address! */
{
VERBOSE(char buffer[STRERROR_LEN]);
infof(data, "Immediate connect fail for %s: %s", ipaddress,
curlx_strerror(error, buffer, sizeof(buffer)));
NOVERBOSE((void)ipaddress);
}
data->state.os_errno = error;
/* connect failed */
return CURLE_COULDNT_CONNECT;
}
struct cf_socket_ctx {
@ -1560,22 +1547,12 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
if(!curlx_sztouz(rv, pnwritten)) {
int sockerr = SOCKERRNO;
if(
#ifdef USE_WINSOCK
/* This is how Windows does it */
(SOCKEWOULDBLOCK == sockerr)
#else
/* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
due to its inability to send off data without blocking. We therefore
treat both error codes the same here */
(SOCKEWOULDBLOCK == sockerr) ||
(EAGAIN == sockerr) || (SOCKEINTR == sockerr) ||
(SOCKEINPROGRESS == sockerr)
if(SOCK_EAGAIN(sockerr)
#ifndef USE_WINSOCK
|| (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS)
#endif
) {
/* EWOULDBLOCK */
result = CURLE_AGAIN;
result = CURLE_AGAIN; /* EWOULDBLOCK */
}
else {
char buffer[STRERROR_LEN];
@ -1626,21 +1603,12 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
if(!curlx_sztouz(rv, pnread)) {
int sockerr = SOCKERRNO;
if(
#ifdef USE_WINSOCK
/* This is how Windows does it */
(SOCKEWOULDBLOCK == sockerr)
#else
/* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
due to its inability to send off data without blocking. We therefore
treat both error codes the same here */
(SOCKEWOULDBLOCK == sockerr) ||
(EAGAIN == sockerr) || (SOCKEINTR == sockerr)
if(SOCK_EAGAIN(sockerr)
#ifndef USE_WINSOCK
|| (sockerr == SOCKEINTR)
#endif
) {
/* EWOULDBLOCK */
result = CURLE_AGAIN;
result = CURLE_AGAIN; /* EWOULDBLOCK */
}
else {
char buffer[STRERROR_LEN];

View file

@ -1137,6 +1137,15 @@ typedef unsigned int curl_bit;
#define SOCKEWOULDBLOCK EWOULDBLOCK
#endif
/* The socket error may be EWOULDBLOCK or on some systems EAGAIN when
it returned due to its inability to send/read data without blocking.
We treat both error codes the same here. */
#if !defined(USE_WINSOCK) && EAGAIN != SOCKEWOULDBLOCK
#define SOCK_EAGAIN(e) ((e) == SOCKEWOULDBLOCK || (e) == EAGAIN)
#else
#define SOCK_EAGAIN(e) ((e) == SOCKEWOULDBLOCK)
#endif
/*
* Macro argv_item_t hides platform details to code using it.
*/

View file

@ -232,16 +232,9 @@ static int wakeup_inet(curl_socket_t socks[2], bool nonblocking)
/* Do not block forever */
if(curlx_timediff_ms(curlx_now(), start) > (60 * 1000))
goto error;
if(
#ifdef USE_WINSOCK
/* This is how Windows does it */
(SOCKEWOULDBLOCK == sockerr)
#else
/* errno may be EWOULDBLOCK or on some systems EAGAIN when it
returned due to its inability to send off data without
blocking. We therefore treat both error codes the same here */
(SOCKEWOULDBLOCK == sockerr) || (EAGAIN == sockerr) ||
(SOCKEINTR == sockerr) || (SOCKEINPROGRESS == sockerr)
if(SOCK_EAGAIN(sockerr)
#ifndef USE_WINSOCK
|| (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS)
#endif
) {
continue;
@ -320,15 +313,12 @@ int Curl_wakeup_signal(curl_socket_t socks[2])
err = 0;
if(wakeup_write(socks[1], buf, sizeof(buf)) < 0) {
err = SOCKERRNO;
#ifdef USE_WINSOCK
if(err == SOCKEWOULDBLOCK)
err = 0; /* wakeup is already ongoing */
#else
if(SOCKEINTR == err)
#ifndef USE_WINSOCK
if(err == SOCKEINTR)
continue;
if((err == SOCKEWOULDBLOCK) || (err == EAGAIN))
err = 0; /* wakeup is already ongoing */
#endif
if(SOCK_EAGAIN(err))
err = 0; /* wakeup is already ongoing */
}
break;
}
@ -346,15 +336,13 @@ CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all)
if(!rc)
break;
else if(rc < 0) {
#ifdef USE_WINSOCK
if(SOCKERRNO == SOCKEWOULDBLOCK)
break;
#else
if(SOCKEINTR == SOCKERRNO)
int sockerr = SOCKERRNO;
#ifndef USE_WINSOCK
if(sockerr == SOCKEINTR)
continue;
if((SOCKERRNO == SOCKEWOULDBLOCK) || (SOCKERRNO == EAGAIN))
break;
#endif
if(SOCK_EAGAIN(sockerr))
break;
result = CURLE_READ_ERROR;
break;
}

View file

@ -165,12 +165,10 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
;
if(!curlx_sztouz(rv, psent)) {
switch(SOCKERRNO) {
case EAGAIN:
#if EAGAIN != SOCKEWOULDBLOCK
case SOCKEWOULDBLOCK:
#endif
int sockerr = SOCKERRNO;
if(SOCK_EAGAIN(sockerr))
return CURLE_AGAIN;
switch(sockerr) {
case SOCKEMSGSIZE:
/* UDP datagram is too large; caused by PMTUD. Let it be lost. */
*psent = pktlen;
@ -179,13 +177,13 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
if(pktlen > gsolen) {
/* GSO failure */
infof(data, "sendmsg() returned %zd (errno %d); disable GSO", rv,
SOCKERRNO);
sockerr);
qctx->no_gso = TRUE;
return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
}
FALLTHROUGH();
default:
failf(data, "sendmsg() returned %zd (errno %d)", rv, SOCKERRNO);
failf(data, "sendmsg() returned %zd (errno %d)", rv, sockerr);
result = CURLE_SEND_ERROR;
goto out;
}
@ -206,7 +204,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
;
if(!curlx_sztouz(rv, psent)) {
if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
if(SOCK_EAGAIN(SOCKERRNO)) {
result = CURLE_AGAIN;
goto out;
}
@ -487,7 +485,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
(SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
;
if(mcount == -1) {
if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
if(SOCK_EAGAIN(SOCKERRNO)) {
CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN");
goto out;
}
@ -574,7 +572,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
(SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
;
if(!curlx_sztouz(rc, &nread)) {
if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
if(SOCK_EAGAIN(SOCKERRNO)) {
goto out;
}
if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
@ -644,7 +642,7 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf,
(SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
;
if(!curlx_sztouz(rv, &nread)) {
if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
if(SOCK_EAGAIN(SOCKERRNO)) {
CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN");
goto out;
}

View file

@ -45,6 +45,12 @@
#include "curlx/base64.h"
#endif
#if EAGAIN != EWOULDBLOCK
#define RAW_EAGAIN(e) ((e) == EWOULDBLOCK || (e) == EAGAIN)
#else
#define RAW_EAGAIN(e) ((e) == EWOULDBLOCK)
#endif
struct rustls_ssl_backend_data {
const struct rustls_client_config *config;
struct rustls_connection *conn;
@ -160,7 +166,7 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf,
io_ctx.data = data;
io_error = rustls_connection_read_tls(backend->conn, read_cb, &io_ctx,
&tls_bytes_read);
if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
if(RAW_EAGAIN(io_error)) {
*err = CURLE_AGAIN;
return -1;
}
@ -270,7 +276,7 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
while(rustls_connection_wants_write(rconn)) {
io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx,
&tlswritten);
if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
if(RAW_EAGAIN(io_error)) {
CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes",
tlswritten_total);
return CURLE_AGAIN;

View file

@ -109,7 +109,7 @@ size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
#ifndef CURL_WINDOWS_UWP
rc = sread(per->infd, buffer, curlx_uztosi(sz * nmemb));
if(rc < 0) {
if(SOCKERRNO == SOCKEWOULDBLOCK) {
if(SOCK_EAGAIN(SOCKERRNO)) {
errno = 0;
config->readbusy = TRUE;
return CURL_READFUNC_PAUSE;

View file

@ -976,7 +976,7 @@ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req)
retry:
written = swrite(sock, buffer, num);
if(written < 0) {
if((SOCKEWOULDBLOCK == SOCKERRNO) || (EAGAIN == SOCKERRNO)) {
if(SOCK_EAGAIN(SOCKERRNO)) {
curlx_wait_ms(10);
goto retry;
}
@ -1133,8 +1133,7 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req)
logmsg("Got %zd bytes from client", got);
}
if((got == -1) &&
((SOCKERRNO == EAGAIN) || (SOCKERRNO == SOCKEWOULDBLOCK))) {
if((got == -1) && SOCK_EAGAIN(SOCKERRNO)) {
int rc;
fd_set input;
fd_set output;
@ -1194,7 +1193,7 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req)
else if(got < 0) {
char errbuf[STRERROR_LEN];
int error = SOCKERRNO;
if(EAGAIN == error || SOCKEWOULDBLOCK == error) {
if(SOCK_EAGAIN(error)) {
/* nothing to read at the moment */
return 0;
}
@ -1335,7 +1334,7 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
if(rc) {
error = SOCKERRNO;
if((error == SOCKEINPROGRESS) || (error == SOCKEWOULDBLOCK)) {
if((error == SOCKEINPROGRESS) || SOCK_EAGAIN(error)) {
fd_set output;
struct timeval timeout = { 0 };
timeout.tv_sec = 1; /* 1000 ms */
@ -1353,7 +1352,7 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
error = SOCKERRNO;
if((error == 0) || (SOCKEISCONN == error))
goto success;
else if((error != SOCKEINPROGRESS) && (error != SOCKEWOULDBLOCK))
else if((error != SOCKEINPROGRESS) && !SOCK_EAGAIN(error))
goto error;
}
else if(!rc) {
@ -1822,7 +1821,7 @@ static curl_socket_t accept_connection(curl_socket_t sock)
if(msgsock == CURL_SOCKET_BAD) {
error = SOCKERRNO;
if(EAGAIN == error || SOCKEWOULDBLOCK == error) {
if(SOCK_EAGAIN(error)) {
/* nothing to accept */
return 0;
}