@streamr/dht 100.0.0-testnet-one.0 → 100.0.0-testnet-one.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/connection/ConnectionLockRpcRemote.js +1 -25
- package/dist/src/connection/ConnectionLockRpcRemote.js.map +1 -1
- package/dist/src/connection/ConnectionManager.d.ts +0 -1
- package/dist/src/connection/ConnectionManager.js +7 -6
- package/dist/src/connection/ConnectionManager.js.map +1 -1
- package/dist/src/connection/ConnectorFacade.d.ts +2 -2
- package/dist/src/connection/ConnectorFacade.js +2 -3
- package/dist/src/connection/ConnectorFacade.js.map +1 -1
- package/dist/src/connection/ManagedConnection.d.ts +1 -0
- package/dist/src/connection/ManagedConnection.js +11 -1
- package/dist/src/connection/ManagedConnection.js.map +1 -1
- package/dist/src/connection/connectivityChecker.js +3 -2
- package/dist/src/connection/connectivityChecker.js.map +1 -1
- package/dist/src/connection/websocket/ClientWebsocket.d.ts +1 -0
- package/dist/src/connection/websocket/ClientWebsocket.js +6 -3
- package/dist/src/connection/websocket/ClientWebsocket.js.map +1 -1
- package/dist/src/connection/websocket/ServerWebsocket.d.ts +4 -0
- package/dist/src/connection/websocket/ServerWebsocket.js +32 -21
- package/dist/src/connection/websocket/ServerWebsocket.js.map +1 -1
- package/dist/src/connection/websocket/WebsocketConnector.d.ts +0 -1
- package/dist/src/connection/websocket/WebsocketConnector.js +21 -10
- package/dist/src/connection/websocket/WebsocketConnector.js.map +1 -1
- package/dist/src/connection/websocket/WebsocketConnectorRpcLocal.d.ts +1 -1
- package/dist/src/connection/websocket/WebsocketConnectorRpcLocal.js +8 -11
- package/dist/src/connection/websocket/WebsocketConnectorRpcLocal.js.map +1 -1
- package/dist/src/connection/websocket/WebsocketConnectorRpcRemote.d.ts +2 -2
- package/dist/src/connection/websocket/WebsocketConnectorRpcRemote.js +3 -37
- package/dist/src/connection/websocket/WebsocketConnectorRpcRemote.js.map +1 -1
- package/dist/src/connection/websocket/WebsocketServer.js +21 -4
- package/dist/src/connection/websocket/WebsocketServer.js.map +1 -1
- package/dist/src/dht/DhtNode.d.ts +4 -4
- package/dist/src/dht/DhtNode.js +31 -20
- package/dist/src/dht/DhtNode.js.map +1 -1
- package/dist/src/dht/DhtNodeRpcLocal.d.ts +1 -4
- package/dist/src/dht/DhtNodeRpcLocal.js +1 -5
- package/dist/src/dht/DhtNodeRpcLocal.js.map +1 -1
- package/dist/src/dht/PeerManager.d.ts +10 -6
- package/dist/src/dht/PeerManager.js +95 -30
- package/dist/src/dht/PeerManager.js.map +1 -1
- package/dist/src/dht/contact/SortedContactList.d.ts +20 -6
- package/dist/src/dht/contact/SortedContactList.js +55 -24
- package/dist/src/dht/contact/SortedContactList.js.map +1 -1
- package/dist/src/dht/discovery/DiscoverySession.d.ts +3 -5
- package/dist/src/dht/discovery/DiscoverySession.js +11 -9
- package/dist/src/dht/discovery/DiscoverySession.js.map +1 -1
- package/dist/src/dht/discovery/PeerDiscovery.d.ts +4 -2
- package/dist/src/dht/discovery/PeerDiscovery.js +17 -16
- package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -1
- package/dist/src/dht/find/FindSession.js +6 -1
- package/dist/src/dht/find/FindSession.js.map +1 -1
- package/dist/src/dht/find/Finder.js +6 -1
- package/dist/src/dht/find/Finder.js.map +1 -1
- package/dist/src/dht/routing/Router.d.ts +1 -1
- package/dist/src/dht/routing/Router.js +8 -4
- package/dist/src/dht/routing/Router.js.map +1 -1
- package/dist/src/dht/routing/RoutingSession.js +8 -1
- package/dist/src/dht/routing/RoutingSession.js.map +1 -1
- package/dist/src/dht/store/StoreRpcLocal.js +19 -5
- package/dist/src/dht/store/StoreRpcLocal.js.map +1 -1
- package/dist/src/helpers/PeerID.d.ts +1 -0
- package/dist/src/helpers/PeerID.js +7 -2
- package/dist/src/helpers/PeerID.js.map +1 -1
- package/dist/src/helpers/peerIdFromPeerDescriptor.js +2 -2
- package/dist/src/helpers/peerIdFromPeerDescriptor.js.map +1 -1
- package/package.json +5 -5
- package/src/connection/ConnectionLockRpcRemote.ts +1 -2
- package/src/connection/ConnectionManager.ts +16 -17
- package/src/connection/ConnectorFacade.ts +1 -4
- package/src/connection/ManagedConnection.ts +12 -1
- package/src/connection/connectivityChecker.ts +3 -2
- package/src/connection/websocket/ClientWebsocket.ts +5 -2
- package/src/connection/websocket/ServerWebsocket.ts +40 -25
- package/src/connection/websocket/WebsocketConnector.ts +23 -12
- package/src/connection/websocket/WebsocketConnectorRpcLocal.ts +9 -11
- package/src/connection/websocket/WebsocketConnectorRpcRemote.ts +5 -14
- package/src/connection/websocket/WebsocketServer.ts +20 -5
- package/src/dht/DhtNode.ts +32 -24
- package/src/dht/DhtNodeRpcLocal.ts +2 -9
- package/src/dht/PeerManager.ts +110 -36
- package/src/dht/contact/SortedContactList.ts +87 -44
- package/src/dht/discovery/DiscoverySession.ts +15 -14
- package/src/dht/discovery/PeerDiscovery.ts +37 -22
- package/src/dht/find/FindSession.ts +6 -1
- package/src/dht/find/Finder.ts +6 -7
- package/src/dht/routing/Router.ts +8 -4
- package/src/dht/routing/RoutingSession.ts +8 -8
- package/src/dht/store/StoreRpcLocal.ts +19 -7
- package/src/helpers/PeerID.ts +6 -2
- package/src/helpers/peerIdFromPeerDescriptor.ts +4 -4
- package/test/benchmark/Find.test.ts +1 -1
- package/test/benchmark/KademliaCorrectness.test.ts +1 -1
- package/test/benchmark/SortedContactListBenchmark.test.ts +150 -0
- package/test/benchmark/WebsocketServerMemoryLeak.test.ts +41 -0
- package/test/benchmark/kademlia-simulation/SimulationNode.ts +6 -1
- package/test/end-to-end/Layer0.test.ts +4 -4
- package/test/end-to-end/Layer0MixedConnectionTypes.test.ts +10 -10
- package/test/end-to-end/Layer0Webrtc-Layer1.test.ts +4 -4
- package/test/end-to-end/Layer1-Scale-WebSocket.test.ts +2 -2
- package/test/end-to-end/Layer1-Scale-Webrtc.test.ts +2 -2
- package/test/end-to-end/RecoveryFromFailedAutoCertification.test.ts +1 -1
- package/test/end-to-end/memory-leak.test.ts +1 -0
- package/test/integration/DhtJoinPeerDiscovery.test.ts +2 -2
- package/test/integration/Layer1-scale.test.ts +1 -1
- package/test/integration/Mock-Layer1-Layer0.test.ts +15 -15
- package/test/integration/MultipleEntryPointJoining.test.ts +7 -7
- package/test/integration/ReplicateData.test.ts +6 -1
- package/test/integration/SimultaneousConnections.test.ts +81 -49
- package/test/integration/Store.test.ts +1 -1
- package/test/integration/StoreOnDhtWithTwoNodes.test.ts +1 -1
- package/test/integration/WebrtcConnectionManagement.test.ts +29 -0
- package/test/integration/WebsocketConnectionManagement.test.ts +65 -4
- package/test/integration/WebsocketConnectorRpc.test.ts +3 -5
- package/test/unit/SortedContactList.test.ts +15 -10
|
@@ -95,7 +95,7 @@ const INTERNAL_SERVICE_ID = 'system/connection-manager'
|
|
|
95
95
|
// - if we create stricter types for incoming messages (message.sourceDescriptor or
|
|
96
96
|
// disconnectNotice.peerDescriptor)
|
|
97
97
|
// - if ManagedConnection#peerDescriptor is never undefined
|
|
98
|
-
export const getNodeIdOrUnknownFromPeerDescriptor = (peerDescriptor: PeerDescriptor | undefined): string => {
|
|
98
|
+
export const getNodeIdOrUnknownFromPeerDescriptor = (peerDescriptor: PeerDescriptor | undefined): string => {
|
|
99
99
|
if (peerDescriptor !== undefined) {
|
|
100
100
|
return getNodeIdFromPeerDescriptor(peerDescriptor)
|
|
101
101
|
} else {
|
|
@@ -158,7 +158,12 @@ export class ConnectionManager extends EventEmitter<TransportEvents> implements
|
|
|
158
158
|
if (this.connections.size <= maxConnections) {
|
|
159
159
|
return
|
|
160
160
|
}
|
|
161
|
-
const disconnectionCandidates = new SortedContactList<Contact>(
|
|
161
|
+
const disconnectionCandidates = new SortedContactList<Contact>({
|
|
162
|
+
referenceId: peerIdFromPeerDescriptor(this.getLocalPeerDescriptor()),
|
|
163
|
+
maxSize: 100000, // TODO use config option or named constant?
|
|
164
|
+
allowToContainReferenceId: false,
|
|
165
|
+
emitEvents: false
|
|
166
|
+
})
|
|
162
167
|
this.connections.forEach((connection) => {
|
|
163
168
|
if (!this.locks.isLocked(connection.peerIdKey) && Date.now() - connection.getLastUsed() > lastUsedLimit) {
|
|
164
169
|
logger.trace('disconnecting in timeout interval: ' + getNodeIdOrUnknownFromPeerDescriptor(connection.getPeerDescriptor()))
|
|
@@ -182,7 +187,6 @@ export class ConnectionManager extends EventEmitter<TransportEvents> implements
|
|
|
182
187
|
logger.trace(`Starting ConnectionManager...`)
|
|
183
188
|
await this.connectorFacade.start(
|
|
184
189
|
(connection: ManagedConnection) => this.onNewConnection(connection),
|
|
185
|
-
(peerDescriptor: PeerDescriptor) => this.canConnect(peerDescriptor),
|
|
186
190
|
this
|
|
187
191
|
)
|
|
188
192
|
// Garbage collection of connections
|
|
@@ -275,14 +279,14 @@ export class ConnectionManager extends EventEmitter<TransportEvents> implements
|
|
|
275
279
|
return connection.send(binary, doNotConnect)
|
|
276
280
|
}
|
|
277
281
|
|
|
278
|
-
private isConnectionToSelf(peerDescriptor: PeerDescriptor): boolean {
|
|
282
|
+
private isConnectionToSelf(peerDescriptor: PeerDescriptor): boolean {
|
|
279
283
|
return areEqualPeerDescriptors(peerDescriptor, this.getLocalPeerDescriptor()) || this.isOwnWebsocketServer(peerDescriptor)
|
|
280
284
|
}
|
|
281
285
|
|
|
282
286
|
private isOwnWebsocketServer(peerDescriptor: PeerDescriptor): boolean {
|
|
283
287
|
const localPeerDescriptor = this.getLocalPeerDescriptor()
|
|
284
288
|
if ((peerDescriptor.websocket !== undefined) && (localPeerDescriptor.websocket !== undefined)) {
|
|
285
|
-
return ((peerDescriptor.websocket.port === localPeerDescriptor.websocket.port)
|
|
289
|
+
return ((peerDescriptor.websocket.port === localPeerDescriptor.websocket.port)
|
|
286
290
|
&& (peerDescriptor.websocket.host === localPeerDescriptor.websocket.host))
|
|
287
291
|
} else {
|
|
288
292
|
return false
|
|
@@ -313,11 +317,6 @@ export class ConnectionManager extends EventEmitter<TransportEvents> implements
|
|
|
313
317
|
return this.locks.isRemoteLocked(peerIdKey)
|
|
314
318
|
}
|
|
315
319
|
|
|
316
|
-
private canConnect(peerDescriptor: PeerDescriptor): boolean {
|
|
317
|
-
// Perhaps the connection's state should be checked here
|
|
318
|
-
return !this.hasConnection(peerDescriptor) // TODO: Add port range check
|
|
319
|
-
}
|
|
320
|
-
|
|
321
320
|
public handleMessage(message: Message): void {
|
|
322
321
|
logger.trace('Received message of type ' + message.messageType)
|
|
323
322
|
if (message.messageType !== MessageType.RPC) {
|
|
@@ -325,7 +324,7 @@ export class ConnectionManager extends EventEmitter<TransportEvents> implements
|
|
|
325
324
|
return
|
|
326
325
|
}
|
|
327
326
|
if (this.duplicateMessageDetector.isMostLikelyDuplicate(message.messageId)) {
|
|
328
|
-
logger.trace('handleMessage filtered duplicate ' + getNodeIdFromPeerDescriptor(message.sourceDescriptor!)
|
|
327
|
+
logger.trace('handleMessage filtered duplicate ' + getNodeIdFromPeerDescriptor(message.sourceDescriptor!)
|
|
329
328
|
+ ' ' + message.serviceId + ' ' + message.messageId)
|
|
330
329
|
return
|
|
331
330
|
}
|
|
@@ -333,7 +332,7 @@ export class ConnectionManager extends EventEmitter<TransportEvents> implements
|
|
|
333
332
|
if (message.serviceId === INTERNAL_SERVICE_ID) {
|
|
334
333
|
this.rpcCommunicator?.handleMessageFromPeer(message)
|
|
335
334
|
} else {
|
|
336
|
-
logger.trace('emit "message" ' + getNodeIdFromPeerDescriptor(message.sourceDescriptor!)
|
|
335
|
+
logger.trace('emit "message" ' + getNodeIdFromPeerDescriptor(message.sourceDescriptor!)
|
|
337
336
|
+ ' ' + message.serviceId + ' ' + message.messageId)
|
|
338
337
|
this.emit('message', message)
|
|
339
338
|
}
|
|
@@ -376,16 +375,16 @@ export class ConnectionManager extends EventEmitter<TransportEvents> implements
|
|
|
376
375
|
if (storedConnection && storedConnection.connectionId.equals(connection.connectionId)) {
|
|
377
376
|
this.locks.clearAllLocks(peerIdKey)
|
|
378
377
|
this.connections.delete(peerIdKey)
|
|
379
|
-
logger.trace(getNodeIdOrUnknownFromPeerDescriptor(connection.getPeerDescriptor())
|
|
378
|
+
logger.trace(getNodeIdOrUnknownFromPeerDescriptor(connection.getPeerDescriptor())
|
|
380
379
|
+ ' deleted connection in onDisconnected() gracefulLeave: ' + gracefulLeave)
|
|
381
380
|
this.emit('disconnected', connection.getPeerDescriptor()!, gracefulLeave)
|
|
382
381
|
this.onConnectionCountChange()
|
|
383
382
|
} else {
|
|
384
|
-
logger.trace(getNodeIdOrUnknownFromPeerDescriptor(connection.getPeerDescriptor())
|
|
383
|
+
logger.trace(getNodeIdOrUnknownFromPeerDescriptor(connection.getPeerDescriptor())
|
|
385
384
|
+ ' onDisconnected() did nothing, no such connection in connectionManager')
|
|
386
385
|
if (storedConnection) {
|
|
387
386
|
logger.trace(getNodeIdOrUnknownFromPeerDescriptor(connection.getPeerDescriptor())
|
|
388
|
-
|
|
387
|
+
+ ' connectionIds do not match ' + storedConnection.connectionId.toString() + ' ' + connection.connectionId.toString())
|
|
389
388
|
}
|
|
390
389
|
}
|
|
391
390
|
|
|
@@ -426,11 +425,11 @@ export class ConnectionManager extends EventEmitter<TransportEvents> implements
|
|
|
426
425
|
const oldConnection = this.connections.get(newPeerID.toKey())!
|
|
427
426
|
logger.trace('replaced: ' + getNodeIdFromPeerDescriptor(newConnection.getPeerDescriptor()!))
|
|
428
427
|
const buffer = oldConnection.stealOutputBuffer()
|
|
429
|
-
|
|
428
|
+
|
|
430
429
|
for (const data of buffer) {
|
|
431
430
|
newConnection.sendNoWait(data)
|
|
432
431
|
}
|
|
433
|
-
|
|
432
|
+
|
|
434
433
|
oldConnection.reportBufferSentByOtherConnection()
|
|
435
434
|
oldConnection.replacedByOtherConnection = true
|
|
436
435
|
} else {
|
|
@@ -16,7 +16,6 @@ export interface ConnectorFacade {
|
|
|
16
16
|
getLocalPeerDescriptor: () => PeerDescriptor | undefined
|
|
17
17
|
start: (
|
|
18
18
|
onNewConnection: (connection: ManagedConnection) => boolean,
|
|
19
|
-
canConnect: (peerDescriptor: PeerDescriptor) => boolean,
|
|
20
19
|
autoCertifierTransport: ITransport
|
|
21
20
|
) => Promise<void>
|
|
22
21
|
stop: () => Promise<void>
|
|
@@ -59,14 +58,12 @@ export class DefaultConnectorFacade implements ConnectorFacade {
|
|
|
59
58
|
|
|
60
59
|
async start(
|
|
61
60
|
onNewConnection: (connection: ManagedConnection) => boolean,
|
|
62
|
-
canConnect: (peerDescriptor: PeerDescriptor) => boolean,
|
|
63
61
|
autoCertifierTransport: ITransport
|
|
64
62
|
): Promise<void> {
|
|
65
63
|
logger.trace(`Creating WebsocketConnectorRpcLocal`)
|
|
66
64
|
const webSocketConnectorConfig = {
|
|
67
65
|
transport: this.config.transport,
|
|
68
66
|
// TODO should we use canConnect also for WebrtcConnector? (NET-1142)
|
|
69
|
-
canConnect: (peerDescriptor: PeerDescriptor) => canConnect(peerDescriptor),
|
|
70
67
|
onNewConnection,
|
|
71
68
|
portRange: this.config.websocketPortRange,
|
|
72
69
|
host: this.config.websocketHost,
|
|
@@ -115,7 +112,7 @@ export class DefaultConnectorFacade implements ConnectorFacade {
|
|
|
115
112
|
})
|
|
116
113
|
}
|
|
117
114
|
} catch (err) {
|
|
118
|
-
logger.warn('Failed to auto-certify, disabling WebSocket server TLS')
|
|
115
|
+
logger.warn('Failed to auto-certify, disabling WebSocket server TLS', { error: err })
|
|
119
116
|
await this.restartWebsocketConnector({
|
|
120
117
|
...webSocketConnectorConfig,
|
|
121
118
|
serverEnableTls: false
|
|
@@ -57,6 +57,9 @@ export class ManagedConnection extends EventEmitter<Events> {
|
|
|
57
57
|
protected outgoingConnection?: IConnection
|
|
58
58
|
protected incomingConnection?: IConnection
|
|
59
59
|
|
|
60
|
+
// TODO: Temporary debug variable, should be removed in the future.
|
|
61
|
+
private created = Date.now()
|
|
62
|
+
|
|
60
63
|
constructor(
|
|
61
64
|
localPeerDescriptor: PeerDescriptor,
|
|
62
65
|
connectionType: ConnectionType,
|
|
@@ -252,7 +255,12 @@ export class ManagedConnection extends EventEmitter<Events> {
|
|
|
252
255
|
result = await runAndRaceEvents3<OutpuBufferEvents>([() => { this.outputBuffer.push(data) }],
|
|
253
256
|
this.outputBufferEmitter, ['bufferSent', 'bufferSendingFailed'], 15000)
|
|
254
257
|
} catch (e) {
|
|
255
|
-
logger.debug(`Connection to ${getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor)} timed out
|
|
258
|
+
logger.debug(`Connection to ${getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor)} timed out`, {
|
|
259
|
+
peerDescriptor: this.remotePeerDescriptor,
|
|
260
|
+
type: this.connectionType,
|
|
261
|
+
lifetime: Date.now() - this.created
|
|
262
|
+
})
|
|
263
|
+
await this.close(false)
|
|
256
264
|
throw new Err.SendFailed('Sending buffer timed out')
|
|
257
265
|
}
|
|
258
266
|
|
|
@@ -307,6 +315,9 @@ export class ManagedConnection extends EventEmitter<Events> {
|
|
|
307
315
|
}
|
|
308
316
|
|
|
309
317
|
public async close(gracefulLeave: boolean): Promise<void> {
|
|
318
|
+
if (this.closing) {
|
|
319
|
+
return
|
|
320
|
+
}
|
|
310
321
|
if (this.replacedByOtherConnection) {
|
|
311
322
|
logger.trace('close() called on replaced connection')
|
|
312
323
|
}
|
|
@@ -44,13 +44,14 @@ export const sendConnectivityRequest = async (
|
|
|
44
44
|
tls: entryPoint.websocket!.tls,
|
|
45
45
|
}
|
|
46
46
|
const url = connectivityMethodToWebsocketUrl(wsServerInfo, 'connectivityRequest')
|
|
47
|
+
logger.debug(`Attempting connectivity check with entrypoint ${url}`)
|
|
47
48
|
try {
|
|
48
49
|
outgoingConnection = await connectAsync({
|
|
49
50
|
url,
|
|
50
51
|
selfSigned: request.selfSigned
|
|
51
52
|
})
|
|
52
53
|
} catch (e) {
|
|
53
|
-
throw new Err.ConnectionFailed(`Failed to connect to
|
|
54
|
+
throw new Err.ConnectionFailed(`Failed to connect to entrypoint for connectivity check: ${url}`, e)
|
|
54
55
|
}
|
|
55
56
|
// send connectivity request
|
|
56
57
|
const msg: Message = {
|
|
@@ -74,7 +75,7 @@ export const sendConnectivityRequest = async (
|
|
|
74
75
|
try {
|
|
75
76
|
const message: Message = Message.fromBinary(bytes)
|
|
76
77
|
if (message.body.oneofKind === 'connectivityResponse') {
|
|
77
|
-
logger.
|
|
78
|
+
logger.debug('ConnectivityResponse received: ' + JSON.stringify(Message.toJson(message)))
|
|
78
79
|
const connectivityResponseMessage = message.body.connectivityResponse
|
|
79
80
|
outgoingConnection!.off('data', listener)
|
|
80
81
|
clearTimeout(timeoutId)
|
|
@@ -8,6 +8,9 @@ const logger = new Logger(module)
|
|
|
8
8
|
// https://kapeli.com/cheat_sheets/WebSocket_Status_Codes.docset/Contents/Resources/Documents/index
|
|
9
9
|
// Browsers send this automatically when closing a tab
|
|
10
10
|
export const GOING_AWAY = 1001
|
|
11
|
+
// The GOING_AWAY is a reserved code and we shouldn't send that from the application. Therefore
|
|
12
|
+
// we have a custom counterpart
|
|
13
|
+
export const CUSTOM_GOING_AWAY = 3001
|
|
11
14
|
|
|
12
15
|
const BINARY_TYPE = 'arraybuffer'
|
|
13
16
|
|
|
@@ -69,7 +72,7 @@ export class ClientWebsocket extends EventEmitter<ConnectionEvents> implements I
|
|
|
69
72
|
this.destroyed = true
|
|
70
73
|
this.stopListening()
|
|
71
74
|
this.socket = undefined
|
|
72
|
-
const gracefulLeave = code === GOING_AWAY
|
|
75
|
+
const gracefulLeave = (code === GOING_AWAY) || (code === CUSTOM_GOING_AWAY)
|
|
73
76
|
this.emit('disconnected', gracefulLeave, code, reason)
|
|
74
77
|
this.removeAllListeners()
|
|
75
78
|
}
|
|
@@ -92,7 +95,7 @@ export class ClientWebsocket extends EventEmitter<ConnectionEvents> implements I
|
|
|
92
95
|
this.removeAllListeners()
|
|
93
96
|
if (!this.destroyed) {
|
|
94
97
|
logger.trace(`Closing socket for connection ${this.connectionId.toString()}`)
|
|
95
|
-
this.socket?.close(gracefulLeave
|
|
98
|
+
this.socket?.close(gracefulLeave ? CUSTOM_GOING_AWAY : undefined)
|
|
96
99
|
} else {
|
|
97
100
|
logger.debug('Tried to close() a stopped connection')
|
|
98
101
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import EventEmitter from 'eventemitter3'
|
|
2
2
|
import { IConnection, ConnectionID, ConnectionEvents, ConnectionType } from '../IConnection'
|
|
3
|
-
import { connection as WsConnection } from 'websocket'
|
|
3
|
+
import { Message, connection as WsConnection } from 'websocket'
|
|
4
4
|
import { Logger } from '@streamr/utils'
|
|
5
5
|
import { Url } from 'url'
|
|
6
|
-
import { GOING_AWAY } from './ClientWebsocket'
|
|
6
|
+
import { CUSTOM_GOING_AWAY, GOING_AWAY } from './ClientWebsocket'
|
|
7
7
|
|
|
8
8
|
const logger = new Logger(module)
|
|
9
9
|
|
|
@@ -29,37 +29,52 @@ export class ServerWebsocket extends EventEmitter<ConnectionEvents> implements I
|
|
|
29
29
|
constructor(socket: WsConnection, resourceURL: Url) {
|
|
30
30
|
super()
|
|
31
31
|
|
|
32
|
+
this.onMessage = this.onMessage.bind(this)
|
|
33
|
+
this.onClose = this.onClose.bind(this)
|
|
34
|
+
this.onError = this.onError.bind(this)
|
|
35
|
+
|
|
32
36
|
this.resourceURL = resourceURL
|
|
33
37
|
this.connectionId = new ConnectionID()
|
|
34
38
|
|
|
35
|
-
socket.on('message',
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
logger.debug('Received string Message: ' + message.utf8Data)
|
|
39
|
-
} else if (message.type === MessageType.BINARY) {
|
|
40
|
-
logger.trace('Received Binary Message of ' + message.binaryData.length + ' bytes')
|
|
41
|
-
this.emit('data',
|
|
42
|
-
new Uint8Array(message.binaryData.buffer, message.binaryData.byteOffset,
|
|
43
|
-
message.binaryData.byteLength / Uint8Array.BYTES_PER_ELEMENT))
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
socket.on('close', (reasonCode, description) => {
|
|
47
|
-
logger.trace('Peer ' + socket.remoteAddress + ' disconnected.')
|
|
48
|
-
this.doDisconnect(reasonCode, description)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
socket.on('error', (error) => {
|
|
52
|
-
this.emit('error', error.name)
|
|
53
|
-
})
|
|
39
|
+
socket.on('message', this.onMessage)
|
|
40
|
+
socket.on('close', this.onClose)
|
|
41
|
+
socket.on('error', this.onError)
|
|
54
42
|
|
|
55
43
|
this.socket = socket
|
|
56
44
|
}
|
|
57
45
|
|
|
46
|
+
private onMessage(message: Message): void {
|
|
47
|
+
logger.trace('ServerWebsocket::onMessage')
|
|
48
|
+
if (message.type === MessageType.UTF8) {
|
|
49
|
+
logger.debug('Received string Message: ' + message.utf8Data)
|
|
50
|
+
} else if (message.type === MessageType.BINARY) {
|
|
51
|
+
logger.trace('Received Binary Message of ' + message.binaryData.length + ' bytes')
|
|
52
|
+
this.emit('data',
|
|
53
|
+
new Uint8Array(message.binaryData.buffer, message.binaryData.byteOffset,
|
|
54
|
+
message.binaryData.byteLength / Uint8Array.BYTES_PER_ELEMENT))
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private onClose(reasonCode: number, description: string): void {
|
|
59
|
+
logger.trace('Peer ' + this.socket?.remoteAddress + ' disconnected.')
|
|
60
|
+
this.doDisconnect(reasonCode, description)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private onError(error: Error): void {
|
|
64
|
+
this.emit('error', error.name)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private stopListening(): void {
|
|
68
|
+
this.socket?.off('message', this.onMessage)
|
|
69
|
+
this.socket?.off('close', this.onClose)
|
|
70
|
+
this.socket?.off('error', this.onError)
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
private doDisconnect(reasonCode: number, description: string): void {
|
|
59
74
|
this.stopped = true
|
|
60
|
-
this.
|
|
75
|
+
this.stopListening()
|
|
61
76
|
this.socket = undefined
|
|
62
|
-
const gracefulLeave = reasonCode === GOING_AWAY
|
|
77
|
+
const gracefulLeave = (reasonCode === GOING_AWAY) || (reasonCode === CUSTOM_GOING_AWAY)
|
|
63
78
|
this.emit('disconnected', gracefulLeave, reasonCode, description)
|
|
64
79
|
}
|
|
65
80
|
|
|
@@ -83,7 +98,7 @@ export class ServerWebsocket extends EventEmitter<ConnectionEvents> implements I
|
|
|
83
98
|
this.emit('disconnected', gracefulLeave, undefined, 'close() called')
|
|
84
99
|
this.removeAllListeners()
|
|
85
100
|
if (!this.stopped) {
|
|
86
|
-
this.socket?.close(gracefulLeave
|
|
101
|
+
this.socket?.close(gracefulLeave ? GOING_AWAY : undefined)
|
|
87
102
|
} else {
|
|
88
103
|
logger.debug('Tried to close a stopped connection')
|
|
89
104
|
}
|
|
@@ -93,7 +108,7 @@ export class ServerWebsocket extends EventEmitter<ConnectionEvents> implements I
|
|
|
93
108
|
if (!this.stopped) {
|
|
94
109
|
this.removeAllListeners()
|
|
95
110
|
if (this.socket) {
|
|
96
|
-
this.
|
|
111
|
+
this.stopListening()
|
|
97
112
|
this.socket.close()
|
|
98
113
|
this.socket = undefined
|
|
99
114
|
}
|
|
@@ -44,7 +44,6 @@ const ENTRY_POINT_CONNECTION_ATTEMPTS = 5
|
|
|
44
44
|
|
|
45
45
|
export interface WebsocketConnectorConfig {
|
|
46
46
|
transport: ITransport
|
|
47
|
-
canConnect: (peerDescriptor: PeerDescriptor) => boolean
|
|
48
47
|
onNewConnection: (connection: ManagedConnection) => boolean
|
|
49
48
|
portRange?: PortRange
|
|
50
49
|
maxMessageSize?: number
|
|
@@ -100,8 +99,18 @@ export class WebsocketConnector {
|
|
|
100
99
|
|
|
101
100
|
private registerLocalRpcMethods(config: WebsocketConnectorConfig) {
|
|
102
101
|
const rpcLocal = new WebsocketConnectorRpcLocal({
|
|
103
|
-
canConnect: (peerDescriptor: PeerDescriptor) => config.canConnect(peerDescriptor),
|
|
104
102
|
connect: (targetPeerDescriptor: PeerDescriptor) => this.connect(targetPeerDescriptor),
|
|
103
|
+
hasConnection: (targetPeerDescriptor: PeerDescriptor): boolean => {
|
|
104
|
+
const peerKey = keyFromPeerDescriptor(targetPeerDescriptor)
|
|
105
|
+
if (this.connectingConnections.has(peerKey)
|
|
106
|
+
|| this.connectingConnections.has(peerKey)
|
|
107
|
+
|| this.ongoingConnectRequests.has(peerKey)
|
|
108
|
+
) {
|
|
109
|
+
return true
|
|
110
|
+
} else {
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
113
|
+
},
|
|
105
114
|
onNewConnection: (connection: ManagedConnection) => config.onNewConnection(connection),
|
|
106
115
|
abortSignal: this.abortController.signal
|
|
107
116
|
})
|
|
@@ -109,7 +118,7 @@ export class WebsocketConnector {
|
|
|
109
118
|
WebsocketConnectionRequest,
|
|
110
119
|
WebsocketConnectionResponse,
|
|
111
120
|
'requestConnection',
|
|
112
|
-
async (req: WebsocketConnectionRequest, context: ServerCallContext) => {
|
|
121
|
+
async (req: WebsocketConnectionRequest, context: ServerCallContext): Promise<WebsocketConnectionResponse> => {
|
|
113
122
|
if (!this.abortController.signal.aborted) {
|
|
114
123
|
return rpcLocal.requestConnection(req, context)
|
|
115
124
|
} else {
|
|
@@ -191,7 +200,7 @@ export class WebsocketConnector {
|
|
|
191
200
|
// Do real connectivity checking
|
|
192
201
|
const connectivityRequest = {
|
|
193
202
|
port: this.selectedPort!,
|
|
194
|
-
host: this.host,
|
|
203
|
+
host: this.host,
|
|
195
204
|
tls: this.serverEnableTls,
|
|
196
205
|
selfSigned
|
|
197
206
|
}
|
|
@@ -267,8 +276,14 @@ export class WebsocketConnector {
|
|
|
267
276
|
targetPeerDescriptor,
|
|
268
277
|
toProtoRpcClient(new WebsocketConnectorRpcClient(this.rpcCommunicator.getRpcClientTransport()))
|
|
269
278
|
)
|
|
270
|
-
|
|
271
|
-
|
|
279
|
+
remoteConnector.requestConnection().then((_response: WebsocketConnectionResponse) => {
|
|
280
|
+
logger.trace('Sent WebsocketConnectionRequest request to peer', { targetPeerDescriptor })
|
|
281
|
+
return
|
|
282
|
+
}, (err) => {
|
|
283
|
+
logger.debug('Failed to send WebsocketConnectionRequest request to peer of failed to get the response ', {
|
|
284
|
+
error: err, targetPeerDescriptor
|
|
285
|
+
})
|
|
286
|
+
})
|
|
272
287
|
})
|
|
273
288
|
const managedConnection = new ManagedConnection(
|
|
274
289
|
this.localPeerDescriptor!,
|
|
@@ -293,12 +308,8 @@ export class WebsocketConnector {
|
|
|
293
308
|
if (this.ongoingConnectRequests.has(peerId.toKey())) {
|
|
294
309
|
const ongoingConnectRequest = this.ongoingConnectRequests.get(peerId.toKey())!
|
|
295
310
|
ongoingConnectRequest.attachImplementation(serverWebsocket)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
} else {
|
|
299
|
-
ongoingConnectRequest.acceptHandshake()
|
|
300
|
-
this.ongoingConnectRequests.delete(peerId.toKey())
|
|
301
|
-
}
|
|
311
|
+
ongoingConnectRequest.acceptHandshake()
|
|
312
|
+
this.ongoingConnectRequests.delete(peerId.toKey())
|
|
302
313
|
} else {
|
|
303
314
|
const managedConnection = new ManagedConnection(
|
|
304
315
|
this.localPeerDescriptor!,
|
|
@@ -9,8 +9,8 @@ import { DhtCallContext } from '../../rpc-protocol/DhtCallContext'
|
|
|
9
9
|
import { ManagedConnection } from '../ManagedConnection'
|
|
10
10
|
|
|
11
11
|
interface WebsocketConnectorRpcLocalConfig {
|
|
12
|
-
canConnect: (peerDescriptor: PeerDescriptor) => boolean
|
|
13
12
|
connect: (targetPeerDescriptor: PeerDescriptor) => ManagedConnection
|
|
13
|
+
hasConnection: (targetPeerDescriptor: PeerDescriptor) => boolean
|
|
14
14
|
onNewConnection: (connection: ManagedConnection) => boolean
|
|
15
15
|
abortSignal: AbortSignal
|
|
16
16
|
}
|
|
@@ -25,17 +25,15 @@ export class WebsocketConnectorRpcLocal implements IWebsocketConnectorRpc {
|
|
|
25
25
|
|
|
26
26
|
public async requestConnection(_request: WebsocketConnectionRequest, context: ServerCallContext): Promise<WebsocketConnectionResponse> {
|
|
27
27
|
const senderPeerDescriptor = (context as DhtCallContext).incomingSourceDescriptor!
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
setImmediate(() => {
|
|
29
|
+
if (this.config.abortSignal.aborted) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
if (!this.config.hasConnection(senderPeerDescriptor)) {
|
|
33
33
|
const connection = this.config.connect(senderPeerDescriptor)
|
|
34
34
|
this.config.onNewConnection(connection)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return { accepted: false }
|
|
39
|
-
}
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
return { accepted: true }
|
|
40
38
|
}
|
|
41
39
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PeerDescriptor,
|
|
3
|
-
WebsocketConnectionRequest
|
|
3
|
+
WebsocketConnectionRequest,
|
|
4
|
+
WebsocketConnectionResponse
|
|
4
5
|
} from '../../proto/packages/dht/protos/DhtRpc'
|
|
5
6
|
import { IWebsocketConnectorRpcClient } from '../../proto/packages/dht/protos/DhtRpc.client'
|
|
6
7
|
import { Logger } from '@streamr/utils'
|
|
7
|
-
import * as Err from '../../helpers/errors'
|
|
8
8
|
import { ProtoRpcClient } from '@streamr/proto-rpc'
|
|
9
9
|
import { getNodeIdFromPeerDescriptor } from '../../helpers/peerIdFromPeerDescriptor'
|
|
10
10
|
import { RpcRemote } from '../../dht/contact/RpcRemote'
|
|
@@ -21,19 +21,10 @@ export class WebsocketConnectorRpcRemote extends RpcRemote<IWebsocketConnectorRp
|
|
|
21
21
|
super(localPeerDescriptor, remotePeerDescriptor, 'DUMMY', client)
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
async requestConnection(
|
|
24
|
+
async requestConnection(): Promise<WebsocketConnectionResponse> {
|
|
25
25
|
logger.trace(`Requesting WebSocket connection from ${getNodeIdFromPeerDescriptor(this.getLocalPeerDescriptor())}`)
|
|
26
|
-
const request: WebsocketConnectionRequest = {
|
|
27
|
-
ip,
|
|
28
|
-
port
|
|
29
|
-
}
|
|
26
|
+
const request: WebsocketConnectionRequest = {}
|
|
30
27
|
const options = this.formDhtRpcOptions()
|
|
31
|
-
|
|
32
|
-
const res = await this.getClient().requestConnection(request, options)
|
|
33
|
-
return res.accepted
|
|
34
|
-
} catch (err) {
|
|
35
|
-
logger.debug(new Err.WebsocketConnectionRequestRejected('WebsocketConnectionRequest rejected', err).stack!)
|
|
36
|
-
return false
|
|
37
|
-
}
|
|
28
|
+
return this.getClient().requestConnection(request, options)
|
|
38
29
|
}
|
|
39
30
|
}
|
|
@@ -101,11 +101,17 @@ export class WebsocketServer extends EventEmitter<ConnectionSourceEvents> {
|
|
|
101
101
|
return
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
let connection
|
|
105
|
+
try {
|
|
106
|
+
connection = request.accept(undefined, request.origin)
|
|
107
|
+
logger.trace('Connection accepted.', { remoteAddress: request.remoteAddress })
|
|
108
|
+
} catch (err) {
|
|
109
|
+
logger.debug('Accepting websocket connection failed', { remoteAddress: request.remoteAddress, err })
|
|
110
|
+
}
|
|
107
111
|
|
|
108
|
-
|
|
112
|
+
if (connection) {
|
|
113
|
+
this.emit('connected', new ServerWebsocket(connection, request.resourceURL))
|
|
114
|
+
}
|
|
109
115
|
})
|
|
110
116
|
this.httpServer.once('error', (err: Error) => {
|
|
111
117
|
reject(new WebsocketServerStartError('Starting Websocket server failed', err))
|
|
@@ -137,9 +143,18 @@ export class WebsocketServer extends EventEmitter<ConnectionSourceEvents> {
|
|
|
137
143
|
this.removeAllListeners()
|
|
138
144
|
return new Promise((resolve, _reject) => {
|
|
139
145
|
this.wsServer?.shutDown()
|
|
140
|
-
this.httpServer?.close(
|
|
146
|
+
this.httpServer?.once('close', () => {
|
|
147
|
+
// removeAllListeners is maybe not needed?
|
|
148
|
+
this.httpServer?.removeAllListeners()
|
|
141
149
|
resolve()
|
|
142
150
|
})
|
|
151
|
+
this.httpServer?.close()
|
|
152
|
+
// the close method "Stops the server from accepting new connections and closes all
|
|
153
|
+
// connections connected to this server which are not sending a request or waiting for a
|
|
154
|
+
// response." (https://nodejs.org/api/http.html#serverclosecallback)
|
|
155
|
+
// i.e. we need to call closeAllConnections() to close the rest of the connections
|
|
156
|
+
// (in practice this closes the active websocket connections)
|
|
157
|
+
this.httpServer?.closeAllConnections()
|
|
143
158
|
})
|
|
144
159
|
}
|
|
145
160
|
|