Skip to content

Commit 534a3a9

Browse files
authored
Expose address getters (#14)
1 parent 210b90b commit 534a3a9

File tree

5 files changed

+187
-45
lines changed

5 files changed

+187
-45
lines changed

binding.c

Lines changed: 97 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -598,29 +598,7 @@ bare_tcp_bind(js_env_t *env, js_callback_info_t *info) {
598598
return NULL;
599599
}
600600

601-
struct sockaddr_storage name;
602-
err = uv_tcp_getsockname(&tcp->handle, (struct sockaddr *) &name, &addr_len);
603-
604-
if (err < 0) {
605-
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
606-
assert(err == 0);
607-
608-
return NULL;
609-
}
610-
611-
int local_port;
612-
613-
if (family == 4) {
614-
local_port = ntohs(((struct sockaddr_in *) &name)->sin_port);
615-
} else {
616-
local_port = ntohs(((struct sockaddr_in6 *) &name)->sin6_port);
617-
}
618-
619-
js_value_t *res;
620-
err = js_create_uint32(env, local_port, &res);
621-
assert(err == 0);
622-
623-
return res;
601+
return NULL;
624602
}
625603

626604
static js_value_t *
@@ -871,6 +849,101 @@ bare_tcp_nodelay(js_env_t *env, js_callback_info_t *info) {
871849
return NULL;
872850
}
873851

