Skip to content

Commit d21bb0b

Browse files
authored
feat: Switch to NodeJS's EventEmitter as parent class (#55)
Switch to NodeJS's EventEmitter as parent class as part of the migration to a NodeJS out-of-the-box approach (#41). BREAKING CHANGE: Sockets will no longer return RemovableListeners when calling their addListener() method. Now they inherit all the event methods from Node's EventEmitter class.
1 parent 7977572 commit d21bb0b

File tree

7 files changed

+312
-228
lines changed

7 files changed

+312
-228
lines changed

.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"parser": "babel-eslint",
23
"plugins": ["prettier", "jest", "jsdoc"],
34
"env": {
45
"es6": true,

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,3 @@ buck-out/
5050

5151
# Tests
5252
coverage
53-
!coverage/coverage-final.json

coverage/coverage-final.json

Lines changed: 0 additions & 4 deletions
This file was deleted.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@
5959
"@semantic-release/npm": "^7.0.0",
6060
"@types/jest": "^25.1.3",
6161
"@types/react-native": "^0.61.17",
62+
"babel-eslint": "^10.1.0",
6263
"babel-jest": "^24.9.0",
63-
"eslint": "^6.6.0",
64+
"eslint": "^6.8.0",
6465
"eslint-config-prettier": "^6.5.0",
6566
"eslint-plugin-jest": "^23.6.0",
6667
"eslint-plugin-jsdoc": "^21.0.0",
@@ -74,6 +75,7 @@
7475
"typescript": "^3.8.2"
7576
},
7677
"dependencies": {
77-
"buffer": "^5.4.3"
78+
"buffer": "^5.4.3",
79+
"events": "^3.1.0"
7880
}
7981
}

src/TcpServer.js

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,56 +18,59 @@ export default class TcpServer extends TcpSocket {
1818
this._eventEmitter = eventEmitter;
1919
}
2020

