Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
eb53d09
net: add setTOS and getTOS to Socket
amyssnippet Jan 24, 2026
9e7a183
added doc
amyssnippet Jan 24, 2026
14b3fb0
fixed some lint issues
amyssnippet Jan 24, 2026
f339cd0
already complete with validation and platform specific tests
amyssnippet Jan 24, 2026
d32f212
added: REPLACEME
amyssnippet Jan 24, 2026
71ecbd7
common.mustCall used
amyssnippet Jan 24, 2026
c0407b2
undo: unrelated header was removed irrelevantly
amyssnippet Jan 24, 2026
8439cfd
fix: resolve windows compilation and linter issues
amyssnippet Jan 25, 2026
fe329db
rollback to tcp only
amyssnippet Jan 25, 2026
778580a
fix: ensure getTOS returns numeric default when handle lacks getTOS
amyssnippet Jan 26, 2026
efb5f88
fix: handle setTOS errors in afterConnect and use options.tos
amyssnippet Jan 26, 2026
8dce839
fix: capture errno immediately after getsockopt failures
amyssnippet Jan 26, 2026
1fb4683
fix: use correct length type for Windows getsockopt and fix DSCP mask…
amyssnippet Jan 26, 2026
1084571
fix: validate options.tos in constructor
amyssnippet Jan 26, 2026
0a90f1c
fix: use validateInt32 for options.tos and standardize GetTOS error
amyssnippet Jan 26, 2026
603445d
fix: remove unused errno_val in TCPWrap::GetTOS POSIX path
amyssnippet Jan 26, 2026
f22a74c
test: add boundary value assertions for TOS
amyssnippet Jan 26, 2026
b272fda
test: add pre-connect TOS caching test
amyssnippet Jan 26, 2026
c6f1153
doc: add notes for setTOS and getTOS about pre-connect caching and OS…
amyssnippet Jan 26, 2026
fe14b34
fix: import UV_EBADF and remove duplicate mask declaration
amyssnippet Jan 26, 2026
041c30a
fix: update TOS cache when handle lacks setTOS
amyssnippet Jan 26, 2026
7516aff
doc: clarify pre-connect TOS test comment
amyssnippet Jan 26, 2026
83d875c
refined: comments
amyssnippet Jan 26, 2026
c03162b
doc: remove prohibited 'Note that' phrases
amyssnippet Jan 26, 2026
b884430
fix: reorder includes for Windows compatibility and add missing POSIX…
amyssnippet Jan 26, 2026
c39d13e
fix: resolve windows header order and doc lint errors
amyssnippet Jan 26, 2026
98747a7
feat: rename setTOS/getTOS to setTypeOfService/getTypeOfService for A…
amyssnippet Jan 26, 2026
551a5d3
fix: update internal handle calls to use new TypeOfService method names
amyssnippet Jan 26, 2026
2bb4591
win vs2022 ci fix
amyssnippet Jan 27, 2026
a2d4c36
added: todo for libuv api
amyssnippet Jan 27, 2026
b04b62f
fixed:win vs2022 lint ci
amyssnippet Jan 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions doc/api/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -1461,6 +1461,45 @@ If `timeout` is 0, then the existing idle timeout is disabled.
The optional `callback` parameter will be added as a one-time listener for the
[`'timeout'`][] event.

### `socket.getTypeOfService()`

<!-- YAML
added: REPLACEME
-->

* Returns: {integer} The current TOS value.

Returns the current Type of Service (TOS) field for IPv4 packets or Traffic
Class for IPv6 packets for this socket.

`setTypeOfService()` may be called before the socket is connected; the value
will be cached and applied when the socket establishes a connection.
`getTypeOfService()` will return the currently set value even before connection.

On some platforms (e.g., Linux), certain TOS/ECN bits may be masked or ignored,
and behavior can differ between IPv4 and IPv6 or dual-stack sockets. Callers
should verify platform-specific semantics.