852+
static js_value_t *
853+
bare_tcp_address(js_env_t *env, js_callback_info_t *info) {
854+
int err;
855+
856+
size_t argc = 2;
857+
js_value_t *argv[2];
858+
859+
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
860+
assert(err == 0);
861+
862+
assert(argc == 2);
863+
864+
bare_tcp_t *tcp;
865+
err = js_get_arraybuffer_info(env, argv[0], (void **) &tcp, NULL);
866+
assert(err == 0);
867+
868+
bool local;
869+
err = js_get_value_bool(env, argv[1], &local);
870+
assert(err == 0);
871+
872+
struct sockaddr_storage addr;
873+
int len = sizeof(addr);
874+
875+
if (local) {
876+
err = uv_tcp_getsockname(&tcp->handle, (struct sockaddr *) &addr, &len);
877+
} else {
878+
err = uv_tcp_getpeername(&tcp->handle, (struct sockaddr *) &addr, &len);
879+
}
880+
881+
if (err < 0) {
882+
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
883+
assert(err == 0);
884+
885+
return NULL;
886+
}
887+
888+
js_value_t *result;
889+
err = js_create_object(env, &result);
890+
assert(err == 0);
891+
892+
js_value_t *result_address;
893+
js_value_t *result_family;
894+
js_value_t *result_port;
895+
896+
if (addr.ss_family == AF_INET) {
897+
struct sockaddr_in *addr_in = (struct sockaddr_in *) &addr;
898+
899+
char address[INET_ADDRSTRLEN];
900+
err = uv_inet_ntop(AF_INET, &(addr_in->sin_addr), address, sizeof(address));
901+
assert(err == 0);
902+
903+
err = js_create_string_utf8(env, (utf8_t *) address, -1, &result_address);
904+
assert(err == 0);
905+
906+
err = js_create_int32(env, 4, &result_family);
907+
assert(err == 0);
908+
909+
err = js_create_int32(env, ntohs(addr_in->sin_port), &result_port);
910+
assert(err == 0);
911+
} else if (addr.ss_family == AF_INET6) {
912+
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *) &addr;
913+
914+
char address[INET6_ADDRSTRLEN];
915+
err = uv_inet_ntop(AF_INET6, &(addr_in6->sin6_addr), address, sizeof(address));
916+
assert(err == 0);
917+
918+
err = js_create_string_utf8(env, (utf8_t *) address, -1, &result_address);
919+
assert(err == 0);
920+
921+
err = js_create_int32(env, 6, &result_family);
922+
assert(err == 0);
923+
924+
err = js_create_int32(env, ntohs(addr_in6->sin6_port), &result_port);
925+
assert(err == 0);
926+
} else {
927+
err = UV_EAI_ADDRFAMILY;
928+
929+
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
930+
assert(err == 0);
931+
932+
return NULL;
933+
}
934+
935+
err = js_set_named_property(env, result, "address", result_address);
936+
assert(err == 0);
937+
938+
err = js_set_named_property(env, result, "family", result_family);
939+
assert(err == 0);
940+
941+
err = js_set_named_property(env, result, "port", result_port);
942+
assert(err == 0);
943+
944+
return result;
945+
}
946+
874947
static js_value_t *
875948
bare_tcp_ref(js_env_t *env, js_callback_info_t *info) {
876949
int err;
@@ -938,6 +1011,7 @@ bare_tcp_exports(js_env_t *env, js_value_t *exports) {
9381011
V("close", bare_tcp_close)
9391012
V("keepalive", bare_tcp_keepalive)
9401013
V("nodelay", bare_tcp_nodelay)
1014+
V("address", bare_tcp_address)
9411015
V("ref", bare_tcp_ref)
9421016
V("unref", bare_tcp_unref)
9431017
#undef V

index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ interface TCPSocket<M extends TCPSocketEvents = TCPSocketEvents> extends Duplex<
4545
readonly pending: boolean
4646
readonly timeout?: number
4747
readonly readyState: 'open' | 'opening'
48+
readonly localAddress?: string
49+
readonly localFamily?: string
50+
readonly localPort?: number
51+
readonly remoteAddress?: string
52+
readonly remoteFamily?: string
53+
readonly remotePort?: number
4854

4955
connect(port: number, host?: string, opts?: TCPSocketConnectOptions, onconnect?: () => void): this
5056
connect(port: number, host: string, onconnect: () => void): this

index.js

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ exports.Socket = class TCPSocket extends Duplex {
1919

2020
this._allowHalfOpen = allowHalfOpen
2121

22-
this._remotePort = -1
23-
this._remoteHost = null
24-
this._remoteFamily = 0
22+
this._localAddress = null
23+
this._remoteAddress = null
2524

2625
this._pendingOpen = null
2726
this._pendingWrite = null
@@ -69,6 +68,30 @@ exports.Socket = class TCPSocket extends Duplex {
6968
return 'opening'
7069
}
7170

71+
get localAddress() {
72+
if (this._state & constants.state.CONNECTED) return this._localAddress.address
73+
}
74+
75+
get localFamily() {
76+
if (this._state & constants.state.CONNECTED) return `IPv${this._localAddress.family}`
77+
}
78+
79+
get localPort() {
80+
if (this._state & constants.state.CONNECTED) return this._localAddress.port
81+
}
82+
83+
get remoteAddress() {
84+
if (this._state & constants.state.CONNECTED) return this._remoteAddress.address
85+
}
86+
87+
get remoteFamily() {
88+
if (this._state & constants.state.CONNECTED) return `IPv${this._remoteAddress.family}`
89+
}
90+
91+
get remotePort() {
92+
if (this._state & constants.state.CONNECTED) return this._remoteAddress.port
93+
}
94+
7295
connect(port, host = 'localhost', opts = {}, onconnect) {
7396
if (this._state & constants.state.CONNECTING || this._state & constants.state.CONNECTED) {
7497
throw errors.SOCKET_ALREADY_CONNECTED('Socket is already connected')
@@ -152,10 +175,6 @@ exports.Socket = class TCPSocket extends Duplex {
152175
if (noDelay) this.setNoDelay()
153176
if (timeout) this.setTimeout(timeout)
154177

155-
this._remotePort = port
156-
this._remoteHost = host
157-
this._remoteFamily = family
158-
159178
if (onconnect) this.once('connect', onconnect)
160179
} catch (err) {
161180
queueMicrotask(() => {
@@ -293,6 +312,9 @@ exports.Socket = class TCPSocket extends Duplex {
293312
_reset() {
294313
this._state = 0
295314

315+
this._localAddress = null
316+
this._remoteAddress = null
317+
296318
binding.reset(this._handle)
297319
}
298320

@@ -315,6 +337,9 @@ exports.Socket = class TCPSocket extends Duplex {
315337
this._state &= ~constants.state.CONNECTING
316338
this._continueOpen()
317339

340+
this._localAddress = binding.address(this._handle, true)
341+
this._remoteAddress = binding.address(this._handle, false)
342+
318343
this.emit('connect')
319344
}
320345

@@ -396,9 +421,7 @@ exports.Server = class TCPServer extends EventEmitter {
396421
this._noDelay = noDelay
397422
this._pauseOnConnect = pauseOnConnect
398423

399-
this._port = -1
400-
this._host = null
401-
this._family = 0
424+
this._address = null
402425
this._connections = new Set()
403426

404427
this._error = null
@@ -420,15 +443,11 @@ exports.Server = class TCPServer extends EventEmitter {
420443
}
421444

422445
address() {
423-
if ((this._state & constants.state.BOUND) === 0) {
424-
return null
425-
}
446+
if ((this._state & constants.state.BOUND) === 0) return null
426447

427-
return {
428-
address: this._host,
429-
family: `IPv${this._family}`,
430-
port: this._port
431-
}
448+
const { address, family, port } = this._address
449+
450+
return { address, family: `IPv${family}`, port }
432451
}
433452

434453
listen(port = 0, host = 'localhost', backlog = 511, opts = {}, onlistening) {
@@ -506,9 +525,10 @@ exports.Server = class TCPServer extends EventEmitter {
506525
if (this._state & constants.state.UNREFED) binding.unref(this._handle)
507526

508527
try {
509-
this._port = binding.bind(this._handle, port, host, backlog, family)
510-
this._host = host
511-
this._family = family
528+
binding.bind(this._handle, port, host, backlog, family)
529+
530+
this._address = binding.address(this._handle, true)
531+
512532
this._state |= constants.state.BOUND
513533
this._state &= ~constants.state.BINDING
514534

@@ -577,6 +597,9 @@ exports.Server = class TCPServer extends EventEmitter {
577597

578598
socket._state |= constants.state.CONNECTED
579599

600+
socket._localAddress = binding.address(socket._handle, true)
601+
socket._remoteAddress = binding.address(socket._handle, false)
602+
580603
this._connections.add(socket)
581604

582605
if (this._keepAlive) socket.setKeepAlive(this._keepAlive, this._keepAliveInitialDelay)
@@ -601,6 +624,7 @@ exports.Server = class TCPServer extends EventEmitter {
601624
this._state &= ~constants.state.BINDING
602625
this._error = null
603626
this._handle = null
627+
this._address = null
604628

605629
if (err) this.emit('error', err)
606630
else this.emit('close')

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bare-tcp",
3-
"version": "2.2.2",
3+
"version": "2.2.2-0",
44
"description": "Native TCP sockets for JavaScript",
55
"exports": {
66
".": {

test.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,44 @@ test('socket state getters', async (t) => {
7979
server.close()
8080
})
8181

82+
test('address getters', async (t) => {
83+
t.plan(14)
84+
85+
const server = createServer()
86+
.on('connection', (socket) => {
87+
t.is(socket.localAddress, '127.0.0.1')
88+
t.is(socket.localFamily, 'IPv4')
89+
t.is(typeof socket.localPort, 'number')
90+
91+
t.is(socket.remoteAddress, '127.0.0.1')
92+
t.is(socket.remoteFamily, 'IPv4')
93+
t.is(typeof socket.remotePort, 'number')
94+
95+
t.ok(socket.localPort !== socket.remotePort)
96+
97+
socket.on('close', () => server.close()).end()
98+
})
99+
.listen(0, '127.0.0.1')
100+
101+
await waitForServer(server)
102+
103+
const { port: serverPort } = server.address()
104+
105+
const socket = createConnection(serverPort)
106+
.on('connect', () => {
107+
t.is(socket.localAddress, '127.0.0.1')
108+
t.is(socket.localFamily, 'IPv4')
109+
t.is(typeof socket.localPort, 'number')
110+
111+
t.is(socket.remoteAddress, '127.0.0.1')
112+
t.is(socket.remoteFamily, 'IPv4')
113+
t.is(socket.remotePort, serverPort)
114+
115+
t.ok(socket.localPort !== socket.remotePort)
116+
})
117+
.end()
118+
})
119+
82120
test('port already in use', async (t) => {
83121
t.plan(1)
84122

0 commit comments

Comments
 (0)