Skip to content

Conversation

@amyssnippet
Copy link

@amyssnippet amyssnippet commented Jan 24, 2026

this PR implements socket.setTOS(tos) and socket.getTOS() in net.Socket. it needed this to support DSCP tagging (QoS) for traffic prioritization, which wasn't previously exposed in the JS API. for the implementation:

  • I added the logic in tcp_wrap.cc to attempt IP_TOS first, and fallback to IPV6_TCLASS if that fails. This handles both IPv4 and IPv6 sockets automatically without needing a separate flag.
  • Windows returns UV_ENOSYS for now since the headers/implementation differ there.
  • Added a new test file (test-net-socket-tos.js) to verify input validation and ensure the values are actually set on supported platforms.

Fixes: #61489

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/net

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Jan 24, 2026
Copy link
Member

@mcollina mcollina left a 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?

@mcollina mcollina requested a review from ronag January 24, 2026 13:16
@mcollina mcollina added the semver-minor PRs that contain new features and should be released in the next minor version. label Jan 24, 2026
@amyssnippet amyssnippet requested a review from mcollina January 24, 2026 13:41
@amyssnippet amyssnippet mentioned this pull request Jan 24, 2026
@amyssnippet amyssnippet requested a review from mcollina January 24, 2026 14:34
@amyssnippet
Copy link
Author

Thanks @mcollina , is this ready to be merged? Please let me know if there is anything else you need me to adjust.

@ronag ronag added the request-ci Add this label to start a Jenkins CI on a PR. label Jan 25, 2026
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Jan 25, 2026
@nodejs-github-bot
Copy link
Collaborator

@ronag
Copy link
Member

ronag commented Jan 25, 2026

@mcollina I'm confused by the CI failures. Any ideas? Or just flaky?

@anonrig
Copy link
Member

anonrig commented Jan 26, 2026

What does TOS stand for?

@ronag
Copy link
Member

ronag commented Jan 26, 2026

What does TOS stand for?

https://en.wikipedia.org/wiki/Type_of_service

} else {
args.GetReturnValue().Set(uv_translate_sys_error(errno));
}
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't say this needs to block this PR, but upstreaming these additions to https://github.com/libuv/libuv might be a good follow-up change

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even i was thinking the same, if internal members of nodejs will work and do the changes over there to libuv, then additions will be fast, rather than i do.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you can open an issue there pointing to this PR, that will help and maybe someone picks it up.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@santigimeno i already made an issue there, and a member of libuv is working on that, untill that, we could make a todo here in the cpp code to track the isssue and its relevant pr, and will update the libuv api, once it is rolled out. i looked a long on recent issues, prs whch got merged and i saw that it took a lot of time, but as we need to roll out this feature soon in the next minor version, we could consider forward looking at this pr so that we could easily bring the feature

@codecov
Copy link

codecov bot commented Jan 26, 2026

Codecov Report

❌ Patch coverage is 60.75269% with 73 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.76%. Comparing base (5d39030) to head (b04b62f).
⚠️ Report is 27 commits behind head on main.

Files with missing lines Patch % Lines
src/tcp_wrap.cc 57.62% 26 Missing and 24 partials ⚠️
lib/net.js 66.17% 21 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #61503      +/-   ##
==========================================
- Coverage   89.82%   89.76%   -0.07%     
==========================================
  Files         667      672       +5     
  Lines      203693   203881     +188     
  Branches    39163    39203      +40     
==========================================
+ Hits       182963   183007      +44     
- Misses      13061    13177     +116     
- Partials     7669     7697      +28     
Files with missing lines Coverage Δ
src/tcp_wrap.h 71.42% <ø> (ø)
lib/net.js 94.51% <66.17%> (-0.78%) ⬇️
src/tcp_wrap.cc 69.49% <57.62%> (-5.12%) ⬇️

... and 48 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@amyssnippet
Copy link
Author

@ronag kindly approve once again, i fixed the issue related to win vs2022 build error on my win laptop, and also i tested it here. i guess it should work, rest the rhel and linuxone test failures are flaky, i confirmed it by looking the console logs.

also i added a todo for the libuv in future. as i saw it will take time for the required api in the libuv.

libuv/libuv#5011

@ronag ronag changed the title net: add setTOS and getTOS to Socket net: add setTypeOfService and getTypeOfService to Socket Jan 27, 2026
@ronag ronag added the request-ci Add this label to start a Jenkins CI on a PR. label Jan 27, 2026
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Jan 27, 2026
@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@amyssnippet
Copy link
Author

i have fixed all issues from my side, on both linux and windows. all platforms are ready to get served with the 26 pre version. rest ci failures are flaky. Thanks!

@amyssnippet amyssnippet requested review from addaleax and ronag and removed request for ronag January 27, 2026 16:16
if (options.tos !== undefined) {
validateInt32(options.tos, 'options.tos', 0, 255);
}
this[kSetTOS] = options.tos;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ This isn't documented, is it? Should it also be called options.typeOfService instead of options.tos?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have updated the net.md doc, and will roll out in the next commit. and i have kept ADDME in the version, as i dont have much knowledge about versioning, so kindly update me from your side regarding that, i am attaching a screenshot of that here and code as well

image
### `new net.Socket([options])`

