net: add SocketAddress class · nodejs/node@daa8a7b
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+};