@streamr/dht 100.0.0-testnet-one.0 → 100.0.0-testnet-one.1
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 +1 -2
- package/dist/src/connection/ConnectorFacade.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 +30 -19
- 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 +2 -5
- package/dist/src/dht/discovery/DiscoverySession.js +12 -9
- package/dist/src/dht/discovery/DiscoverySession.js.map +1 -1
- package/dist/src/dht/discovery/PeerDiscovery.d.ts +1 -1
- package/dist/src/dht/discovery/PeerDiscovery.js +4 -10
- 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 +0 -3
- 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 +31 -21
- 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 +14 -14
- package/src/dht/discovery/PeerDiscovery.ts +9 -16
- 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/StoreOnDhtWithTwoNodes.test.ts +1 -1
- package/test/integration/WebsocketConnectionManagement.test.ts +41 -3
- 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,
|
|
@@ -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
|
|
package/src/dht/DhtNode.ts
CHANGED
|
@@ -51,7 +51,7 @@ import { MarkRequired } from 'ts-essentials'
|
|
|
51
51
|
import { DhtNodeRpcLocal } from './DhtNodeRpcLocal'
|
|
52
52
|
import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
|
|
53
53
|
import { ExternalApiRpcLocal } from './ExternalApiRpcLocal'
|
|
54
|
-
import { PeerManager } from './PeerManager'
|
|
54
|
+
import { PeerManager, getDistance } from './PeerManager'
|
|
55
55
|
|
|
56
56
|
export interface DhtNodeEvents {
|
|
57
57
|
newContact: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
|
|
@@ -148,7 +148,7 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
148
148
|
|
|
149
149
|
public connectionManager?: ConnectionManager
|
|
150
150
|
private started = false
|
|
151
|
-
private
|
|
151
|
+
private abortController = new AbortController()
|
|
152
152
|
private entryPointDisconnectTimeout?: NodeJS.Timeout
|
|
153
153
|
|
|
154
154
|
constructor(conf: DhtNodeOptions) {
|
|
@@ -173,7 +173,7 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
public async start(): Promise<void> {
|
|
176
|
-
if (this.started || this.
|
|
176
|
+
if (this.started || this.abortController.signal.aborted) {
|
|
177
177
|
return
|
|
178
178
|
}
|
|
179
179
|
logger.trace(`Starting new Streamr Network DHT Node with serviceId ${this.config.serviceId}`)
|
|
@@ -283,7 +283,7 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
283
283
|
localDataStore: this.localDataStore,
|
|
284
284
|
dhtNodeEmitter: this,
|
|
285
285
|
getNodesClosestToIdFromBucket: (id: Uint8Array, n?: number) => {
|
|
286
|
-
return this.peerManager!.
|
|
286
|
+
return this.peerManager!.getClosestNeighborsTo(id, n)
|
|
287
287
|
},
|
|
288
288
|
rpcRequestTimeout: this.config.rpcRequestTimeout
|
|
289
289
|
})
|
|
@@ -297,7 +297,7 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
297
297
|
private initPeerManager() {
|
|
298
298
|
this.peerManager = new PeerManager({
|
|
299
299
|
numberOfNodesPerKBucket: this.config.numberOfNodesPerKBucket,
|
|
300
|
-
|
|
300
|
+
maxContactListSize: this.config.maxNeighborListSize,
|
|
301
301
|
ownPeerId: this.getNodeId(),
|
|
302
302
|
connectionManager: this.connectionManager!,
|
|
303
303
|
peerDiscoveryQueryBatchSize: this.config.peerDiscoveryQueryBatchSize,
|
|
@@ -343,13 +343,16 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
343
343
|
}
|
|
344
344
|
|
|
345
345
|
private bindRpcLocalMethods(): void {
|
|
346
|
-
if (!this.started || this.
|
|
346
|
+
if (!this.started || this.abortController.signal.aborted) {
|
|
347
347
|
return
|
|
348
348
|
}
|
|
349
349
|
const dhtNodeRpcLocal = new DhtNodeRpcLocal({
|
|
350
|
-
bucket: this.peerManager!.bucket!,
|
|
351
350
|
serviceId: this.config.serviceId,
|
|
352
351
|
peerDiscoveryQueryBatchSize: this.config.peerDiscoveryQueryBatchSize,
|
|
352
|
+
getClosestPeersTo: (kademliaId: Uint8Array, limit: number) => {
|
|
353
|
+
return this.peerManager!.getClosestNeighborsTo(kademliaId, limit)
|
|
354
|
+
.map((dhtPeer: DhtNodeRpcRemote) => dhtPeer.getPeerDescriptor())
|
|
355
|
+
},
|
|
353
356
|
addNewContact: (contact: PeerDescriptor) => this.peerManager!.handleNewPeers([contact]),
|
|
354
357
|
removeContact: (contact: PeerDescriptor) => this.removeContact(contact)
|
|
355
358
|
})
|
|
@@ -382,8 +385,8 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
382
385
|
}
|
|
383
386
|
|
|
384
387
|
private isPeerCloserToIdThanSelf(peer1: PeerDescriptor, compareToId: PeerID): boolean {
|
|
385
|
-
const distance1 =
|
|
386
|
-
const distance2 =
|
|
388
|
+
const distance1 = getDistance(peer1.nodeId, compareToId.value)
|
|
389
|
+
const distance2 = getDistance(this.localPeerDescriptor!.nodeId, compareToId.value)
|
|
387
390
|
return distance1 < distance2
|
|
388
391
|
}
|
|
389
392
|
|
|
@@ -408,16 +411,16 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
408
411
|
return this.localPeerDescriptor
|
|
409
412
|
}
|
|
410
413
|
|
|
411
|
-
public getClosestContacts(
|
|
412
|
-
return this.peerManager!.
|
|
414
|
+
public getClosestContacts(limit?: number): PeerDescriptor[] {
|
|
415
|
+
return this.peerManager!.getClosestContactsTo(this.localPeerDescriptor!.nodeId, limit).map((peer) => peer.getPeerDescriptor())
|
|
413
416
|
}
|
|
414
|
-
|
|
417
|
+
|
|
415
418
|
public getNodeId(): PeerID {
|
|
416
419
|
return peerIdFromPeerDescriptor(this.localPeerDescriptor!)
|
|
417
420
|
}
|
|
418
421
|
|
|
419
|
-
public
|
|
420
|
-
return this.peerManager!.
|
|
422
|
+
public getNumberOfNeighbors(): number {
|
|
423
|
+
return this.peerManager!.getNumberOfNeighbors()
|
|
421
424
|
}
|
|
422
425
|
|
|
423
426
|
private connectToEntryPoint(entryPoint: PeerDescriptor): void {
|
|
@@ -435,7 +438,7 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
435
438
|
}
|
|
436
439
|
|
|
437
440
|
public async send(msg: Message): Promise<void> {
|
|
438
|
-
if (!this.started || this.
|
|
441
|
+
if (!this.started || this.abortController.signal.aborted) {
|
|
439
442
|
return
|
|
440
443
|
}
|
|
441
444
|
const reachableThrough = this.peerDiscovery!.isJoinOngoing() ? this.config.entryPoints ?? [] : []
|
|
@@ -481,7 +484,7 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
481
484
|
}
|
|
482
485
|
|
|
483
486
|
public async deleteDataFromDht(key: Uint8Array, waitForCompletion: boolean): Promise<void> {
|
|
484
|
-
if (!this.
|
|
487
|
+
if (!this.abortController.signal.aborted) {
|
|
485
488
|
await this.finder!.startFind(key, FindAction.DELETE_DATA, undefined, waitForCompletion)
|
|
486
489
|
}
|
|
487
490
|
}
|
|
@@ -508,8 +511,9 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
508
511
|
return Array.from(this.peerManager!.connections.values()).map((peer) => peer.getPeerDescriptor())
|
|
509
512
|
}
|
|
510
513
|
|
|
511
|
-
|
|
512
|
-
|
|
514
|
+
// TODO rename to getNeighbors
|
|
515
|
+
public getAllNeighborPeerDescriptors(): PeerDescriptor[] {
|
|
516
|
+
return this.peerManager!.getNeighbors()
|
|
513
517
|
}
|
|
514
518
|
|
|
515
519
|
public getNumberOfConnections(): number {
|
|
@@ -529,7 +533,13 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
529
533
|
}
|
|
530
534
|
|
|
531
535
|
public async waitForNetworkConnectivity(): Promise<void> {
|
|
532
|
-
await waitForCondition(() =>
|
|
536
|
+
await waitForCondition(() => {
|
|
537
|
+
if (!this.peerManager) {
|
|
538
|
+
return false
|
|
539
|
+
} else {
|
|
540
|
+
return (this.peerManager.getNumberOfConnections() > 0)
|
|
541
|
+
}
|
|
542
|
+
}, this.config.networkConnectivityTimeout, 100, this.abortController.signal)
|
|
533
543
|
}
|
|
534
544
|
|
|
535
545
|
public hasJoined(): boolean {
|
|
@@ -537,11 +547,11 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
537
547
|
}
|
|
538
548
|
|
|
539
549
|
public async stop(): Promise<void> {
|
|
540
|
-
if (this.
|
|
550
|
+
if (this.abortController.signal.aborted || !this.started) {
|
|
541
551
|
return
|
|
542
552
|
}
|
|
543
553
|
logger.trace('stop()')
|
|
544
|
-
this.
|
|
554
|
+
this.abortController.abort()
|
|
545
555
|
await this.storeRpcLocal!.destroy()
|
|
546
556
|
if (this.entryPointDisconnectTimeout) {
|
|
547
557
|
clearTimeout(this.entryPointDisconnectTimeout)
|