Skip to content

Commit daa8a7b

Browse files
jasnellMylesBorins
authored andcommitted
net: add SocketAddress class
Signed-off-by: James M Snell <[email protected]> PR-URL: #37917 Reviewed-By: Matteo Collina <[email protected]>
1 parent a4169ce commit daa8a7b

10 files changed

+516
-0
lines changed

doc/api/net.md

+45
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,51 @@ added: v15.0.0
135135

136136
The list of rules added to the blocklist.
137137

138+
## Class: `net.SocketAddress`
139+
<!-- YAML
140+
added: REPLACEME
141+
-->
142+
### `new net.SocketAddress([options])`
143+
<!-- YAML
144+
added: REPLACEME
145+
-->
146+
147+
* `options` {Object}
148+
* `address` {string} The network address as either an IPv4 or IPv6 string.
149+
**Default**: `'127.0.0.1'` if `family` is `'ipv4'`; `'::'` if `family` is
150+
`'ipv6'`.
151+
* `family` {string} One of either `'ipv4'` or 'ipv6'`. **Default**: `'ipv4'`.
152+
* `flowlabel` {number} An IPv6 flow-label used only if `family` is `'ipv6'`.
153+
* `port` {number} An IP port.
154+
155+
### `socketaddress.address`
156+
<!-- YAML
157+
added: REPLACEME
158+
-->
159+
160+
* Type {string}
161+
162+
### `socketaddress.family`
163+
<!-- YAML
164+
added: REPLACEME
165+
-->
166+
167+
* Type {string} Either `'ipv4'` or `'ipv6'`.
168+
169+
### `socketaddress.flowlabel`
170+
<!-- YAML
171+
added: REPLACEME
172+
-->
173+
174+
* Type {number}
175+
176+
### `socketaddress.port`
177+
<!-- YAML
178+
added: REPLACEME
179+
-->
180+
181+
* Type {number}
182+
138183
## Class: `net.Server`
139184
<!-- YAML
140185
added: v0.1.90

doc/api/worker_threads.md

+1
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ In particular, the significant differences to `JSON` are:
573573
* {KeyObject}s,
574574
* {MessagePort}s,
575575
* {net.BlockList}s,
576+
* {net.SocketAddress}es,
576577
* {X509Certificate}s.
577578

578579
```js

lib/internal/socketaddress.js

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
'use strict';
2+
3+
const {
4+
ObjectSetPrototypeOf,
5+
Symbol,
6+
} = primordials;
7+
8+
const {
9+
SocketAddress: _SocketAddress,
10+
AF_INET,
11+
AF_INET6,
12+
} = internalBinding('block_list');
13+
14+
const {
15+
validateObject,
16+
validateString,
17+
validatePort,
18+
validateUint32,
19+
} = require('internal/validators');
20+
21+
const {
22+
codes: {
23+
ERR_INVALID_ARG_VALUE,
24+
},
25+
} = require('internal/errors');
26+
27+
const {
28+
customInspectSymbol: kInspect,
29+
} = require('internal/util');
30+
31+
const { inspect } = require('internal/util/inspect');
32+
33+
const {
34+
JSTransferable,
35+
kClone,
36+
kDeserialize,
37+
} = require('internal/worker/js_transferable');
38+
39+
const kHandle = Symbol('kHandle');
40+
const kDetail = Symbol('kDetail');
41+
42+
class SocketAddress extends JSTransferable {
43+
static isSocketAddress(value) {
44+
return value?.[kHandle] !== undefined;
45+
}
46+
47+
constructor(options = {}) {
48+
super();
49+
validateObject(options, 'options');
50+
const {
51+
family = 'ipv4',
52+
address = (family === 'ipv4' ? '127.0.0.1' : '::'),
53+
port = 0,
54+
flowlabel = 0,
55+
} = options;
56+
57+
let type;
58+
switch (family) {
59+
case 'ipv4':
60+
type = AF_INET;
61+
break;
62+
case 'ipv6':
63+
type = AF_INET6;
64+
break;
65+
default:
66+
throw new ERR_INVALID_ARG_VALUE('options.family', family);
67+
}
68+
69+
validateString(address, 'options.address');
70+
validatePort(port, 'options.port');
71+
validateUint32(flowlabel, 'options.flowlabel', false);
72+
73+
this[kHandle] = new _SocketAddress(address, port, type, flowlabel);
74+
this[kDetail] = this[kHandle].detail({
75+
address: undefined,
76+
port: undefined,
77+
family: undefined,
78+
flowlabel: undefined,
79+
});
80+
}
81+
82+
get address() {
83+
return this[kDetail].address;
84+
}
85+
86+
get port() {
87+
return this[kDetail].port;
88+
}
89+
90+
get family() {
91+
return this[kDetail].family === AF_INET ? 'ipv4' : 'ipv6';
92+
}
93+
94+
get flowlabel() {
95+
// The flow label can be changed internally.
96+
return this[kHandle].flowlabel();
97+
}
98+
99+
[kInspect](depth, options) {
100+
if (depth < 0)
101+
return this;
102+
103+
const opts = {
104+
...options,
105+
depth: options.depth == null ? null : options.depth - 1
106+
};
107+
108+
return `SocketAddress ${inspect(this.toJSON(), opts)}`;
109+
}
110+
111+
[kClone]() {
112+
const handle = this[kHandle];
113+
return {
114+
data: { handle },
115+
deserializeInfo: 'internal/socketaddress:InternalSocketAddress',
116+
};
117+
}
118+
119+
[kDeserialize]({ handle }) {
120+
this[kHandle] = handle;
121+
this[kDetail] = handle.detail({
122+
address: undefined,
123+
port: undefined,
124+
family: undefined,
125+
flowlabel: undefined,
126+
});
127+
}
128+
129+
toJSON() {
130+
return {
131+
address: this.address,
132+
port: this.port,
133+
family: this.family,
134+
flowlabel: this.flowlabel,
135+
};
136+
}
137+
}
138+
139+
class InternalSocketAddress extends JSTransferable {
140+
constructor(handle) {
141+
super();
142+
this[kHandle] = handle;
143+
}
144+
}
145+
146+
InternalSocketAddress.prototype.constructor =
147+
SocketAddress.prototype.construtor;
148+
ObjectSetPrototypeOf(InternalSocketAddress.prototype, SocketAddress.prototype);
149+
150+
module.exports = {
151+
SocketAddress,
152+
InternalSocketAddress,
153+
};

