@streamr/dht 0.0.1-tatum.1 → 0.0.1-tatum.3
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/ConnectionManager.d.ts +6 -1
- package/dist/src/connection/ConnectionManager.js +21 -26
- package/dist/src/connection/ConnectionManager.js.map +1 -1
- package/dist/src/connection/ConnectivityChecker.d.ts +4 -2
- package/dist/src/connection/ConnectivityChecker.js +23 -19
- package/dist/src/connection/ConnectivityChecker.js.map +1 -1
- package/dist/src/connection/ManagedConnection.js +2 -6
- package/dist/src/connection/ManagedConnection.js.map +1 -1
- package/dist/src/connection/Simulator/SimulatorTransport.js +1 -1
- package/dist/src/connection/Simulator/SimulatorTransport.js.map +1 -1
- package/dist/src/connection/WebRTC/WebRtcConnector.d.ts +2 -0
- package/dist/src/connection/WebRTC/WebRtcConnector.js +14 -1
- package/dist/src/connection/WebRTC/WebRtcConnector.js.map +1 -1
- package/dist/src/connection/WebSocket/WebSocketConnector.d.ts +5 -3
- package/dist/src/connection/WebSocket/WebSocketConnector.js +19 -12
- package/dist/src/connection/WebSocket/WebSocketConnector.js.map +1 -1
- package/dist/src/connection/WebSocket/WebSocketServer.d.ts +2 -2
- package/dist/src/connection/WebSocket/WebSocketServer.js +29 -37
- package/dist/src/connection/WebSocket/WebSocketServer.js.map +1 -1
- package/dist/src/dht/DhtNode.d.ts +9 -6
- package/dist/src/dht/DhtNode.js +14 -21
- package/dist/src/dht/DhtNode.js.map +1 -1
- package/dist/src/dht/DhtPeer.js +1 -1
- package/dist/src/dht/discovery/PeerDiscovery.d.ts +4 -2
- package/dist/src/dht/discovery/PeerDiscovery.js +18 -33
- package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -1
- package/dist/src/dht/routing/RemoteRouter.js +2 -4
- package/dist/src/dht/routing/RemoteRouter.js.map +1 -1
- package/dist/src/dht/store/DataStore.js +6 -6
- package/dist/src/exports.d.ts +1 -1
- package/dist/src/exports.js.map +1 -1
- package/dist/src/helpers/errors.d.ts +1 -1
- package/dist/src/helpers/errors.js +3 -3
- package/dist/src/helpers/errors.js.map +1 -1
- package/dist/src/helpers/protoClasses.js +0 -2
- package/dist/src/helpers/protoClasses.js.map +1 -1
- package/dist/src/proto/packages/dht/protos/DhtRpc.client.js +1 -1
- package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +14 -50
- package/dist/src/proto/packages/dht/protos/DhtRpc.js +9 -35
- package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -1
- package/dist/src/proto/packages/proto-rpc/protos/ProtoRpc.js +1 -1
- package/package.json +9 -9
- package/protos/DhtRpc.proto +7 -16
- package/src/connection/ConnectionManager.ts +31 -28
- package/src/connection/ConnectivityChecker.ts +28 -23
- package/src/connection/ManagedConnection.ts +2 -6
- package/src/connection/Simulator/SimulatorTransport.ts +1 -1
- package/src/connection/WebRTC/BrowserWebRtcConnection.ts +13 -2
- package/src/connection/WebRTC/WebRtcConnector.ts +14 -0
- package/src/connection/WebSocket/WebSocketConnector.ts +23 -12
- package/src/connection/WebSocket/WebSocketServer.ts +33 -20
- package/src/dht/DhtNode.ts +23 -24
- package/src/dht/DhtPeer.ts +1 -1
- package/src/dht/discovery/PeerDiscovery.ts +17 -8
- package/src/dht/routing/RemoteRouter.ts +2 -4
- package/src/dht/store/DataStore.ts +6 -6
- package/src/exports.ts +1 -1
- package/src/helpers/errors.ts +1 -1
- package/src/helpers/protoClasses.ts +1 -5
- package/src/proto/google/protobuf/any.ts +1 -1
- package/src/proto/google/protobuf/empty.ts +1 -1
- package/src/proto/google/protobuf/timestamp.ts +1 -1
- package/src/proto/packages/dht/protos/DhtRpc.client.ts +1 -1
- package/src/proto/packages/dht/protos/DhtRpc.server.ts +1 -1
- package/src/proto/packages/dht/protos/DhtRpc.ts +22 -69
- package/src/proto/packages/proto-rpc/protos/ProtoRpc.ts +1 -1
- package/test/benchmark/KademliaCorrectness.test.ts +1 -1
- package/test/benchmark/RecursiveFind.test.ts +1 -1
- package/test/end-to-end/Layer0-Layer1.test.ts +3 -3
- package/test/end-to-end/Layer0.test.ts +5 -5
- package/test/end-to-end/Layer0MixedConnectionTypes.test.ts +6 -6
- package/test/end-to-end/Layer0WebRTC-Layer1.test.ts +18 -19
- package/test/end-to-end/Layer0WebRTC.test.ts +18 -24
- package/test/end-to-end/Layer1-Scale-WebRTC.test.ts +2 -2
- package/test/end-to-end/Layer1-Scale-WebSocket.test.ts +2 -2
- package/test/end-to-end/WebSocketConnectionRequest.test.ts +3 -3
- package/test/integration/ConnectionManager.test.ts +10 -9
- package/test/integration/DhtRpc.test.ts +2 -2
- package/test/integration/MigrateData.test.ts +3 -3
- package/test/integration/RouteMessage.test.ts +2 -2
- package/test/integration/ScaleDownDht.test.ts +1 -1
- package/test/integration/SimultaneousConnections.test.ts +9 -6
- package/test/integration/WebRtcConnectionManagement.test.ts +2 -2
- package/test/integration/WebRtcConnectorRpc.test.ts +2 -2
- package/test/integration/WebSocketConnectionManagement.test.ts +3 -2
- package/test/integration/WebSocketConnectorRpc.test.ts +4 -4
- package/test/unit/WebSocketServer.test.ts +1 -8
- package/test/unit/webrtcReplaceInternalIpWithExternalIp.ts +18 -0
- package/test/utils/utils.ts +1 -2
|
@@ -28,6 +28,15 @@ import { PortRange } from '../ConnectionManager'
|
|
|
28
28
|
|
|
29
29
|
const logger = new Logger(module)
|
|
30
30
|
|
|
31
|
+
export const replaceInternalIpWithExternalIp = (candidate: string, ip: string): string => {
|
|
32
|
+
const parsed = candidate.split(' ')
|
|
33
|
+
const type = parsed[7]
|
|
34
|
+
if (type === 'host') {
|
|
35
|
+
parsed[4] = ip
|
|
36
|
+
}
|
|
37
|
+
return parsed.join(' ')
|
|
38
|
+
}
|
|
39
|
+
|
|
31
40
|
export interface WebRtcConnectorConfig {
|
|
32
41
|
rpcTransport: ITransport
|
|
33
42
|
protocolVersion: string
|
|
@@ -36,6 +45,7 @@ export interface WebRtcConnectorConfig {
|
|
|
36
45
|
bufferThresholdLow?: number
|
|
37
46
|
bufferThresholdHigh?: number
|
|
38
47
|
connectionTimeout?: number
|
|
48
|
+
externalIp?: string
|
|
39
49
|
portRange?: PortRange
|
|
40
50
|
}
|
|
41
51
|
|
|
@@ -131,6 +141,10 @@ export class WebRtcConnector implements IWebRtcConnectorService {
|
|
|
131
141
|
)
|
|
132
142
|
|
|
133
143
|
connection.on('localCandidate', (candidate: string, mid: string) => {
|
|
144
|
+
if (this.config.externalIp) {
|
|
145
|
+
candidate = replaceInternalIpWithExternalIp(candidate, this.config.externalIp)
|
|
146
|
+
logger.debug(`onLocalCandidate injected external ip ${candidate} ${mid}`)
|
|
147
|
+
}
|
|
134
148
|
remoteConnector.sendIceCandidate(this.ownPeerDescriptor!, candidate, mid, connection.connectionId.toString())
|
|
135
149
|
})
|
|
136
150
|
|
|
@@ -4,6 +4,7 @@ import { ITransport } from '../../transport/ITransport'
|
|
|
4
4
|
import { ListeningRpcCommunicator } from '../../transport/ListeningRpcCommunicator'
|
|
5
5
|
import { RemoteWebSocketConnector } from './RemoteWebSocketConnector'
|
|
6
6
|
import {
|
|
7
|
+
ConnectivityMethod,
|
|
7
8
|
ConnectivityResponse,
|
|
8
9
|
PeerDescriptor,
|
|
9
10
|
WebSocketConnectionRequest,
|
|
@@ -16,7 +17,7 @@ import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
|
|
|
16
17
|
import { ManagedConnection } from '../ManagedConnection'
|
|
17
18
|
import { WebSocketServer } from './WebSocketServer'
|
|
18
19
|
import { ConnectivityChecker } from '../ConnectivityChecker'
|
|
19
|
-
import { NatType, PortRange } from '../ConnectionManager'
|
|
20
|
+
import { NatType, PortRange, TlsCertificate } from '../ConnectionManager'
|
|
20
21
|
import { PeerIDKey } from '../../helpers/PeerID'
|
|
21
22
|
import { ServerWebSocket } from './ServerWebSocket'
|
|
22
23
|
import { toProtoRpcClient } from '@streamr/proto-rpc'
|
|
@@ -27,6 +28,12 @@ import { sample } from 'lodash'
|
|
|
27
28
|
|
|
28
29
|
const logger = new Logger(module)
|
|
29
30
|
|
|
31
|
+
export const connectivityMethodToWebSocketUrl = (ws: ConnectivityMethod): string => {
|
|
32
|
+
return (ws.tls ? 'wss://' : 'ws://') + ws.host + ':' + ws.port
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const ENTRY_POINT_CONNECTION_ATTEMPTS = 5
|
|
36
|
+
|
|
30
37
|
export class WebSocketConnector implements IWebSocketConnectorService {
|
|
31
38
|
private static readonly WEBSOCKET_CONNECTOR_SERVICE_ID = 'system/websocketconnector'
|
|
32
39
|
private readonly rpcCommunicator: ListeningRpcCommunicator
|
|
@@ -38,6 +45,7 @@ export class WebSocketConnector implements IWebSocketConnectorService {
|
|
|
38
45
|
private portRange?: PortRange
|
|
39
46
|
private host?: string
|
|
40
47
|
private entrypoints?: PeerDescriptor[]
|
|
48
|
+
private readonly tlsCertificate?: TlsCertificate
|
|
41
49
|
private selectedPort?: number
|
|
42
50
|
private readonly protocolVersion: string
|
|
43
51
|
private ownPeerDescriptor?: PeerDescriptor
|
|
@@ -51,7 +59,8 @@ export class WebSocketConnector implements IWebSocketConnectorService {
|
|
|
51
59
|
incomingConnectionCallback: (connection: ManagedConnection) => boolean,
|
|
52
60
|
portRange?: PortRange,
|
|
53
61
|
host?: string,
|
|
54
|
-
entrypoints?: PeerDescriptor[]
|
|
62
|
+
entrypoints?: PeerDescriptor[],
|
|
63
|
+
tlsCertificate?: TlsCertificate
|
|
55
64
|
) {
|
|
56
65
|
this.protocolVersion = protocolVersion
|
|
57
66
|
this.webSocketServer = portRange ? new WebSocketServer() : undefined
|
|
@@ -59,6 +68,7 @@ export class WebSocketConnector implements IWebSocketConnectorService {
|
|
|
59
68
|
this.portRange = portRange
|
|
60
69
|
this.host = host
|
|
61
70
|
this.entrypoints = entrypoints
|
|
71
|
+
this.tlsCertificate = tlsCertificate
|
|
62
72
|
|
|
63
73
|
this.canConnectFunction = fnCanConnect.bind(this)
|
|
64
74
|
|
|
@@ -101,16 +111,17 @@ export class WebSocketConnector implements IWebSocketConnectorService {
|
|
|
101
111
|
this.attachHandshaker(connection)
|
|
102
112
|
}
|
|
103
113
|
})
|
|
104
|
-
const port = await this.webSocketServer.start(this.portRange!, this.
|
|
114
|
+
const port = await this.webSocketServer.start(this.portRange!, this.tlsCertificate)
|
|
105
115
|
this.selectedPort = port
|
|
106
|
-
this.connectivityChecker = new ConnectivityChecker(this.selectedPort)
|
|
116
|
+
this.connectivityChecker = new ConnectivityChecker(this.selectedPort, this.tlsCertificate !== undefined, this.host)
|
|
107
117
|
}
|
|
108
118
|
}
|
|
109
119
|
|
|
110
120
|
public async checkConnectivity(reattempt = 0): Promise<ConnectivityResponse> {
|
|
121
|
+
// TODO: this could throw if the server is not running
|
|
111
122
|
const noServerConnectivityResponse: ConnectivityResponse = {
|
|
112
123
|
openInternet: false,
|
|
113
|
-
|
|
124
|
+
host: '127.0.0.1',
|
|
114
125
|
natType: NatType.UNKNOWN
|
|
115
126
|
}
|
|
116
127
|
if (this.destroyed) {
|
|
@@ -125,9 +136,9 @@ export class WebSocketConnector implements IWebSocketConnectorService {
|
|
|
125
136
|
// return connectivity info given in config
|
|
126
137
|
const preconfiguredConnectivityResponse: ConnectivityResponse = {
|
|
127
138
|
openInternet: true,
|
|
128
|
-
|
|
139
|
+
host: this.host!,
|
|
129
140
|
natType: NatType.OPEN_INTERNET,
|
|
130
|
-
websocket: {
|
|
141
|
+
websocket: { host: this.host!, port: this.selectedPort!, tls: this.tlsCertificate !== undefined }
|
|
131
142
|
}
|
|
132
143
|
return preconfiguredConnectivityResponse
|
|
133
144
|
} else {
|
|
@@ -136,7 +147,8 @@ export class WebSocketConnector implements IWebSocketConnectorService {
|
|
|
136
147
|
}
|
|
137
148
|
}
|
|
138
149
|
} catch (err) {
|
|
139
|
-
if (reattempt <
|
|
150
|
+
if (reattempt < ENTRY_POINT_CONNECTION_ATTEMPTS) {
|
|
151
|
+
logger.error('Failed to connect to the entrypoint', { error: err })
|
|
140
152
|
await wait(2000)
|
|
141
153
|
return this.checkConnectivity(reattempt + 1)
|
|
142
154
|
} else {
|
|
@@ -158,8 +170,7 @@ export class WebSocketConnector implements IWebSocketConnectorService {
|
|
|
158
170
|
} else {
|
|
159
171
|
const socket = new ClientWebSocket()
|
|
160
172
|
|
|
161
|
-
const
|
|
162
|
-
targetPeerDescriptor.websocket!.port
|
|
173
|
+
const url = connectivityMethodToWebSocketUrl(targetPeerDescriptor.websocket!)
|
|
163
174
|
|
|
164
175
|
const managedConnection = new ManagedConnection(this.ownPeerDescriptor!, this.protocolVersion,
|
|
165
176
|
ConnectionType.WEBSOCKET_CLIENT, socket, undefined)
|
|
@@ -177,7 +188,7 @@ export class WebSocketConnector implements IWebSocketConnectorService {
|
|
|
177
188
|
socket.on('disconnected', delFunc)
|
|
178
189
|
managedConnection.on('handshakeCompleted', delFunc)
|
|
179
190
|
|
|
180
|
-
socket.connect(
|
|
191
|
+
socket.connect(url)
|
|
181
192
|
|
|
182
193
|
return managedConnection
|
|
183
194
|
}
|
|
@@ -189,7 +200,7 @@ export class WebSocketConnector implements IWebSocketConnectorService {
|
|
|
189
200
|
targetPeerDescriptor,
|
|
190
201
|
toProtoRpcClient(new WebSocketConnectorServiceClient(this.rpcCommunicator.getRpcClientTransport()))
|
|
191
202
|
)
|
|
192
|
-
remoteConnector.requestConnection(ownPeerDescriptor, ownPeerDescriptor.websocket!.
|
|
203
|
+
remoteConnector.requestConnection(ownPeerDescriptor, ownPeerDescriptor.websocket!.host, ownPeerDescriptor.websocket!.port)
|
|
193
204
|
})
|
|
194
205
|
const managedConnection = new ManagedConnection(this.ownPeerDescriptor!, this.protocolVersion, ConnectionType.WEBSOCKET_SERVER)
|
|
195
206
|
managedConnection.on('disconnected', () => this.ongoingConnectRequests.delete(keyFromPeerDescriptor(targetPeerDescriptor)))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createServer as createHttpServer, Server as HttpServer, IncomingMessage, ServerResponse } from 'http'
|
|
2
|
+
import { createServer as createHttpsServer, Server as HttpsServer } from 'https'
|
|
2
3
|
import EventEmitter from 'eventemitter3'
|
|
3
4
|
import { server as WsServer } from 'websocket'
|
|
4
5
|
import { ServerWebSocket } from './ServerWebSocket'
|
|
@@ -7,9 +8,10 @@ import {
|
|
|
7
8
|
} from '../IConnectionSource'
|
|
8
9
|
|
|
9
10
|
import { Logger, asAbortable } from '@streamr/utils'
|
|
10
|
-
import {
|
|
11
|
-
import { PortRange } from '../ConnectionManager'
|
|
11
|
+
import { WebSocketServerStartError } from '../../helpers/errors'
|
|
12
|
+
import { PortRange, TlsCertificate } from '../ConnectionManager'
|
|
12
13
|
import { range } from 'lodash'
|
|
14
|
+
import fs from 'fs'
|
|
13
15
|
|
|
14
16
|
const logger = new Logger(module)
|
|
15
17
|
|
|
@@ -22,30 +24,41 @@ declare class NodeJsWsServer extends WsServer { }
|
|
|
22
24
|
|
|
23
25
|
export class WebSocketServer extends EventEmitter<ConnectionSourceEvents> {
|
|
24
26
|
|
|
25
|
-
private httpServer?:
|
|
27
|
+
private httpServer?: HttpServer | HttpsServer
|
|
26
28
|
private wsServer?: WsServer
|
|
27
29
|
private readonly abortController = new AbortController()
|
|
28
|
-
|
|
29
|
-
public async start(portRange: PortRange,
|
|
30
|
+
|
|
31
|
+
public async start(portRange: PortRange, tlsCertificate?: TlsCertificate): Promise<number> {
|
|
30
32
|
const ports = range(portRange.min, portRange.max + 1)
|
|
31
33
|
for (const port of ports) {
|
|
32
34
|
try {
|
|
33
|
-
await asAbortable(this.startServer(port,
|
|
35
|
+
await asAbortable(this.startServer(port, tlsCertificate), this.abortController.signal)
|
|
34
36
|
return port
|
|
35
37
|
} catch (err) {
|
|
36
|
-
|
|
38
|
+
if (err.originalError?.code === 'EADDRINUSE') {
|
|
39
|
+
logger.debug(`failed to start WebSocket server on port: ${port} reattempting on next port`)
|
|
40
|
+
} else {
|
|
41
|
+
throw new WebSocketServerStartError(err)
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
44
|
}
|
|
39
|
-
throw new
|
|
45
|
+
throw new WebSocketServerStartError(`Failed to start WebSocket server on any port in range: ${portRange.min}-${portRange.min}`)
|
|
40
46
|
}
|
|
41
47
|
|
|
42
|
-
private startServer(port: number,
|
|
48
|
+
private startServer(port: number, tlsCertificate?: TlsCertificate): Promise<void> {
|
|
49
|
+
const requestListener = (request: IncomingMessage, response: ServerResponse<IncomingMessage>) => {
|
|
50
|
+
logger.trace('Received request for ' + request.url)
|
|
51
|
+
response.writeHead(404)
|
|
52
|
+
response.end()
|
|
53
|
+
}
|
|
43
54
|
return new Promise((resolve, reject) => {
|
|
44
|
-
this.httpServer =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
this.httpServer = tlsCertificate ?
|
|
56
|
+
createHttpsServer({
|
|
57
|
+
key: fs.readFileSync(tlsCertificate.privateKeyFileName),
|
|
58
|
+
cert: fs.readFileSync(tlsCertificate.certFileName)
|
|
59
|
+
}, requestListener)
|
|
60
|
+
:
|
|
61
|
+
createHttpServer(requestListener)
|
|
49
62
|
|
|
50
63
|
function originIsAllowed(_uorigin: string) {
|
|
51
64
|
return true
|
|
@@ -67,9 +80,8 @@ export class WebSocketServer extends EventEmitter<ConnectionSourceEvents> {
|
|
|
67
80
|
|
|
68
81
|
this.emit('connected', new ServerWebSocket(connection, request.resourceURL))
|
|
69
82
|
})
|
|
70
|
-
|
|
71
83
|
this.httpServer.once('error', (err: Error) => {
|
|
72
|
-
reject(new
|
|
84
|
+
reject(new WebSocketServerStartError('Starting Websocket server failed', err))
|
|
73
85
|
})
|
|
74
86
|
|
|
75
87
|
this.httpServer.once('listening', () => {
|
|
@@ -78,9 +90,10 @@ export class WebSocketServer extends EventEmitter<ConnectionSourceEvents> {
|
|
|
78
90
|
})
|
|
79
91
|
|
|
80
92
|
try {
|
|
81
|
-
|
|
93
|
+
// Listen only to IPv4 network interfaces, default value listens to IPv6 as well
|
|
94
|
+
this.httpServer.listen(port, '0.0.0.0')
|
|
82
95
|
} catch (e) {
|
|
83
|
-
reject(new
|
|
96
|
+
reject(new WebSocketServerStartError('Websocket server threw an exception', e))
|
|
84
97
|
}
|
|
85
98
|
})
|
|
86
99
|
}
|
|
@@ -96,7 +109,7 @@ export class WebSocketServer extends EventEmitter<ConnectionSourceEvents> {
|
|
|
96
109
|
})
|
|
97
110
|
}
|
|
98
111
|
|
|
99
|
-
private createWsServer(httpServer:
|
|
112
|
+
private createWsServer(httpServer: HttpServer | HttpsServer): WsServer {
|
|
100
113
|
// Use the real nodejs WebSocket server in Electron tests
|
|
101
114
|
|
|
102
115
|
if (typeof NodeJsWsServer !== 'undefined') {
|
package/src/dht/DhtNode.ts
CHANGED
|
@@ -20,11 +20,13 @@ import {
|
|
|
20
20
|
} from '../proto/packages/dht/protos/DhtRpc'
|
|
21
21
|
import * as Err from '../helpers/errors'
|
|
22
22
|
import { DisconnectionType, ITransport, TransportEvents } from '../transport/ITransport'
|
|
23
|
-
import { ConnectionManager, ConnectionManagerConfig, PortRange } from '../connection/ConnectionManager'
|
|
23
|
+
import { ConnectionManager, ConnectionManagerConfig, PortRange, TlsCertificate } from '../connection/ConnectionManager'
|
|
24
24
|
import { DhtRpcServiceClient, ExternalApiServiceClient } from '../proto/packages/dht/protos/DhtRpc.client'
|
|
25
25
|
import {
|
|
26
26
|
Logger,
|
|
27
|
-
MetricsContext
|
|
27
|
+
MetricsContext,
|
|
28
|
+
hexToBinary,
|
|
29
|
+
binaryToHex
|
|
28
30
|
} from '@streamr/utils'
|
|
29
31
|
import { toProtoRpcClient } from '@streamr/proto-rpc'
|
|
30
32
|
import { RandomContactList } from './contact/RandomContactList'
|
|
@@ -71,7 +73,7 @@ export interface DhtNodeOptions {
|
|
|
71
73
|
entryPoints?: PeerDescriptor[]
|
|
72
74
|
websocketHost?: string
|
|
73
75
|
websocketPortRange?: PortRange
|
|
74
|
-
|
|
76
|
+
peerId?: string
|
|
75
77
|
|
|
76
78
|
nodeName?: string
|
|
77
79
|
rpcRequestTimeout?: number
|
|
@@ -82,6 +84,8 @@ export interface DhtNodeOptions {
|
|
|
82
84
|
webrtcNewConnectionTimeout?: number
|
|
83
85
|
webrtcPortRange?: PortRange
|
|
84
86
|
maxConnections?: number
|
|
87
|
+
tlsCertificate?: TlsCertificate
|
|
88
|
+
externalIp?: string
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
export class DhtNodeConfig {
|
|
@@ -97,7 +101,7 @@ export class DhtNodeConfig {
|
|
|
97
101
|
storeMaxTtl = 60000
|
|
98
102
|
storeNumberOfCopies = 5
|
|
99
103
|
metricsContext = new MetricsContext()
|
|
100
|
-
|
|
104
|
+
peerId = new UUID().toHex()
|
|
101
105
|
|
|
102
106
|
transportLayer?: ITransport
|
|
103
107
|
peerDescriptor?: PeerDescriptor
|
|
@@ -111,7 +115,9 @@ export class DhtNodeConfig {
|
|
|
111
115
|
webrtcDatachannelBufferThresholdLow?: number
|
|
112
116
|
webrtcDatachannelBufferThresholdHigh?: number
|
|
113
117
|
webrtcNewConnectionTimeout?: number
|
|
118
|
+
externalIp?: string
|
|
114
119
|
webrtcPortRange?: PortRange
|
|
120
|
+
tlsCertificate?: TlsCertificate
|
|
115
121
|
|
|
116
122
|
constructor(conf: Partial<DhtNodeOptions>) {
|
|
117
123
|
// assign given non-undefined config vars over defaults
|
|
@@ -129,16 +135,16 @@ const logger = new Logger(module)
|
|
|
129
135
|
|
|
130
136
|
export type Events = TransportEvents & DhtNodeEvents
|
|
131
137
|
|
|
132
|
-
export const createPeerDescriptor = (msg?: ConnectivityResponse,
|
|
133
|
-
let
|
|
138
|
+
export const createPeerDescriptor = (msg?: ConnectivityResponse, peerId?: string, nodeName?: string): PeerDescriptor => {
|
|
139
|
+
let kademliaId: Uint8Array
|
|
134
140
|
if (msg) {
|
|
135
|
-
|
|
141
|
+
kademliaId = peerId ? hexToBinary(peerId) : PeerID.fromIp(msg.host).value
|
|
136
142
|
} else {
|
|
137
|
-
|
|
143
|
+
kademliaId = hexToBinary(peerId!)
|
|
138
144
|
}
|
|
139
|
-
const ret: PeerDescriptor = { kademliaId:
|
|
145
|
+
const ret: PeerDescriptor = { kademliaId, nodeName: nodeName ? nodeName : binaryToHex(kademliaId), type: NodeType.NODEJS }
|
|
140
146
|
if (msg && msg.websocket) {
|
|
141
|
-
ret.websocket = {
|
|
147
|
+
ret.websocket = { host: msg.websocket.host, port: msg.websocket.port, tls: msg.websocket.tls }
|
|
142
148
|
ret.openInternet = true
|
|
143
149
|
}
|
|
144
150
|
return ret
|
|
@@ -207,12 +213,13 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
207
213
|
webrtcDatachannelBufferThresholdHigh: this.config.webrtcDatachannelBufferThresholdHigh,
|
|
208
214
|
webrtcNewConnectionTimeout: this.config.webrtcNewConnectionTimeout,
|
|
209
215
|
webrtcPortRange: this.config.webrtcPortRange,
|
|
210
|
-
|
|
211
|
-
|
|
216
|
+
maxConnections: this.config.maxConnections,
|
|
217
|
+
tlsCertificate: this.config.tlsCertificate,
|
|
218
|
+
externalIp: this.config.externalIp
|
|
212
219
|
}
|
|
213
220
|
// If own PeerDescriptor is given in config, create a ConnectionManager with ws server
|
|
214
221
|
if (this.config.peerDescriptor?.websocket) {
|
|
215
|
-
connectionManagerConfig.websocketHost = this.config.peerDescriptor.websocket.
|
|
222
|
+
connectionManagerConfig.websocketHost = this.config.peerDescriptor.websocket.host
|
|
216
223
|
connectionManagerConfig.websocketPortRange = {
|
|
217
224
|
min: this.config.peerDescriptor.websocket.port,
|
|
218
225
|
max: this.config.peerDescriptor.websocket.port
|
|
@@ -442,7 +449,7 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
442
449
|
this.ownPeerDescriptor = this.config.peerDescriptor
|
|
443
450
|
} else {
|
|
444
451
|
this.ownPeerDescriptor = createPeerDescriptor(connectivityResponse,
|
|
445
|
-
this.config.
|
|
452
|
+
this.config.peerId,
|
|
446
453
|
this.config.nodeName)
|
|
447
454
|
}
|
|
448
455
|
return this.ownPeerDescriptor
|
|
@@ -619,12 +626,12 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
619
626
|
await this.router!.send(msg, reachableThrough)
|
|
620
627
|
}
|
|
621
628
|
|
|
622
|
-
public async joinDht(entryPointDescriptors: PeerDescriptor[], doRandomJoin?: boolean): Promise<void> {
|
|
629
|
+
public async joinDht(entryPointDescriptors: PeerDescriptor[], doRandomJoin?: boolean, retry?: boolean): Promise<void> {
|
|
623
630
|
if (!this.started) {
|
|
624
631
|
throw new Error('Cannot join DHT before calling start() on DhtNode')
|
|
625
632
|
}
|
|
626
633
|
await Promise.all(entryPointDescriptors.map((entryPoint) =>
|
|
627
|
-
this.peerDiscovery!.joinDht(entryPoint, doRandomJoin)
|
|
634
|
+
this.peerDiscovery!.joinDht(entryPoint, doRandomJoin, retry)
|
|
628
635
|
))
|
|
629
636
|
}
|
|
630
637
|
|
|
@@ -700,14 +707,6 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
700
707
|
return this.connectionManager!.getNumberOfWeakLockedConnections()
|
|
701
708
|
}
|
|
702
709
|
|
|
703
|
-
public getNodeName(): string {
|
|
704
|
-
if (this.config.nodeName) {
|
|
705
|
-
return this.config.nodeName
|
|
706
|
-
} else {
|
|
707
|
-
return 'unnamed node'
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
|
|
711
710
|
public isJoinOngoing(): boolean {
|
|
712
711
|
return this.peerDiscovery!.isJoinOngoing()
|
|
713
712
|
}
|
package/src/dht/DhtPeer.ts
CHANGED
|
@@ -69,7 +69,7 @@ export class DhtPeer extends Remote<IDhtRpcServiceClient> implements KBucketCont
|
|
|
69
69
|
return true
|
|
70
70
|
}
|
|
71
71
|
} catch (err) {
|
|
72
|
-
logger.
|
|
72
|
+
logger.trace(`ping failed on ${this.serviceId} to ${this.peerId.toKey()}: ${err}`)
|
|
73
73
|
}
|
|
74
74
|
return false
|
|
75
75
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { DiscoverySession } from './DiscoverySession'
|
|
2
2
|
import { DhtPeer } from '../DhtPeer'
|
|
3
3
|
import crypto from 'crypto'
|
|
4
|
-
import * as Err from '../../helpers/errors'
|
|
5
4
|
import { isSamePeerDescriptor, keyFromPeerDescriptor } from '../../helpers/peerIdFromPeerDescriptor'
|
|
6
5
|
import { PeerDescriptor } from '../../proto/packages/dht/protos/DhtRpc'
|
|
7
6
|
import { Logger, scheduleAtInterval, setAbortableTimeout } from '@streamr/utils'
|
|
@@ -41,13 +40,14 @@ export class PeerDiscovery {
|
|
|
41
40
|
|
|
42
41
|
private rejoinTimeoutRef?: NodeJS.Timeout
|
|
43
42
|
private readonly abortController: AbortController
|
|
43
|
+
private recoveryIntervalStarted = false
|
|
44
44
|
|
|
45
45
|
constructor(config: PeerDiscoveryConfig) {
|
|
46
46
|
this.config = config
|
|
47
47
|
this.abortController = new AbortController()
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
async joinDht(entryPointDescriptor: PeerDescriptor, doRandomJoin = true): Promise<void> {
|
|
50
|
+
async joinDht(entryPointDescriptor: PeerDescriptor, doRandomJoin = true, retry = true): Promise<void> {
|
|
51
51
|
if (this.stopped) {
|
|
52
52
|
return
|
|
53
53
|
}
|
|
@@ -90,16 +90,18 @@ export class PeerDiscovery {
|
|
|
90
90
|
if (randomSession) {
|
|
91
91
|
await randomSession.findClosestNodes(this.config.joinTimeout)
|
|
92
92
|
}
|
|
93
|
+
} catch (_e) {
|
|
94
|
+
logger.debug(`DHT join on ${this.config.serviceId} timed out`)
|
|
95
|
+
} finally {
|
|
93
96
|
if (!this.stopped) {
|
|
94
97
|
if (this.config.bucket.count() === 0) {
|
|
95
|
-
|
|
98
|
+
if (retry) {
|
|
99
|
+
setAbortableTimeout(() => this.rejoinDht(entryPointDescriptor), 1000, this.abortController.signal)
|
|
100
|
+
}
|
|
96
101
|
} else {
|
|
97
|
-
await
|
|
102
|
+
await this.ensureRecoveryIntervalIsRunning()
|
|
98
103
|
}
|
|
99
104
|
}
|
|
100
|
-
} catch (_e) {
|
|
101
|
-
throw new Err.DhtJoinTimeout('join timed out')
|
|
102
|
-
} finally {
|
|
103
105
|
this.ongoingDiscoverySessions.delete(session.sessionId)
|
|
104
106
|
if (randomSession) {
|
|
105
107
|
this.ongoingDiscoverySessions.delete(randomSession.sessionId)
|
|
@@ -128,7 +130,14 @@ export class PeerDiscovery {
|
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
|
|
131
|
-
private async
|
|
133
|
+
private async ensureRecoveryIntervalIsRunning(): Promise<void> {
|
|
134
|
+
if (!this.recoveryIntervalStarted) {
|
|
135
|
+
this.recoveryIntervalStarted = true
|
|
136
|
+
await scheduleAtInterval(() => this.fetchClosestPeersFromBucket(), 60000, true, this.abortController.signal)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private async fetchClosestPeersFromBucket(): Promise<void> {
|
|
132
141
|
if (this.stopped) {
|
|
133
142
|
return
|
|
134
143
|
}
|
|
@@ -30,9 +30,7 @@ export class RemoteRouter extends Remote<IRoutingServiceClient> {
|
|
|
30
30
|
timeout: 10000
|
|
31
31
|
}
|
|
32
32
|
try {
|
|
33
|
-
logger.trace('calling dhtClient.routeMessage')
|
|
34
33
|
const ack = await this.client.routeMessage(message, options)
|
|
35
|
-
logger.trace('dhtClient.routeMessage returned')
|
|
36
34
|
// Success signal if sent to destination and error includes duplicate
|
|
37
35
|
if (
|
|
38
36
|
isSamePeerDescriptor(params.destinationPeer!, this.peerDescriptor)
|
|
@@ -45,7 +43,7 @@ export class RemoteRouter extends Remote<IRoutingServiceClient> {
|
|
|
45
43
|
} catch (err) {
|
|
46
44
|
const fromNode = params.previousPeer ?
|
|
47
45
|
peerIdFromPeerDescriptor(params.previousPeer) : keyFromPeerDescriptor(params.sourcePeer!)
|
|
48
|
-
logger.
|
|
46
|
+
logger.trace(`Failed to send routeMessage from ${fromNode} to ${this.peerId.toKey()} with: ${err}`)
|
|
49
47
|
return false
|
|
50
48
|
}
|
|
51
49
|
return true
|
|
@@ -75,7 +73,7 @@ export class RemoteRouter extends Remote<IRoutingServiceClient> {
|
|
|
75
73
|
const fromNode = params.previousPeer ?
|
|
76
74
|
keyFromPeerDescriptor(params.previousPeer) : keyFromPeerDescriptor(params.sourcePeer!)
|
|
77
75
|
|
|
78
|
-
logger.
|
|
76
|
+
logger.trace(
|
|
79
77
|
`Failed to send forwardMessage from ${fromNode} to ${this.peerId.toKey()} with: ${err}`
|
|
80
78
|
)
|
|
81
79
|
return false
|
|
@@ -136,10 +136,10 @@ export class DataStore implements IStoreService {
|
|
|
136
136
|
try {
|
|
137
137
|
const response = await remoteStore.migrateData({ dataEntry }, doNotConnect)
|
|
138
138
|
if (response.error) {
|
|
139
|
-
logger.
|
|
139
|
+
logger.trace('RemoteStore::migrateData() returned error: ' + response.error)
|
|
140
140
|
}
|
|
141
141
|
} catch (e) {
|
|
142
|
-
logger.
|
|
142
|
+
logger.trace('RemoteStore::migrateData() threw an exception ' + e)
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -177,10 +177,10 @@ export class DataStore implements IStoreService {
|
|
|
177
177
|
successfulNodes.push(closestNodes[i])
|
|
178
178
|
logger.trace('remoteStore.storeData() returned success')
|
|
179
179
|
} else {
|
|
180
|
-
logger.
|
|
180
|
+
logger.trace('remoteStore.storeData() returned error: ' + response.error)
|
|
181
181
|
}
|
|
182
182
|
} catch (e) {
|
|
183
|
-
logger.
|
|
183
|
+
logger.trace('remoteStore.storeData() threw an exception ' + e)
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
return successfulNodes
|
|
@@ -217,11 +217,11 @@ export class DataStore implements IStoreService {
|
|
|
217
217
|
if (response.deleted) {
|
|
218
218
|
logger.trace('remoteStore.deleteData() returned success')
|
|
219
219
|
} else {
|
|
220
|
-
logger.
|
|
220
|
+
logger.trace('could not delete data from ' + PeerID.fromValue(closestNodes[i].kademliaId))
|
|
221
221
|
}
|
|
222
222
|
successfulNodes.push(closestNodes[i])
|
|
223
223
|
} catch (e) {
|
|
224
|
-
logger.
|
|
224
|
+
logger.trace('remoteStore.deleteData() threw an exception ' + e)
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
}
|
package/src/exports.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { SimulatorTransport } from './connection/Simulator/SimulatorTransport'
|
|
|
5
5
|
export { getRandomRegion, getRegionDelayMatrix } from './connection/Simulator/pings'
|
|
6
6
|
export { PeerDescriptor, Message, NodeType, DataEntry } from './proto/packages/dht/protos/DhtRpc'
|
|
7
7
|
export { ITransport } from './transport/ITransport'
|
|
8
|
-
export { ConnectionManager, ConnectionLocker, PortRange } from './connection/ConnectionManager'
|
|
8
|
+
export { ConnectionManager, ConnectionLocker, PortRange, TlsCertificate } from './connection/ConnectionManager'
|
|
9
9
|
export { PeerID, PeerIDKey } from './helpers/PeerID'
|
|
10
10
|
export { DhtPeer } from './dht/DhtPeer'
|
|
11
11
|
export { UUID } from './helpers/UUID'
|
package/src/helpers/errors.ts
CHANGED
|
@@ -33,7 +33,7 @@ class Err extends Error {
|
|
|
33
33
|
|
|
34
34
|
export class ConnectionFailed extends Err { constructor( message?: string, originalError?: Error | string) { super(ErrorCode.CONNECTION_FAILED, message, originalError) } }
|
|
35
35
|
export class CouldNotRoute extends Err { constructor(message?: string, originalError?: Error | string) { super(ErrorCode.COULD_NOT_ROUTE, message, originalError) } }
|
|
36
|
-
export class
|
|
36
|
+
export class WebSocketServerStartError extends Err { constructor(message?: string, originalError?: Error | string) { super(ErrorCode.STARTING_WEBSOCKET_SERVER_FAILED, message, originalError) } }
|
|
37
37
|
export class WebSocketConnectionRequestRejected extends Err { constructor(message?: string, originalError?: Error | string) { super(ErrorCode.WEBSOCKET_CONNECTION_REQUEST_REJECTED, message, originalError) } }
|
|
38
38
|
export class CouldNotStart extends Err { constructor(message?: string, originalError?: Error | string) { super(ErrorCode.COULD_NOT_START, message, originalError) } }
|
|
39
39
|
export class CouldNotStop extends Err { constructor(message?: string, originalError?: Error | string) { super(ErrorCode.COULD_NOT_STOP, message, originalError) } }
|
|
@@ -2,9 +2,7 @@ import { IMessageType } from '@protobuf-ts/runtime'
|
|
|
2
2
|
import {
|
|
3
3
|
ClosestPeersRequest,
|
|
4
4
|
ClosestPeersResponse,
|
|
5
|
-
ConnectivityMethod,
|
|
6
|
-
ConnectivityReportRequest,
|
|
7
|
-
ConnectivityReportResponse,
|
|
5
|
+
ConnectivityMethod,
|
|
8
6
|
ConnectivityRequest,
|
|
9
7
|
ConnectivityResponse,
|
|
10
8
|
DisconnectNotice,
|
|
@@ -41,8 +39,6 @@ export const protoClasses: Array<IMessageType<any>> = [
|
|
|
41
39
|
LeaveNotice,
|
|
42
40
|
PeerDescriptor,
|
|
43
41
|
ConnectivityMethod,
|
|
44
|
-
ConnectivityReportRequest,
|
|
45
|
-
ConnectivityReportResponse,
|
|
46
42
|
DisconnectNotice,
|
|
47
43
|
RouteMessageWrapper,
|
|
48
44
|
RouteMessageAck,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @generated by protobuf-ts 2.
|
|
1
|
+
// @generated by protobuf-ts 2.9.1 with parameter server_generic,generate_dependencies,long_type_number
|
|
2
2
|
// @generated from protobuf file "google/protobuf/any.proto" (package "google.protobuf", syntax proto3)
|
|
3
3
|
// tslint:disable
|
|
4
4
|
//
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @generated by protobuf-ts 2.
|
|
1
|
+
// @generated by protobuf-ts 2.9.1 with parameter server_generic,generate_dependencies,long_type_number
|
|
2
2
|
// @generated from protobuf file "google/protobuf/empty.proto" (package "google.protobuf", syntax proto3)
|
|
3
3
|
// tslint:disable
|
|
4
4
|
//
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @generated by protobuf-ts 2.
|
|
1
|
+
// @generated by protobuf-ts 2.9.1 with parameter server_generic,generate_dependencies,long_type_number
|
|
2
2
|
// @generated from protobuf file "google/protobuf/timestamp.proto" (package "google.protobuf", syntax proto3)
|
|
3
3
|
// tslint:disable
|
|
4
4
|
//
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @generated by protobuf-ts 2.
|
|
1
|
+
// @generated by protobuf-ts 2.9.1 with parameter server_generic,generate_dependencies,long_type_number
|
|
2
2
|
// @generated from protobuf file "packages/dht/protos/DhtRpc.proto" (package "dht", syntax proto3)
|
|
3
3
|
// tslint:disable
|
|
4
4
|
import { ExternalApiService } from "./DhtRpc";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @generated by protobuf-ts 2.
|
|
1
|
+
// @generated by protobuf-ts 2.9.1 with parameter server_generic,generate_dependencies,long_type_number
|
|
2
2
|
// @generated from protobuf file "packages/dht/protos/DhtRpc.proto" (package "dht", syntax proto3)
|
|
3
3
|
// tslint:disable
|
|
4
4
|
import { FindDataResponse } from "./DhtRpc";
|