21-
close() {
22-
this.destroy();
23-
this._connections.forEach((clientSocket) => clientSocket.destroy());
21+
/**
22+
* @override
23+
*/
24+
_registerEvents() {
25+
super._registerEvents();
26+
this._connectionsListener = this._eventEmitter.addListener('connection', (evt) => {
27+
if (evt.id !== this._id) return;
28+
this._onConnection(evt.info);
29+
this.emit('connection', evt.info);
30+
});
2431
}
2532

2633
/**
27-
* @param {(arg0: number) => void} callback
34+
* @override
2835
*/
29-
getConnections(callback) {
30-
callback(this._connections.length);
36+
_unregisterEvents() {
37+
super._unregisterEvents();
38+
this._connectionsListener?.remove();
3139
}
3240

3341
/**
34-
* @param {{ port: number; host: any; }} options
35-
* @param {(arg0: any) => void} callback
42+
* @param {{ port: number; host: string; reuseAddress?: boolean}} options
43+
* @param {(arg0: any) => void} [callback]
3644
* @returns {TcpServer}
3745
*/
3846
listen(options, callback) {
39-
let gotOptions = {};
40-
// Normalize args
41-
if (typeof arguments[0] === 'number') {
42-
// Deprecated old version: listen(port[, host][, callback])
43-
console.warn(
44-
'TcpServer.listen(port[, host][, callback]) is deprecated and has been moved to TcpServer.listen(options[, callback]). It will be removed in [email protected]'
45-
);
46-
gotOptions.port = arguments[0];
47-
/** @type {string} */
48-
gotOptions.host = arguments[1];
49-
callback = arguments[2];
50-
} else {
51-
gotOptions = options;
52-
}
47+
const gotOptions = { ...options };
5348
gotOptions.host = gotOptions.host || '0.0.0.0';
54-
const connectListener = this.on('connect', (ev) => {
55-
connectListener.remove();
49+
this.once('connect', (ev) => {
5650
if (callback) callback(ev.address);
5751
});
58-
this._registerEvents();
59-
this.on('connection', (ev) => this._onConnection(ev.info));
6052
Sockets.listen(this._id, gotOptions);
6153
return this;
6254
}
6355

56+
/**
57+
* @param {(arg0: number) => void} callback
58+
*/
59+
getConnections(callback) {
60+
callback(this._connections.length);
61+
}
62+
63+
close() {
64+
this.destroy();
65+
this._connections.forEach((clientSocket) => clientSocket.destroy());
66+
}
67+
6468
/**
6569
* @private
6670
* @param {{ id: number; address: string; }} info
6771
*/
6872
_onConnection(info) {
69-
const socket = new TcpSocket(info.id, this._eventEmitter);
70-
socket.setAsAlreadyConnected(info.address);
73+
const socket = new TcpSocket(info.id, this._eventEmitter, info.address);
7174
this._connections.push(socket);
7275
this.connectionCallback(socket);
7376
}

src/TcpSocket.js

Lines changed: 41 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
import { NativeModules, Image } from 'react-native';
4+
import { EventEmitter } from 'events';
45
const Buffer = (global.Buffer = global.Buffer || require('buffer').Buffer);
56
const Sockets = NativeModules.TcpSockets;
67

@@ -10,27 +11,6 @@ const STATE = {
1011
CONNECTED: 2,
1112
};
1213

13-
class RemovableListener {
14-
/**
15-
* @param {import("react-native").EmitterSubscription} listener
16-
* @param {import("react-native").NativeEventEmitter} eventEmitter
17-
*/
18-
constructor(listener, eventEmitter) {
19-
this._listener = listener;
20-
this._eventEmitter = eventEmitter;
21-
this._removed = false;
22-
}
23-
24-
isRemoved() {
25-
return this._removed;
26-
}
27-
28-
remove() {
29-
this._eventEmitter.removeSubscription(this._listener);
30-
this._removed = true;
31-
}
32-
}
33-
3414
/**
3515
* @typedef {{
3616
* port: number;
@@ -45,99 +25,71 @@ class RemovableListener {
4525
* tlsCert?: any,
4626
* }} ConnectionOptions
4727
*/
48-
export default class TcpSocket {
28+
export default class TcpSocket extends EventEmitter {
4929
/**
5030
* Initialices a TcpSocket.
5131
*
5232
* @param {number} id
5333
* @param {import('react-native').NativeEventEmitter} eventEmitter
34+
* @param {string} [address]
5435
*/
55-
constructor(id, eventEmitter) {
36+
constructor(id, eventEmitter, address) {
37+
super();
5638
this._id = id;
5739
this._eventEmitter = eventEmitter;
5840
/** @type {number} */
5941
this._state = STATE.DISCONNECTED;
60-
/** @type {RemovableListener[]} */
61-
this._listeners = [];
62-
}
63-
64-
/**
65-
* Adds a listener to be invoked when events of the specified type are emitted by the `TcpSocket`.
66-
* An optional calling `context` may be provided.
67-
* The data arguments emitted will be passed to the listener callback.
68-
*
69-
* @param {string} event Name of the event to listen to
70-
* @param {(arg0: any) => void} callback Function to invoke when the specified event is emitted
71-
* @param {any} [context] Optional context object to use when invoking the listener
72-
* @returns {RemovableListener}
73-
*/
74-
on(event, callback, context) {
75-
const newListener = this._selectListener(event, callback, context);
76-
const removableListener = new RemovableListener(newListener, this._eventEmitter);
77-
this._listeners.push(removableListener);
78-
return removableListener;
42+
this._registerEvents();
43+
if (address != undefined) this._setConnected(address);
7944
}
8045

8146
/**
82-
* @private
83-
* @param {string} event
84-
* @param {function(any):void} callback
85-
* @param {any} [context]
47+
* @protected
8648
*/
87-
_selectListener(event, callback, context) {
88-
switch (event) {
89-
case 'data':
90-
return this._eventEmitter.addListener(
91-
'data',
92-
(evt) => {
93-
if (evt.id !== this._id) return;
94-
const bufferTest = Buffer.from(evt.data, 'base64');
95-
callback(bufferTest);
96-
},
97-
context
98-
);
99-
case 'error':
100-
return this._eventEmitter.addListener(
101-
'error',
102-
(evt) => {
103-
if (evt.id !== this._id) return;
104-
callback(evt.error);
105-
},
106-
context
107-
);
108-
default:
109-
return this._eventEmitter.addListener(
110-
event,
111-
(evt) => {
112-
if (evt.id !== this._id) return;
113-
callback(evt);
114-
},
115-
context
116-
);
117-
}
49+
_registerEvents() {
50+
this._unregisterEvents();
51+
this._dataListener = this._eventEmitter.addListener('data', (evt) => {
52+
if (evt.id !== this._id) return;
53+
const bufferTest = Buffer.from(evt.data, 'base64');
54+
this.emit('data', bufferTest);
55+
});
56+
this._errorListener = this._eventEmitter.addListener('error', (evt) => {
57+
if (evt.id !== this._id) return;
58+
this._onError();
59+
this.emit('error', evt.error);
60+
});
61+
this._closeListener = this._eventEmitter.addListener('close', (evt) => {
62+
if (evt.id !== this._id) return;
63+
this._onClose();
64+
this.emit('close', evt.error);
65+
});
66+
this._connectListener = this._eventEmitter.addListener('connect', (evt) => {
67+
if (evt.id !== this._id) return;
68+
this._onConnect(evt.address);
69+
this.emit('connect', evt.address);
70+
});
11871
}
11972

12073
/**
121-
* @deprecated
74+
* @protected
12275
*/
123-
off() {
124-
console.warn(
125-
'TCPSocket.off() is deprecated and produces no effect, please use the listener remove() method instead.'
126-
);
76+
_unregisterEvents() {
77+
this._dataListener?.remove();
78+
this._errorListener?.remove();
79+
this._closeListener?.remove();
80+
this._connectListener?.remove();
12781
}
12882

12983
/**
13084
* @param {ConnectionOptions} options
13185
* @param {(address: string) => void} [callback]
13286
*/
13387
connect(options, callback) {
134-
this._registerEvents();
13588
const customOptions = { ...options };
13689
// Normalize args
13790
customOptions.host = customOptions.host || 'localhost';
13891
customOptions.port = Number(customOptions.port) || 0;
139-
const connectListener = this.on('connect', (ev) => {
140-
connectListener.remove();
92+
this.once('connect', (ev) => {
14193
if (callback) callback(ev.address);
14294
});
14395
// Timeout
@@ -246,36 +198,19 @@ export default class TcpSocket {
246198
}
247199
}
248200

249-
/**
250-
* @protected
251-
*/
252-
_registerEvents() {
253-
this.on('connect', (ev) => this._onConnect(ev.address));
254-
this.on('close', () => this._onClose());
255-
this.on('error', () => this._onError());
256-
}
257-
258-
/**
259-
* @private
260-
*/
261-
_unregisterEvents() {
262-
this._listeners.forEach((listener) => (listener.isRemoved() ? listener.remove() : null));
263-
this._listeners = [];
264-
}
265-
266201
/**
267202
* @private
268203
* @param {string} address
269204
*/
270205
_onConnect(address) {
271-
this.setConnected(address);
206+
this._setConnected(address);
272207
}
273208

274209
/**
275210
* @private
276211
*/
277212
_onClose() {
278-
this.setDisconnected();
213+
this._setDisconnected();
279214
}
280215

281216
/**
@@ -316,14 +251,6 @@ export default class TcpSocket {
316251
);
317252
}
318253

319-
/**
320-
* @param {string} address
321-
*/
322-
setAsAlreadyConnected(address) {
323-
this._registerEvents();
324-
this.setConnected(address);
325-
}
326-
327254
/**
328255
* @private
329256
* @param {string | Buffer | Uint8Array} buffer
@@ -347,15 +274,15 @@ export default class TcpSocket {
347274
* @private
348275
* @param {string} address
349276
*/
350-
setConnected(address) {
277+
_setConnected(address) {
351278
this._state = STATE.CONNECTED;
352279
this._address = address;
353280
}
354281

355282
/**
356283
* @private
357284
*/
358-
setDisconnected() {
285+
_setDisconnected() {
359286
if (this._state === STATE.DISCONNECTED) return;
360287
this._unregisterEvents();
361288
this._state = STATE.DISCONNECTED;

0 commit comments

Comments
 (0)