lib/net.js

+5
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ const {
122122
let cluster;
123123
let dns;
124124
let BlockList;
125+
let SocketAddress;
125126

126127
const { clearTimeout } = require('timers');
127128
const { kTimeout } = require('internal/timers');
@@ -1751,6 +1752,10 @@ module.exports = {
17511752
BlockList ??= require('internal/blocklist').BlockList;
17521753
return BlockList;
17531754
},
1755+
get SocketAddress() {
1756+
SocketAddress ??= require('internal/socketaddress').SocketAddress;
1757+
return SocketAddress;
1758+
},
17541759
connect,
17551760
createConnection: connect,
17561761
createServer,

node.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@
212212
'lib/internal/repl/await.js',
213213
'lib/internal/repl/history.js',
214214
'lib/internal/repl/utils.js',
215+
'lib/internal/socketaddress.js',
215216
'lib/internal/socket_list.js',
216217
'lib/internal/source_map/prepare_stack_trace.js',
217218
'lib/internal/source_map/source_map.js',

src/env.h

+2
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ constexpr size_t kFsStatsBufferLength =
258258
V(fingerprint256_string, "fingerprint256") \
259259
V(fingerprint_string, "fingerprint") \
260260
V(flags_string, "flags") \
261+
V(flowlabel_string, "flowlabel") \
261262
V(fragment_string, "fragment") \
262263
V(function_string, "function") \
263264
V(get_data_clone_error_string, "_getDataCloneError") \
@@ -465,6 +466,7 @@ constexpr size_t kFsStatsBufferLength =
465466
V(script_context_constructor_template, v8::FunctionTemplate) \
466467
V(secure_context_constructor_template, v8::FunctionTemplate) \
467468
V(shutdown_wrap_template, v8::ObjectTemplate) \
469+
V(socketaddress_constructor_template, v8::FunctionTemplate) \
468470
V(streambaseoutputstream_constructor_template, v8::ObjectTemplate) \
469471
V(qlogoutputstream_constructor_template, v8::ObjectTemplate) \
470472
V(tcp_constructor_template, v8::FunctionTemplate) \

src/node_errors.h

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ void OnFatalError(const char* location, const char* message);
5656
V(ERR_CRYPTO_JOB_INIT_FAILED, Error) \
5757
V(ERR_DLOPEN_FAILED, Error) \
5858
V(ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE, Error) \
59+
V(ERR_INVALID_ADDRESS, Error) \
5960
V(ERR_INVALID_ARG_VALUE, TypeError) \
6061
V(ERR_OSSL_EVP_INVALID_DIGEST, Error) \
6162
V(ERR_INVALID_ARG_TYPE, TypeError) \
@@ -143,6 +144,7 @@ ERRORS_WITH_CODE(V)
143144
V(ERR_DLOPEN_FAILED, "DLOpen failed") \
144145
V(ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE, \
145146
"Context not associated with Node.js environment") \
147+
V(ERR_INVALID_ADDRESS, "Invalid socket address") \
146148
V(ERR_INVALID_MODULE, "No such module") \
147149
V(ERR_INVALID_THIS, "Value of \"this\" is the wrong type") \
148150
V(ERR_INVALID_TRANSFER_OBJECT, "Found invalid object in transferList") \

0 commit comments

Comments
 (0)