### `socket.setTypeOfService(tos)`

<!-- YAML
added: REPLACEME
-->

* `tos` {integer} The TOS value to set (0-255).
* 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.

`setTypeOfService()` may be called before the socket is connected; the value
will be cached and applied when the socket establishes a connection.
`getTypeOfService()` will return the currently set value even before connection.

On some platforms (e.g., Linux), certain TOS/ECN bits may be masked or ignored,
and behavior can differ between IPv4 and IPv6 or dual-stack sockets. Callers
should verify platform-specific semantics.

### `socket.timeout`

<!-- YAML
Expand Down
68 changes: 68 additions & 0 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const {
const assert = require('internal/assert');
const {
UV_EADDRINUSE,
UV_EBADF,
UV_EINVAL,
UV_ENOTCONN,
UV_ECANCELED,
Expand Down Expand Up @@ -358,6 +359,7 @@ const kBytesWritten = Symbol('kBytesWritten');
const kSetNoDelay = Symbol('kSetNoDelay');
const kSetKeepAlive = Symbol('kSetKeepAlive');
const kSetKeepAliveInitialDelay = Symbol('kSetKeepAliveInitialDelay');
const kSetTOS = Symbol('kSetTOS');

function Socket(options) {
if (!(this instanceof Socket)) return new Socket(options);
Expand Down Expand Up @@ -473,6 +475,10 @@ function Socket(options) {
this[kSetNoDelay] = Boolean(options.noDelay);
this[kSetKeepAlive] = Boolean(options.keepAlive);
this[kSetKeepAliveInitialDelay] = ~~(options.keepAliveInitialDelay / 1000);
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.


// Shut down the socket when we're finished with it.
this.on('end', onReadableStreamEnd);
Expand Down Expand Up @@ -652,6 +658,59 @@ Socket.prototype.setKeepAlive = function(enable, initialDelayMsecs) {
};


Socket.prototype.setTypeOfService = function(tos) {
if (NumberIsNaN(tos)) {
throw new ERR_INVALID_ARG_TYPE('tos', 'number', tos);
}
validateInt32(tos, 'tos', 0, 255);

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

if (!this._handle.setTypeOfService) {
this[kSetTOS] = tos;
return this;
}
Comment on lines +667 to +675
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;
}


if (tos !== this[kSetTOS]) {
this[kSetTOS] = tos;
const err = this._handle.setTypeOfService(tos);
// On Windows, setting TOS is often restricted or returns error codes even if partially applied.
// We treat this as a "best effort" operation and do not throw on Windows.
if (err && !isWindows) {
throw new ErrnoException(err, 'setTypeOfService');
}
}

return this;
};


Socket.prototype.getTypeOfService = function() {
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;
}
Comment on lines +692 to +699
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;
}


const res = this._handle.getTypeOfService();
if (typeof res === 'number' && res < 0) {
// On Windows, getsockopt(IP_TOS) often fails. In that case, fall back
// to the cached value we attempted to set, or 0.
if (isWindows) {
return this[kSetTOS] !== undefined ? this[kSetTOS] : 0;
}
throw new ErrnoException(res, 'getTypeOfService');
}
return res;
};


Socket.prototype.address = function() {
return this._getsockname();
};
Expand Down Expand Up @@ -1619,6 +1678,15 @@ function afterConnect(status, handle, req, readable, writable) {
self._handle.setKeepAlive(true, self[kSetKeepAliveInitialDelay]);
}

if (self[kSetTOS] !== undefined && self._handle.setTypeOfService) {
const err = self._handle.setTypeOfService(self[kSetTOS]);
// On Windows, setting TOS is best-effort. If it fails, we shouldn't destroy
// the connection or emit an error, as the socket is otherwise healthy.
if (err && err !== UV_EBADF && !isWindows) {
self.emit('error', new ErrnoException(err, 'setTypeOfService'));
}
}

self.emit('connect');
self.emit('ready');

Expand Down
Loading
Loading