@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.
Files changed (113) hide show
  1. package/dist/src/connection/ConnectionLockRpcRemote.js +1 -25
  2. package/dist/src/connection/ConnectionLockRpcRemote.js.map +1 -1
  3. package/dist/src/connection/ConnectionManager.d.ts +0 -1
  4. package/dist/src/connection/ConnectionManager.js +7 -6
  5. package/dist/src/connection/ConnectionManager.js.map +1 -1
  6. package/dist/src/connection/ConnectorFacade.d.ts +2 -2
  7. package/dist/src/connection/ConnectorFacade.js +2 -3
  8. package/dist/src/connection/ConnectorFacade.js.map +1 -1
  9. package/dist/src/connection/ManagedConnection.d.ts +1 -0
  10. package/dist/src/connection/ManagedConnection.js +11 -1
  11. package/dist/src/connection/ManagedConnection.js.map +1 -1
  12. package/dist/src/connection/connectivityChecker.js +3 -2
  13. package/dist/src/connection/connectivityChecker.js.map +1 -1
  14. package/dist/src/connection/websocket/ClientWebsocket.d.ts +1 -0
  15. package/dist/src/connection/websocket/ClientWebsocket.js +6 -3
  16. package/dist/src/connection/websocket/ClientWebsocket.js.map +1 -1
  17. package/dist/src/connection/websocket/ServerWebsocket.d.ts +4 -0
  18. package/dist/src/connection/websocket/ServerWebsocket.js +32 -21
  19. package/dist/src/connection/websocket/ServerWebsocket.js.map +1 -1
  20. package/dist/src/connection/websocket/WebsocketConnector.d.ts +0 -1
  21. package/dist/src/connection/websocket/WebsocketConnector.js +21 -10
  22. package/dist/src/connection/websocket/WebsocketConnector.js.map +1 -1
  23. package/dist/src/connection/websocket/WebsocketConnectorRpcLocal.d.ts +1 -1
  24. package/dist/src/connection/websocket/WebsocketConnectorRpcLocal.js +8 -11
  25. package/dist/src/connection/websocket/WebsocketConnectorRpcLocal.js.map +1 -1
  26. package/dist/src/connection/websocket/WebsocketConnectorRpcRemote.d.ts +2 -2
  27. package/dist/src/connection/websocket/WebsocketConnectorRpcRemote.js +3 -37
  28. package/dist/src/connection/websocket/WebsocketConnectorRpcRemote.js.map +1 -1
  29. package/dist/src/connection/websocket/WebsocketServer.js +21 -4
  30. package/dist/src/connection/websocket/WebsocketServer.js.map +1 -1
  31. package/dist/src/dht/DhtNode.d.ts +4 -4
  32. package/dist/src/dht/DhtNode.js +31 -20
  33. package/dist/src/dht/DhtNode.js.map +1 -1
  34. package/dist/src/dht/DhtNodeRpcLocal.d.ts +1 -4
  35. package/dist/src/dht/DhtNodeRpcLocal.js +1 -5
  36. package/dist/src/dht/DhtNodeRpcLocal.js.map +1 -1
  37. package/dist/src/dht/PeerManager.d.ts +10 -6
  38. package/dist/src/dht/PeerManager.js +95 -30
  39. package/dist/src/dht/PeerManager.js.map +1 -1
  40. package/dist/src/dht/contact/SortedContactList.d.ts +20 -6
  41. package/dist/src/dht/contact/SortedContactList.js +55 -24
  42. package/dist/src/dht/contact/SortedContactList.js.map +1 -1
  43. package/dist/src/dht/discovery/DiscoverySession.d.ts +3 -5
  44. package/dist/src/dht/discovery/DiscoverySession.js +11 -9
  45. package/dist/src/dht/discovery/DiscoverySession.js.map +1 -1
  46. package/dist/src/dht/discovery/PeerDiscovery.d.ts +4 -2
  47. package/dist/src/dht/discovery/PeerDiscovery.js +17 -16
  48. package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -1
  49. package/dist/src/dht/find/FindSession.js +6 -1
  50. package/dist/src/dht/find/FindSession.js.map +1 -1
  51. package/dist/src/dht/find/Finder.js +6 -1
  52. package/dist/src/dht/find/Finder.js.map +1 -1
  53. package/dist/src/dht/routing/Router.d.ts +1 -1
  54. package/dist/src/dht/routing/Router.js +8 -4
  55. package/dist/src/dht/routing/Router.js.map +1 -1
  56. package/dist/src/dht/routing/RoutingSession.js +8 -1
  57. package/dist/src/dht/routing/RoutingSession.js.map +1 -1
  58. package/dist/src/dht/store/StoreRpcLocal.js +19 -5
  59. package/dist/src/dht/store/StoreRpcLocal.js.map +1 -1
  60. package/dist/src/helpers/PeerID.d.ts +1 -0
  61. package/dist/src/helpers/PeerID.js +7 -2
  62. package/dist/src/helpers/PeerID.js.map +1 -1
  63. package/dist/src/helpers/peerIdFromPeerDescriptor.js +2 -2
  64. package/dist/src/helpers/peerIdFromPeerDescriptor.js.map +1 -1
  65. package/package.json +5 -5
  66. package/src/connection/ConnectionLockRpcRemote.ts +1 -2
  67. package/src/connection/ConnectionManager.ts +16 -17
  68. package/src/connection/ConnectorFacade.ts +1 -4
  69. package/src/connection/ManagedConnection.ts +12 -1
  70. package/src/connection/connectivityChecker.ts +3 -2
  71. package/src/connection/websocket/ClientWebsocket.ts +5 -2
  72. package/src/connection/websocket/ServerWebsocket.ts +40 -25
  73. package/src/connection/websocket/WebsocketConnector.ts +23 -12
  74. package/src/connection/websocket/WebsocketConnectorRpcLocal.ts +9 -11
  75. package/src/connection/websocket/WebsocketConnectorRpcRemote.ts +5 -14
  76. package/src/connection/websocket/WebsocketServer.ts +20 -5
  77. package/src/dht/DhtNode.ts +32 -24
  78. package/src/dht/DhtNodeRpcLocal.ts +2 -9
  79. package/src/dht/PeerManager.ts +110 -36
  80. package/src/dht/contact/SortedContactList.ts +87 -44
  81. package/src/dht/discovery/DiscoverySession.ts +15 -14
  82. package/src/dht/discovery/PeerDiscovery.ts +37 -22
  83. package/src/dht/find/FindSession.ts +6 -1
  84. package/src/dht/find/Finder.ts +6 -7
  85. package/src/dht/routing/Router.ts +8 -4
  86. package/src/dht/routing/RoutingSession.ts +8 -8
  87. package/src/dht/store/StoreRpcLocal.ts +19 -7
  88. package/src/helpers/PeerID.ts +6 -2
  89. package/src/helpers/peerIdFromPeerDescriptor.ts +4 -4
  90. package/test/benchmark/Find.test.ts +1 -1
  91. package/test/benchmark/KademliaCorrectness.test.ts +1 -1
  92. package/test/benchmark/SortedContactListBenchmark.test.ts +150 -0
  93. package/test/benchmark/WebsocketServerMemoryLeak.test.ts +41 -0
  94. package/test/benchmark/kademlia-simulation/SimulationNode.ts +6 -1
  95. package/test/end-to-end/Layer0.test.ts +4 -4
  96. package/test/end-to-end/Layer0MixedConnectionTypes.test.ts +10 -10
  97. package/test/end-to-end/Layer0Webrtc-Layer1.test.ts +4 -4
  98. package/test/end-to-end/Layer1-Scale-WebSocket.test.ts +2 -2
  99. package/test/end-to-end/Layer1-Scale-Webrtc.test.ts +2 -2
  100. package/test/end-to-end/RecoveryFromFailedAutoCertification.test.ts +1 -1
  101. package/test/end-to-end/memory-leak.test.ts +1 -0
  102. package/test/integration/DhtJoinPeerDiscovery.test.ts +2 -2
  103. package/test/integration/Layer1-scale.test.ts +1 -1
  104. package/test/integration/Mock-Layer1-Layer0.test.ts +15 -15
  105. package/test/integration/MultipleEntryPointJoining.test.ts +7 -7
  106. package/test/integration/ReplicateData.test.ts +6 -1
  107. package/test/integration/SimultaneousConnections.test.ts +81 -49
  108. package/test/integration/Store.test.ts +1 -1
  109. package/test/integration/StoreOnDhtWithTwoNodes.test.ts +1 -1
  110. package/test/integration/WebrtcConnectionManagement.test.ts +29 -0
  111. package/test/integration/WebsocketConnectionManagement.test.ts +65 -4
  112. package/test/integration/WebsocketConnectorRpc.test.ts +3 -5
  113. 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>(peerIdFromPeerDescriptor(this.getLocalPeerDescriptor()), 100000)
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
- + ' connectionIds do not match ' + storedConnection.connectionId.toString() + ' ' + connection.connectionId.toString())
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 the entrypoint ${url}`, e)
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.trace('ConnectivityResponse received: ' + JSON.stringify(Message.toJson(message)))
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 === true ? GOING_AWAY : undefined)
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', (message) => {
36
- logger.trace('ServerWebsocket::onMessage')
37
- if (message.type === MessageType.UTF8) {
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.socket?.removeAllListeners()
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 === true ? GOING_AWAY : undefined)
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.socket.removeAllListeners()
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
- // TODO should we have some handling for this floating promise?
271
- remoteConnector.requestConnection(localPeerDescriptor.websocket!.host, localPeerDescriptor.websocket!.port)
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
- if (targetPeerDescriptor && !areEqualPeerDescriptors(this.localPeerDescriptor!, targetPeerDescriptor)) {
297
- ongoingConnectRequest.rejectHandshake(HandshakeError.INVALID_TARGET_PEER_DESCRIPTOR)
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
- if (this.config.canConnect(senderPeerDescriptor)) {
29
- setImmediate(() => {
30
- if (this.config.abortSignal.aborted) {
31
- return
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
- return { accepted: true }
37
- } else {
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(ip: string, port: number): Promise<boolean> {
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
- try {
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
- const connection = request.accept(undefined, request.origin)
105
-
106
- logger.trace('IConnection accepted.')
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
- this.emit('connected', new ServerWebsocket(connection, request.resourceURL))
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