Skip to content

Commit 36d7612

Browse files
committed
Merge branch 'main' of https://github.com/nodejs/node
2 parents e271c66 + 12249cc commit 36d7612

8 files changed

Lines changed: 201 additions & 59 deletions

File tree

deps/uv/docs/src/tcp.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,35 @@ API
9191
9292
.. versionchanged:: 1.49.0 If `delay` is less than 1 then ``UV_EINVAL``` is returned.
9393
94+
.. c:function:: int uv_tcp_keepalive_ex(uv_tcp_t* handle, int on, unsigned int idle, unsigned int intvl, unsigned int cnt)
95+
96+
Enable / disable TCP keep-alive with all socket options: `TCP_KEEPIDLE`, `TCP_KEEPINTVL` and `TCP_KEEPCNT`.
97+
`idle` is the value for `TCP_KEEPIDLE`, `intvl` is the value for `TCP_KEEPINTVL`,
98+
`cnt` is the value for `TCP_KEEPCNT`, ignored when `on` is zero.
99+
100+
With TCP keep-alive enabled, `idle` is the time (in seconds) the connection needs to remain idle before
101+
TCP starts sending keep-alive probes. `intvl` is the time (in seconds) between individual keep-alive probes.
102+
TCP will drop the connection after sending `cnt` probes without getting any replies from the peer, then the
103+
handle is destroyed with a ``UV_ETIMEDOUT`` error passed to the corresponding callback.
104+
105+
If one of `idle`, `intvl`, or `cnt` is less than 1, ``UV_EINVAL`` is returned.
106+
107+
.. versionchanged:: 1.52.0 added support of setting `TCP_KEEPINTVL` and `TCP_KEEPCNT` socket options.
108+
109+
.. note::
110+
Ensure that the socket options are supported by the underlying operating system.
111+
Currently supported platforms:
112+
- AIX
113+
- DragonFlyBSD
114+
- FreeBSD
115+
- HP-UX
116+
- illumos
117+
- Linux
118+
- macOS
119+
- NetBSD
120+
- Solaris
121+
- Windows
122+
94123
.. c:function:: int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable)
95124
96125
Enable / disable simultaneous asynchronous accept requests that are

deps/uv/include/uv.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,11 @@ UV_EXTERN int uv_tcp_nodelay(uv_tcp_t* handle, int enable);
602602
UV_EXTERN int uv_tcp_keepalive(uv_tcp_t* handle,
603603
int enable,
604604
unsigned int delay);
605+
UV_EXTERN int uv_tcp_keepalive_ex(uv_tcp_t* handle,
606+
int on,
607+
unsigned int idle,
608+
unsigned int intvl,
609+
unsigned int cnt);
605610
UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable);
606611

607612
enum uv_tcp_flags {

deps/uv/src/unix/internal.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,11 @@ int uv__slurp(const char* filename, char* buf, size_t len);
359359
/* tcp */
360360
int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb);
361361
int uv__tcp_nodelay(int fd, int on);
362-
int uv__tcp_keepalive(int fd, int on, unsigned int delay);
362+
int uv__tcp_keepalive(int fd,
363+
int on,
364+
unsigned int idle,
365+
unsigned int intvl,
366+
unsigned int cnt);
363367

364368
/* tty */
365369
void uv__tty_close(uv_tty_t* handle);

deps/uv/src/unix/stream.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) {
416416

417417
/* TODO Use delay the user passed in. */
418418
if ((stream->flags & UV_HANDLE_TCP_KEEPALIVE) &&
419-
uv__tcp_keepalive(fd, 1, 60)) {
419+
uv__tcp_keepalive(fd, 1, 60, 1, 10)) {
420420
return UV__ERR(errno);
421421
}
422422
}

deps/uv/src/unix/tcp.c

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -465,22 +465,18 @@ int uv__tcp_nodelay(int fd, int on) {
465465
#else
466466
#define UV_KEEPALIVE_FACTOR(x)
467467
#endif
468-
int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
469-
int idle;
470-
int intvl;
471-
int cnt;
472-
473-
(void) &idle;
474-
(void) &intvl;
475-
(void) &cnt;
476-
468+
int uv__tcp_keepalive(int fd,
469+
int on,
470+
unsigned int idle,
471+
unsigned int intvl,
472+
unsigned int cnt) {
477473
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)))
478474
return UV__ERR(errno);
479475

480476
if (!on)
481477
return 0;
482478

483-
if (delay < 1)
479+
if (idle < 1 || intvl < 1 || cnt < 1)
484480
return UV_EINVAL;
485481

486482
#ifdef __sun
@@ -506,13 +502,16 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
506502
* The TCP connection will be aborted after certain amount of probes, which is set by TCP_KEEPCNT, without receiving response.
507503
*/
508504

509-
idle = delay;
510-
/* Kernel expects at least 10 seconds. */
505+
/* Kernel expects at least 10 seconds for TCP_KEEPIDLE and TCP_KEEPINTVL. */
511506
if (idle < 10)
512507
idle = 10;
513-
/* Kernel expects at most 10 days. */
508+
if (intvl < 10)
509+
intvl = 10;
510+
/* Kernel expects at most 10 days for TCP_KEEPIDLE and TCP_KEEPINTVL. */
514511
if (idle > 10*24*60*60)
515512
idle = 10*24*60*60;
513+
if (intvl > 10*24*60*60)
514+
intvl = 10*24*60*60;
516515

517516
UV_KEEPALIVE_FACTOR(idle);
518517

@@ -522,12 +521,10 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
522521
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)))
523522
return UV__ERR(errno);
524523

525-
intvl = 10; /* required at least 10 seconds */
526524
UV_KEEPALIVE_FACTOR(intvl);
527525
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
528526
return UV__ERR(errno);
529527

530-
cnt = 1; /* 1 retry, ensure (TCP_KEEPINTVL * TCP_KEEPCNT) is 10 seconds */
531528
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)))
532529
return UV__ERR(errno);
533530
#else
@@ -539,15 +536,14 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
539536

540537
/* Note that the consequent probes will not be sent at equal intervals on Solaris,
541538
* but will be sent using the exponential backoff algorithm. */
542-
int time_to_abort = 10; /* 10 seconds */
539+
unsigned int time_to_abort = intvl * cnt;
543540
UV_KEEPALIVE_FACTOR(time_to_abort);
544541
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, &time_to_abort, sizeof(time_to_abort)))
545542
return UV__ERR(errno);
546543
#endif
547544

548545
#else /* !defined(__sun) */
549546

550-
idle = delay;
551547
UV_KEEPALIVE_FACTOR(idle);
552548
#ifdef TCP_KEEPIDLE
553549
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)))
@@ -559,14 +555,12 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
559555
#endif
560556

561557
#ifdef TCP_KEEPINTVL
562-
intvl = 1; /* 1 second; same as default on Win32 */
563558
UV_KEEPALIVE_FACTOR(intvl);
564559
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
565560
return UV__ERR(errno);
566561
#endif
567562

568563
#ifdef TCP_KEEPCNT
569-
cnt = 10; /* 10 retries; same as hardcoded on Win32 */
570564
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)))
571565
return UV__ERR(errno);
572566
#endif
@@ -594,11 +588,20 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int on) {
594588
}
595589

596590

597-
int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) {
591+
int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int idle) {
592+
return uv_tcp_keepalive_ex(handle, on, idle, 1, 10);
593+
}
594+
595+
596+
int uv_tcp_keepalive_ex(uv_tcp_t* handle,
597+
int on,
598+
unsigned int idle,
599+
unsigned int intvl,
600+
unsigned int cnt) {
598601
int err;
599602

600603
if (uv__stream_fd(handle) != -1) {
601-
err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay);
604+
err = uv__tcp_keepalive(uv__stream_fd(handle), on, idle, intvl, cnt);
602605
if (err)
603606
return err;
604607
}
@@ -608,7 +611,7 @@ int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) {
608611
else
609612
handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE;
610613

611-
/* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge
614+
/* TODO Store idle if uv__stream_fd(handle) == -1 but don't want to enlarge
612615
* uv_tcp_t with an int that's almost never used...
613616
*/
614617

deps/uv/src/win/tcp.c

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,29 +49,101 @@ static int uv__tcp_nodelay(uv_tcp_t* handle, SOCKET socket, int enable) {
4949
}
5050

5151

52-
static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsigned int delay) {
52+
/*
53+
* Check if Windows version is 10.0.16299 (Windows 10, version 1709) or later.
54+
*/
55+
static int minimal_windows10_version1709(void) {
56+
OSVERSIONINFOW os_info;
57+
if (!pRtlGetVersion)
58+
return 0;
59+
os_info.dwOSVersionInfoSize = sizeof(os_info);
60+
os_info.szCSDVersion[0] = L'\0';
61+
pRtlGetVersion(&os_info);
62+
if (os_info.dwMajorVersion < 10)
63+
return 0;
64+
if (os_info.dwMajorVersion > 10)
65+
return 1;
66+
if (os_info.dwMinorVersion > 0)
67+
return 1;
68+
return os_info.dwBuildNumber >= 16299;
69+
}
70+
71+
72+
static int uv__tcp_keepalive(uv_tcp_t* handle,
73+
SOCKET socket,
74+
int on,
75+
unsigned int idle,
76+
unsigned int intvl,
77+
unsigned int cnt) {
5378
if (setsockopt(socket,
5479
SOL_SOCKET,
5580
SO_KEEPALIVE,
56-
(const char*)&enable,
57-
sizeof enable) == -1) {
81+
(const char*)&on,
82+
sizeof on) == -1) {
5883
return WSAGetLastError();
5984
}
6085

61-
if (!enable)
86+
if (!on)
6287
return 0;
6388

64-
if (delay < 1)
89+
if (idle < 1 || intvl < 1 || cnt < 1)
6590
return UV_EINVAL;
6691

67-
if (setsockopt(socket,
68-
IPPROTO_TCP,
69-
TCP_KEEPALIVE,
70-
(const char*)&delay,
71-
sizeof delay) == -1) {
72-
return WSAGetLastError();
92+
/* Windows 10, version 1709 (build 10.0.16299) and later require second units
93+
* for TCP keepalive options. */
94+
if (minimal_windows10_version1709()) {
95+
if (setsockopt(socket,
96+
IPPROTO_TCP,
97+
TCP_KEEPIDLE,
98+
(const char*)&idle,
99+
sizeof idle) == -1) {
100+
return WSAGetLastError();
101+
}
102+
103+
if (setsockopt(socket,
104+
IPPROTO_TCP,
105+
TCP_KEEPINTVL,
106+
(const char*)&intvl,
107+
sizeof intvl) == -1) {
108+
return WSAGetLastError();
109+
}
110+
111+
if (setsockopt(socket,
112+
IPPROTO_TCP,
113+
TCP_KEEPCNT,
114+
(const char*)&cnt,
115+
sizeof cnt) == -1) {
116+
return WSAGetLastError();
117+
}
118+
119+
return 0;
73120
}
74121

122+
/* For those versions prior to Windows 10 version 1709,
123+
* we fall back to SIO_KEEPALIVE_VALS that expects millisecond units.
124+
* The SIO_KEEPALIVE_VALS IOCTL is supported on Windows 2000
125+
* and later versions of the operating system. */
126+
struct tcp_keepalive keepalive;
127+
keepalive.onoff = on;
128+
keepalive.keepalivetime = idle * 1000;
129+
keepalive.keepaliveinterval = intvl * 1000;
130+
/* On Windows Vista and later, the number of keep-alive probes
131+
* (data retransmissions) is set to 10 and cannot be changed.
132+
* On Windows Server 2003, Windows XP, and Windows 2000, the default setting
133+
* for number of keep-alive probes is 5 and cannot be changed programmatically.
134+
*/
135+
DWORD dummy;
136+
if (WSAIoctl(socket,
137+
SIO_KEEPALIVE_VALS,
138+
(LPVOID) &keepalive,
139+
sizeof keepalive,
140+
NULL,
141+
0,
142+
&dummy,
143+
NULL,
144+
NULL) == -1)
145+
return WSAGetLastError();
146+
75147
return 0;
76148
}
77149

@@ -132,7 +204,7 @@ static int uv__tcp_set_socket(uv_loop_t* loop,
132204

133205
/* TODO: Use stored delay. */
134206
if (handle->flags & UV_HANDLE_TCP_KEEPALIVE) {
135-
err = uv__tcp_keepalive(handle, socket, 1, 60);
207+
err = uv__tcp_keepalive(handle, socket, 1, 60, 1, 10);
136208
if (err)
137209
return err;
138210
}
@@ -749,20 +821,6 @@ static int uv__is_loopback(const struct sockaddr_storage* storage) {
749821
return 0;
750822
}
751823

752-
// Check if Windows version is 10.0.16299 or later
753-
static int uv__is_fast_loopback_fail_supported(void) {
754-
OSVERSIONINFOW os_info;
755-
if (!pRtlGetVersion)
756-
return 0;
757-
pRtlGetVersion(&os_info);
758-
if (os_info.dwMajorVersion < 10)
759-
return 0;
760-
if (os_info.dwMajorVersion > 10)
761-
return 1;
762-
if (os_info.dwMinorVersion > 0)
763-
return 1;
764-
return os_info.dwBuildNumber >= 16299;
765-
}
766824

767825
static int uv__tcp_try_connect(uv_connect_t* req,
768826
uv_tcp_t* handle,
@@ -809,7 +867,7 @@ static int uv__tcp_try_connect(uv_connect_t* req,
809867
* is not reachable, instead of waiting for 2s. We do not care if this fails.
810868
* This only works on Windows version 10.0.16299 and later.
811869
*/
812-
if (uv__is_fast_loopback_fail_supported() && uv__is_loopback(&converted)) {
870+
if (minimal_windows10_version1709() && uv__is_loopback(&converted)) {
813871
memset(&retransmit_ioctl, 0, sizeof(retransmit_ioctl));
814872
retransmit_ioctl.Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
815873
retransmit_ioctl.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
@@ -1335,22 +1393,30 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int enable) {
13351393
}
13361394

13371395

1338-
int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) {
1396+
int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int idle) {
1397+
return uv_tcp_keepalive_ex(handle, on, idle, 1, 10);
1398+
}
1399+
1400+
int uv_tcp_keepalive_ex(uv_tcp_t* handle,
1401+
int on,
1402+
unsigned int idle,
1403+
unsigned int intvl,
1404+
unsigned int cnt) {
13391405
int err;
13401406

13411407
if (handle->socket != INVALID_SOCKET) {
1342-
err = uv__tcp_keepalive(handle, handle->socket, enable, delay);
1408+
err = uv__tcp_keepalive(handle, handle->socket, on, idle, intvl, cnt);
13431409
if (err)
13441410
return uv_translate_sys_error(err);
13451411
}
13461412

1347-
if (enable) {
1413+
if (on) {
13481414
handle->flags |= UV_HANDLE_TCP_KEEPALIVE;
13491415
} else {
13501416
handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE;
13511417
}
13521418

1353-
/* TODO: Store delay if handle->socket isn't created yet. */
1419+
/* TODO: Store idle if handle->socket isn't created yet. */
13541420

13551421
return 0;
13561422
}

0 commit comments

Comments
 (0)