<!-- YAML
added: v0.3.4
changes:
  - version: ADDME
    pr-url: https://github.com/nodejs/node/pull/61503
    description: Added `typeOfService` option.
  - version: v15.14.0
    pr-url: https://github.com/nodejs/node/pull/37735
    description: AbortSignal support was added.
  - version: v12.10.0
    pr-url: https://github.com/nodejs/node/pull/25436
    description: Added `onread` option.
-->

* `options` {Object} Available options are:
  * `allowHalfOpen` {boolean} If set to `false`, then the socket will
    automatically end the writable side when the readable side ends. See
    [`net.createServer()`][] and the [`'end'`][] event for details. **Default:**
    `false`.
  * `blockList` {net.BlockList} `blockList` can be used for disabling outbound
    access to specific IP addresses, IP ranges, or IP subnets.
  * `fd` {number} If specified, wrap around an existing socket with
    the given file descriptor, otherwise a new socket will be created.
  * `keepAlive` {boolean} If set to `true`, it enables keep-alive functionality on
    the socket immediately after the connection is established, similarly on what
    is done in [`socket.setKeepAlive()`][]. **Default:** `false`.
  * `keepAliveInitialDelay` {number} If set to a positive number, it sets the
    initial delay before the first keepalive probe is sent on an idle socket. **Default:** `0`.
  * `noDelay` {boolean} If set to `true`, it disables the use of Nagle's algorithm
    immediately after the socket is established. **Default:** `false`.
  * `onread` {Object} If specified, incoming data is stored in a single `buffer`
    and passed to the supplied `callback` when data arrives on the socket.
    This will cause the streaming functionality to not provide any data.
    The socket will emit events like `'error'`, `'end'`, and `'close'`
    as usual. Methods like `pause()` and `resume()` will also behave as
    expected.
    * `buffer` {Buffer|Uint8Array|Function} Either a reusable chunk of memory to
      use for storing incoming data or a function that returns such.
    * `callback` {Function} This function is called for every chunk of incoming
      data. Two arguments are passed to it: the number of bytes written to
      `buffer` and a reference to `buffer`. Return `false` from this function to
      implicitly `pause()` the socket. This function will be executed in the
      global context.
  * `readable` {boolean} Allow reads on the socket when an `fd` is passed,
    otherwise ignored. **Default:** `false`.
  * `signal` {AbortSignal} An Abort signal that may be used to destroy the
    socket.
  * `typeOfService` {number} The initial Type of Service (TOS) value.
  * `writable` {boolean} Allow writes on the socket when an `fd` is passed,
    otherwise ignored. **Default:** `false`.
* Returns: {net.Socket}

Creates a new socket object.

The newly created socket can be either a TCP socket or a streaming [IPC][]
endpoint, depending on what it [`connect()`][`socket.connect()`] to.

Comment on lines +667 to +675
if (!this._handle) {
this[kSetTOS] = tos;
return this;
}

if (!this._handle.setTypeOfService) {
this[kSetTOS] = tos;
return this;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!this._handle) {
this[kSetTOS] = tos;
return this;
}
if (!this._handle.setTypeOfService) {
this[kSetTOS] = tos;
return this;
}
if (!this._handle?.setTypeOfService) {
this[kSetTOS] = tos;
return this;
}

Comment on lines +692 to +699
if (!this._handle) {
// Return cached value if set, otherwise default to 0
return this[kSetTOS] !== undefined ? this[kSetTOS] : 0;
}

if (!this._handle.getTypeOfService) {
return this[kSetTOS] !== undefined ? this[kSetTOS] : 0;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!this._handle) {
// Return cached value if set, otherwise default to 0
return this[kSetTOS] !== undefined ? this[kSetTOS] : 0;
}
if (!this._handle.getTypeOfService) {
return this[kSetTOS] !== undefined ? this[kSetTOS] : 0;
}
if (!this._handle?.getTypeOfService) {
return this[kSetTOS] !== undefined ? this[kSetTOS] : 0;
}

@@ -74,6 +74,8 @@ class TCPWrap : public ConnectionWrap<TCPWrap, uv_tcp_t> {
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetNoDelay(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetKeepAlive(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetTOS(const v8::FunctionCallbackInfo<v8::Value>& args);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use SetTypeOfService rather than these abbreviations please?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was on the way to that, i am parallely doing the changes on my 3 machines, win, linux and mac. constantly building, linting, formatting on a single change, will roll out soon

Copy link
Member

@juanarbol juanarbol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think windows may need a different implementation, with a different API.

Do not use. Type of Service (TOS) settings should only be set using the Quality of Service API. See Differentiated Services in the Quality of Service section of the Platform SDK for more information.

https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options


// 3. Perform the system call (Platform specific casting)
#ifdef _WIN32
if (setsockopt(reinterpret_cast<::SOCKET>(fd),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just FYI, windows is not gonna be very happy about this

Do not use. Type of Service (TOS) settings should only be set using the Quality of Service API. See Differentiated Services in the Quality of Service section of the Platform SDK for more information.

Refs: https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs that have at least one approval, no pending requests for changes, and a CI started. c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. semver-minor PRs that contain new features and should be released in the next minor version.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

socket.setTOS

8 participants