-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
net: add setTOS and getTOS to Socket #61503
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Review requested:
|
mcollina
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work. Can you add the documentation?
|
Thanks @mcollina , is this ready to be merged? Please let me know if there is anything else you need me to adjust. |
42d5142 to
c0407b2
Compare
|
@mcollina I'm confused by the CI failures. Any ideas? Or just flaky? |
|
@ronag and @mcollina , it's all my fault, as things work for the linux better, on which i worked on, and i observed most of failures are for windows and very few or i guess only 1 or 2 for macos, and thats completely the code fault written by me. And the reason is platform incompatiblity. Some constants worked for Linux and macos but not for windows. Gimme some time I will fix all of them and get back with solutions soon. Thanks! |
|
@ronag and @mcollina i guess all errors are fixed, its because of windows incompatibility, so now lets rerun the checks. and rest this below picture of macos failures are flake i read the console output https://ci.nodejs.org/job/node-test-commit-osx/68706/nodes=macos15-arm64/
|
20e5908 to
57f36ad
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds setTOS() and getTOS() methods to net.Socket and dgram.Socket to enable DSCP tagging for QoS traffic prioritization. The implementation handles both IPv4 (IP_TOS) and IPv6 (IPV6_TCLASS) sockets automatically with fallback logic.
Changes:
- Implemented native C++ methods in
tcp_wrap.ccandudp_wrap.ccto set/get TOS values using appropriate socket options - Added JavaScript API methods in
lib/net.jsandlib/dgram.jswith validation and deferred application logic - Created comprehensive test coverage across multiple scenarios (validation, implicit bind/connect, connected state)
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/parallel/test-tcp-setTOS.js | Tests basic TCP setTOS/getTOS functionality with validation |
| test/parallel/test-tcp-setTOS-implicit-connect.js | Tests TCP TOS setting before connect |
| test/parallel/test-net-socket-tos.js | Comprehensive TCP socket TOS validation tests |
| test/parallel/test-dgram-setTOS.js | Tests basic UDP setTOS/getTOS with validation |
| test/parallel/test-dgram-setTOS-implicit-bind.js | Tests UDP TOS setting before bind |
| test/parallel/test-dgram-setTOS-connect.js | Tests UDP TOS with connected datagram socket |
| src/udp_wrap.h | Declares SetTOS and GetTOS methods for UDP |
| src/udp_wrap.cc | Implements UDP TOS get/set with IPv4/IPv6 handling |
| src/tcp_wrap.h | Declares SetTOS and GetTOS methods for TCP |
| src/tcp_wrap.cc | Implements TCP TOS get/set with IPv4/IPv6 handling and header includes |
| lib/net.js | Adds Socket.prototype.setTOS/getTOS with deferred application logic |
| lib/dgram.js | Adds Socket.prototype.setTOS/getTOS with deferred application logic |
| doc/api/net.md | Documents the new setTOS and getTOS methods |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/tcp_wrap.cc
Outdated
| #ifdef __linux__ | ||
| #include <netinet/ip6.h> | ||
| #else | ||
| #include <netinet/ip6.h> | ||
| #endif |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conditional include is redundant - both branches include the same header <netinet/ip6.h>. Either remove the conditional entirely if the header is universally available, or use it to conditionally include different headers if platform-specific handling is needed.
| #ifdef __linux__ | |
| #include <netinet/ip6.h> | |
| #else | |
| #include <netinet/ip6.h> | |
| #endif | |
| #include <netinet/ip6.h> |
lib/net.js
Outdated
|
|
||
| // TOS must be an integer in the range 0..255. | ||
| if (!Number.isInteger(tos) || tos < 0 || tos > 255) { | ||
| throw new ErrnoException(UV_EINVAL, 'setTOS'); |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This validation error uses ErrnoException with UV_EINVAL, but validation errors for out-of-range values typically use ERR_OUT_OF_RANGE for consistency with the rest of the codebase and to provide clearer error messages. Consider using ERR_OUT_OF_RANGE for the range validation instead.
| throw new ErrnoException(UV_EINVAL, 'setTOS'); | |
| throw new ERR_OUT_OF_RANGE('tos', '>= 0 && <= 255', tos); |
lib/dgram.js
Outdated
| if (!Number.isInteger(tos) || tos < 0 || tos > 255) { | ||
| throw new ErrnoException(UV_EINVAL, 'setTOS'); | ||
| } |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This validation error uses ErrnoException with UV_EINVAL, but validation errors for out-of-range values typically use ERR_OUT_OF_RANGE for consistency with the rest of the codebase and to provide clearer error messages. Consider using ERR_OUT_OF_RANGE for the range validation instead.
| * Returns: {net.Socket} The socket itself. | ||
|
|
||
| Sets the Type of Service (TOS) field for IPv4 packets or Traffic Class for IPv6 | ||
| Packets sent from this socket. This can be used to prioritize network traffic. |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected capitalization of 'Packets' to 'packets'.
| Packets sent from this socket. This can be used to prioritize network traffic. | |
| packets sent from this socket. This can be used to prioritize network traffic. |
lib/dgram.js
Outdated
| if (socket[kSetTOS] !== undefined && state.handle && state.handle.setTOS) { | ||
| const err = state.handle.setTOS(socket[kSetTOS]); | ||
| if (err && err !== UV_EBADF) { | ||
| // Non-fatal: surface error to the socket | ||
| socket.emit('error', new ErrnoException(err, 'setTOS')); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it better to always defer to on listening?
lib/dgram.js
Outdated
| // Ignore EBADF: on some platforms the underlying fd may not be | ||
| // fully initialized yet even though a handle exists. In that case | ||
| // defer applying the setting to when the socket is actually bound | ||
| // (handled by bind/replaceHandle). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems rather hacky... isn't there a way to know when the handle is initialized?
|
@ronag, i request that this pr should only address the tcp or is udp required?? if we push the udp in next update, then it will be easy to rollback changes on crashes |
|
as the errors were found only in the dgram which is an udp related work, so we can do 2 different work. one for tcp and one for udp |
57f36ad to
fe329db
Compare
|
IMHO, tcp is sufficient |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| this[kSetNoDelay] = Boolean(options.noDelay); | ||
| this[kSetKeepAlive] = Boolean(options.keepAlive); | ||
| this[kSetKeepAliveInitialDelay] = ~~(options.keepAliveInitialDelay / 1000); | ||
| this[kSetTOS] = options.TOS; |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The option name 'TOS' uses inconsistent casing compared to other options ('noDelay', 'keepAlive'). Consider using 'tos' (lowercase) instead of 'TOS' for consistency with the method names and other socket options.
| this[kSetTOS] = options.TOS; | |
| this[kSetTOS] = options.tos; |
| } | ||
|
|
||
| if (!this._handle.getTOS) { | ||
| return this[kSetTOS]; |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When getTOS() is called and the handle exists but doesn't support getTOS, the method returns the cached kSetTOS value which could be undefined. This creates inconsistent behavior compared to line 683 where undefined is explicitly converted to 0. The return should be: return this[kSetTOS] !== undefined ? this[kSetTOS] : 0;
| return this[kSetTOS]; | |
| // Return cached value if set, otherwise default to 0 | |
| return this[kSetTOS] !== undefined ? this[kSetTOS] : 0; |
| } | ||
|
|
||
| // If both failed, return the negative errno | ||
| args.GetReturnValue().Set(-errno); |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using errno directly after failed getsockopt calls is unreliable because errno may have been modified by intervening operations. The errno value should be captured immediately after the getsockopt call fails before any other operations occur.
| #ifdef _WIN32 | ||
| // Windows: Try IPv4 | ||
| if (setsockopt(reinterpret_cast<SOCKET>(fd), | ||
| IPPROTO_IP, | ||
| IP_TOS, | ||
| reinterpret_cast<const char*>(&tos), | ||
| static_cast<int>(sizeof(tos))) == 0) { | ||
| successful_at_least_once = true; | ||
| } | ||
| // Windows: Try IPv6 (Best effort) | ||
| if (setsockopt(reinterpret_cast<SOCKET>(fd), | ||
| IPPROTO_IPV6, | ||
| IPV6_TCLASS, | ||
| reinterpret_cast<const char*>(&tos), | ||
| static_cast<int>(sizeof(tos))) == 0) { | ||
| successful_at_least_once = true; | ||
| } | ||
| #else | ||
| // POSIX (Linux/macOS): Try IPv4 | ||
| if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == 0) { | ||
| successful_at_least_once = true; | ||
| } | ||
| // POSIX (Linux/macOS): Try IPv6 | ||
| if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) == 0) { | ||
| successful_at_least_once = true; | ||
| } | ||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't there a way to check if a socket is ipv4 or ipv6 before doing this? Rather than relying on fallback on any error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you re absolutely right, i have refractored the implementation to detect whether the socket is ipv4 or 6, and performs a single targeted call with correct constant

this PR implements
socket.setTOS(tos)andsocket.getTOS()innet.Socket. it needed this to support DSCP tagging (QoS) for traffic prioritization, which wasn't previously exposed in the JS API. for the implementation:tcp_wrap.ccto attemptIP_TOSfirst, and fallback toIPV6_TCLASSif that fails. This handles both IPv4 and IPv6 sockets automatically without needing a separate flag.UV_ENOSYSfor now since the headers/implementation differ there.test-net-socket-tos.js) to verify input validation and ensure the values are actually set on supported platforms.Fixes: #61489