Skip to content

Commit b775fe7

Browse files
committed
Networking (macOS): support TFO
1 parent 60eaa32 commit b775fe7

File tree

1 file changed

+72
-11
lines changed

1 file changed

+72
-11
lines changed

src/common/networking/networking_linux.c

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "util/debug.h"
88

99
#include <unistd.h>
10+
#include <sys/poll.h>
1011
#include <sys/time.h>
1112
#include <sys/socket.h>
1213
#include <netdb.h>
@@ -25,18 +26,35 @@ static const char* tryTcpFastOpen(FFNetworkingState* state)
2526
#else
2627
FF_DEBUG("Attempting to use TCP Fast Open to connect to %s", state->host.chars);
2728

29+
#ifndef __APPLE__ // On macOS, TCP_FASTOPEN doesn't seem to be needed
2830
// Set TCP Fast Open
29-
int qlen = 5;
30-
if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)) != 0) {
31+
#ifdef __linux__
32+
int flag = 5; // the queue length of pending packets
33+
#else
34+
int flag = 1; // enable TCP Fast Open
35+
#endif
36+
if (setsockopt(state->sockfd, IPPROTO_TCP,
37+
#ifdef __APPLE__
38+
// https://github.com/rust-lang/libc/pull/3135
39+
0x218 // TCP_FASTOPEN_FORCE_ENABLE
40+
#else
41+
TCP_FASTOPEN
42+
#endif
43+
, &flag, sizeof(flag)) != 0) {
3144
FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", strerror(errno));
45+
return "setsockopt(TCP_FASTOPEN) failed";
3246
} else {
33-
FF_DEBUG("Successfully set TCP_FASTOPEN option, queue length: %d", qlen);
47+
#ifdef __linux__
48+
FF_DEBUG("Successfully set TCP_FASTOPEN option, queue length: %d", flag);
49+
#elif defined(__APPLE__)
50+
FF_DEBUG("Successfully set TCP_FASTOPEN_FORCE_ENABLE option");
51+
#else
52+
FF_DEBUG("Successfully set TCP_FASTOPEN option");
53+
#endif
3454
}
55+
#endif
3556

36-
// Try to send data using Fast Open
37-
#ifdef __APPLE__
38-
ssize_t sent = 0;
39-
#else
57+
#ifndef __APPLE__
4058
FF_DEBUG("Using sendto() + MSG_FASTOPEN to send %u bytes of data", state->command.length);
4159
ssize_t sent = sendto(state->sockfd,
4260
state->command.chars,
@@ -47,6 +65,28 @@ static const char* tryTcpFastOpen(FFNetworkingState* state)
4765
MSG_DONTWAIT,
4866
state->addr->ai_addr,
4967
state->addr->ai_addrlen);
68+
#else
69+
if (fcntl(state->sockfd, F_SETFL, O_NONBLOCK) == -1) {
70+
FF_DEBUG("fcntl(F_SETFL) failed: %s", strerror(errno));
71+
return "fcntl(F_SETFL) failed";
72+
}
73+
FF_DEBUG("Using connectx() to send %u bytes of data", state->command.length);
74+
// Use connectx to establish connection and send data in one call
75+
size_t sent;
76+
if (connectx(state->sockfd,
77+
&(sa_endpoints_t) {
78+
.sae_dstaddr = state->addr->ai_addr,
79+
.sae_dstaddrlen = state->addr->ai_addrlen,
80+
},
81+
SAE_ASSOCID_ANY, CONNECT_DATA_IDEMPOTENT,
82+
&(struct iovec) {
83+
.iov_base = state->command.chars,
84+
.iov_len = state->command.length,
85+
}, 1, &sent, NULL) != 0) sent = 0;
86+
if (fcntl(state->sockfd, F_SETFL, 0) == -1) {
87+
FF_DEBUG("fcntl(F_SETFL) failed: %s", strerror(errno));
88+
return "fcntl(F_SETFL) failed";
89+
}
5090
#endif
5191
if (sent >= 0 || (errno == EAGAIN || errno == EWOULDBLOCK))
5292
{
@@ -68,7 +108,11 @@ static const char* tryTcpFastOpen(FFNetworkingState* state)
68108
{
69109
// Fast Open failed
70110
FF_DEBUG("TCP Fast Open failed: %s (errno=%d)", strerror(errno), errno);
111+
#ifdef __APPLE__
112+
return "connectx() failed";
113+
#else
71114
return "sendto() failed";
115+
#endif
72116
}
73117
#endif
74118
}
@@ -313,6 +357,26 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
313357
return "ffNetworkingSendHttpRequest() failed";
314358
}
315359

360+
// Set larger initial receive buffer instead of small repeated receives
361+
int rcvbuf = 65536; // 64KB
362+
setsockopt(state->sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
363+
364+
#ifdef __APPLE__
365+
// poll for the socket to be readable.
366+
// Because of the non-blocking connectx() call, the connection might not be established yet
367+
FF_DEBUG("Using poll() to check if socket is readable");
368+
if (poll(&(struct pollfd) {
369+
.fd = state->sockfd,
370+
.events = POLLIN
371+
}, 1, timeout > 0 ? (int) timeout : -1) == -1)
372+
{
373+
FF_DEBUG("poll() failed: %s (errno=%d)", strerror(errno), errno);
374+
close(state->sockfd);
375+
state->sockfd = -1;
376+
return "poll() failed";
377+
}
378+
FF_DEBUG("Socket is readable, proceeding to receive data");
379+
#else
316380
if(timeout > 0)
317381
{
318382
FF_DEBUG("Setting receive timeout: %u ms", timeout);
@@ -321,10 +385,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
321385
timev.tv_usec = (__typeof__(timev.tv_usec)) ((timeout % 1000) * 1000); //milliseconds to microseconds
322386
setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev));
323387
}
324-
325-
// Set larger initial receive buffer instead of small repeated receives
326-
int rcvbuf = 65536; // 64KB
327-
setsockopt(state->sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
388+
#endif
328389

329390
FF_DEBUG("Starting data reception");
330391
FF_MAYBE_UNUSED int recvCount = 0;

0 commit comments

Comments
 (0)