@libp2p/tcp 10.1.19-8484de8a2 → 10.1.19-87bc8d4fb
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/index.min.js +1 -1
- package/dist/index.min.js.map +4 -4
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/listener.d.ts +4 -3
- package/dist/src/listener.d.ts.map +1 -1
- package/dist/src/listener.js +12 -14
- package/dist/src/listener.js.map +1 -1
- package/dist/src/socket-to-conn.d.ts +12 -5
- package/dist/src/socket-to-conn.d.ts.map +1 -1
- package/dist/src/socket-to-conn.js +174 -98
- package/dist/src/socket-to-conn.js.map +1 -1
- package/dist/src/tcp.d.ts.map +1 -1
- package/dist/src/tcp.js +9 -9
- package/dist/src/tcp.js.map +1 -1
- package/dist/src/utils.d.ts +1 -1
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +8 -18
- package/dist/src/utils.js.map +1 -1
- package/package.json +11 -11
- package/src/index.ts +5 -0
- package/src/listener.ts +15 -16
- package/src/socket-to-conn.ts +201 -114
- package/src/tcp.ts +9 -9
- package/src/utils.ts +8 -19
package/dist/src/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Multiaddr } from '@multiformats/multiaddr';
|
|
2
2
|
import type { ListenOptions, IpcSocketConnectOpts, TcpSocketConnectOpts } from 'net';
|
|
3
3
|
export type NetConfig = ListenOptions | (IpcSocketConnectOpts & TcpSocketConnectOpts);
|
|
4
|
-
export declare function multiaddrToNetConfig(addr: Multiaddr,
|
|
4
|
+
export declare function multiaddrToNetConfig(addr: Multiaddr, config?: NetConfig): NetConfig;
|
|
5
5
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/src/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,KAAK,CAAA;AAEpF,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,CAAC,oBAAoB,GAAG,oBAAoB,CAAC,CAAA;AAErF,wBAAgB,oBAAoB,CAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAE,SAAc,GAAG,SAAS,CAqBxF"}
|
package/dist/src/utils.js
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import os from 'os';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export function multiaddrToNetConfig(addr, options = {}) {
|
|
8
|
-
if (Unix.exactMatch(addr)) {
|
|
9
|
-
const listenPath = addr.getComponents().find(c => c.code === CODE_UNIX)?.value;
|
|
10
|
-
if (listenPath == null) {
|
|
11
|
-
throw new InvalidParametersError(`Multiaddr ${addr} was not a Unix address`);
|
|
12
|
-
}
|
|
13
|
-
// unix socket listening
|
|
3
|
+
export function multiaddrToNetConfig(addr, config = {}) {
|
|
4
|
+
const listenPath = addr.getPath();
|
|
5
|
+
// unix socket listening
|
|
6
|
+
if (listenPath != null) {
|
|
14
7
|
if (os.platform() === 'win32') {
|
|
15
8
|
// Use named pipes on Windows systems.
|
|
16
9
|
return { path: path.join('\\\\.\\pipe\\', listenPath) };
|
|
@@ -19,15 +12,12 @@ export function multiaddrToNetConfig(addr, options = {}) {
|
|
|
19
12
|
return { path: listenPath };
|
|
20
13
|
}
|
|
21
14
|
}
|
|
22
|
-
const
|
|
23
|
-
const host = config.host;
|
|
24
|
-
const port = config.port;
|
|
15
|
+
const options = addr.toOptions();
|
|
25
16
|
// tcp listening
|
|
26
17
|
return {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
ipv6Only:
|
|
30
|
-
...options
|
|
18
|
+
...config,
|
|
19
|
+
...options,
|
|
20
|
+
ipv6Only: options.family === 6
|
|
31
21
|
};
|
|
32
22
|
}
|
|
33
23
|
//# sourceMappingURL=utils.js.map
|
package/dist/src/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,IAAI,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,IAAI,MAAM,MAAM,CAAA;AAMvB,MAAM,UAAU,oBAAoB,CAAE,IAAe,EAAE,SAAoB,EAAE;IAC3E,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;IAEjC,wBAAwB;IACxB,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;YAC9B,sCAAsC;YACtC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,UAAU,CAAC,EAAE,CAAA;QACzD,CAAC;aAAM,CAAC;YACN,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAA;QAC7B,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,CAAA;IAEhC,gBAAgB;IAChB,OAAO;QACL,GAAG,MAAM;QACT,GAAG,OAAO;QACV,QAAQ,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC;KAC/B,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libp2p/tcp",
|
|
3
|
-
"version": "10.1.19-
|
|
3
|
+
"version": "10.1.19-87bc8d4fb",
|
|
4
4
|
"description": "A TCP transport for libp2p",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"homepage": "https://github.com/libp2p/js-libp2p/tree/main/packages/transport-tcp#readme",
|
|
@@ -53,23 +53,23 @@
|
|
|
53
53
|
"test:electron-main": "aegir test -t electron-main"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@libp2p/interface": "2.11.0-
|
|
57
|
-
"@libp2p/utils": "6.7.2-
|
|
58
|
-
"@multiformats/multiaddr": "^
|
|
59
|
-
"@multiformats/multiaddr-matcher": "^
|
|
56
|
+
"@libp2p/interface": "2.11.0-87bc8d4fb",
|
|
57
|
+
"@libp2p/utils": "6.7.2-87bc8d4fb",
|
|
58
|
+
"@multiformats/multiaddr": "^12.4.4",
|
|
59
|
+
"@multiformats/multiaddr-matcher": "^2.0.0",
|
|
60
60
|
"@types/sinon": "^17.0.4",
|
|
61
61
|
"main-event": "^1.0.1",
|
|
62
|
+
"p-defer": "^4.0.1",
|
|
62
63
|
"p-event": "^6.0.1",
|
|
63
64
|
"progress-events": "^1.0.1",
|
|
64
|
-
"
|
|
65
|
+
"race-event": "^1.3.0",
|
|
66
|
+
"stream-to-it": "^1.0.1"
|
|
65
67
|
},
|
|
66
68
|
"devDependencies": {
|
|
67
|
-
"@libp2p/logger": "5.2.0-
|
|
68
|
-
"aegir": "^47.0.
|
|
69
|
-
"delay": "^6.0.0",
|
|
70
|
-
"p-defer": "^4.0.1",
|
|
69
|
+
"@libp2p/logger": "5.2.0-87bc8d4fb",
|
|
70
|
+
"aegir": "^47.0.14",
|
|
71
71
|
"p-wait-for": "^5.0.2",
|
|
72
|
-
"sinon": "^
|
|
72
|
+
"sinon": "^20.0.0",
|
|
73
73
|
"sinon-ts": "^2.0.0",
|
|
74
74
|
"wherearewe": "^2.0.1"
|
|
75
75
|
},
|
package/src/index.ts
CHANGED
|
@@ -59,6 +59,11 @@ export interface TCPOptions {
|
|
|
59
59
|
*/
|
|
60
60
|
outboundSocketInactivityTimeout?: number
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* When closing a socket, wait this long for it to close gracefully before it is closed more forcibly
|
|
64
|
+
*/
|
|
65
|
+
socketCloseTimeout?: number
|
|
66
|
+
|
|
62
67
|
/**
|
|
63
68
|
* Set this property to reject connections when the server's connection count gets high.
|
|
64
69
|
* https://nodejs.org/api/net.html#servermaxconnections
|
package/src/listener.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import net from '
|
|
1
|
+
import net from 'net'
|
|
2
2
|
import { AlreadyStartedError, InvalidParametersError, NotStartedError } from '@libp2p/interface'
|
|
3
|
-
import { getThinWaistAddresses } from '@libp2p/utils'
|
|
3
|
+
import { getThinWaistAddresses } from '@libp2p/utils/get-thin-waist-addresses'
|
|
4
4
|
import { multiaddr } from '@multiformats/multiaddr'
|
|
5
5
|
import { TypedEventEmitter, setMaxListeners } from 'main-event'
|
|
6
6
|
import { pEvent } from 'p-event'
|
|
@@ -8,12 +8,13 @@ import { toMultiaddrConnection } from './socket-to-conn.js'
|
|
|
8
8
|
import { multiaddrToNetConfig } from './utils.js'
|
|
9
9
|
import type { CloseServerOnMaxConnectionsOpts, TCPCreateListenerOptions } from './index.js'
|
|
10
10
|
import type { NetConfig } from './utils.js'
|
|
11
|
-
import type { ComponentLogger, Logger, MultiaddrConnection, CounterGroup, MetricGroup, Metrics, Listener, ListenerEvents, Upgrader
|
|
11
|
+
import type { ComponentLogger, Logger, MultiaddrConnection, CounterGroup, MetricGroup, Metrics, Listener, ListenerEvents, Upgrader } from '@libp2p/interface'
|
|
12
12
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
13
13
|
|
|
14
14
|
interface Context extends TCPCreateListenerOptions {
|
|
15
15
|
upgrader: Upgrader
|
|
16
|
-
|
|
16
|
+
socketInactivityTimeout?: number
|
|
17
|
+
socketCloseTimeout?: number
|
|
17
18
|
maxConnections?: number
|
|
18
19
|
backlog?: number
|
|
19
20
|
metrics?: Metrics
|
|
@@ -60,7 +61,6 @@ export class TCPListener extends TypedEventEmitter<ListenerEvents> implements Li
|
|
|
60
61
|
|
|
61
62
|
context.keepAlive = context.keepAlive ?? true
|
|
62
63
|
context.noDelay = context.noDelay ?? true
|
|
63
|
-
context.allowHalfOpen = context.allowHalfOpen ?? false
|
|
64
64
|
|
|
65
65
|
this.shutdownController = new AbortController()
|
|
66
66
|
setMaxListeners(Infinity, this.shutdownController.signal)
|
|
@@ -161,14 +161,14 @@ export class TCPListener extends TypedEventEmitter<ListenerEvents> implements Li
|
|
|
161
161
|
|
|
162
162
|
let maConn: MultiaddrConnection
|
|
163
163
|
try {
|
|
164
|
-
maConn = toMultiaddrConnection({
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
maConn = toMultiaddrConnection(socket, {
|
|
165
|
+
listeningAddr: this.status.listeningAddr,
|
|
166
|
+
socketInactivityTimeout: this.context.socketInactivityTimeout,
|
|
167
|
+
socketCloseTimeout: this.context.socketCloseTimeout,
|
|
167
168
|
metrics: this.metrics?.events,
|
|
168
169
|
metricPrefix: `${this.addr} `,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
log: this.context.logger.forComponent('libp2p:tcp:connection')
|
|
170
|
+
logger: this.context.logger,
|
|
171
|
+
direction: 'inbound'
|
|
172
172
|
})
|
|
173
173
|
} catch (err: any) {
|
|
174
174
|
this.log.error('inbound connection failed', err)
|
|
@@ -210,7 +210,6 @@ export class TCPListener extends TypedEventEmitter<ListenerEvents> implements Li
|
|
|
210
210
|
this.context.closeServerOnMaxConnections != null &&
|
|
211
211
|
this.sockets.size >= this.context.closeServerOnMaxConnections.closeAbove
|
|
212
212
|
) {
|
|
213
|
-
this.log('pausing incoming connections as limit is exceeded - %d/%d', this.sockets.size, this.context.closeServerOnMaxConnections.closeAbove)
|
|
214
213
|
this.pause()
|
|
215
214
|
}
|
|
216
215
|
})
|
|
@@ -265,11 +264,11 @@ export class TCPListener extends TypedEventEmitter<ListenerEvents> implements Li
|
|
|
265
264
|
}
|
|
266
265
|
}
|
|
267
266
|
|
|
268
|
-
async close (
|
|
267
|
+
async close (): Promise<void> {
|
|
269
268
|
const events: Array<Promise<void>> = []
|
|
270
269
|
|
|
271
270
|
if (this.server.listening) {
|
|
272
|
-
events.push(pEvent(this.server, 'close'
|
|
271
|
+
events.push(pEvent(this.server, 'close'))
|
|
273
272
|
}
|
|
274
273
|
|
|
275
274
|
// shut down the server socket, permanently
|
|
@@ -282,7 +281,7 @@ export class TCPListener extends TypedEventEmitter<ListenerEvents> implements Li
|
|
|
282
281
|
// the server socket in case new sockets are opened during the shutdown
|
|
283
282
|
this.sockets.forEach(socket => {
|
|
284
283
|
if (socket.readable) {
|
|
285
|
-
events.push(pEvent(socket, 'close'
|
|
284
|
+
events.push(pEvent(socket, 'close'))
|
|
286
285
|
socket.destroy()
|
|
287
286
|
}
|
|
288
287
|
})
|
|
@@ -321,7 +320,7 @@ export class TCPListener extends TypedEventEmitter<ListenerEvents> implements Li
|
|
|
321
320
|
return
|
|
322
321
|
}
|
|
323
322
|
|
|
324
|
-
this.log('
|
|
323
|
+
this.log('closing server on %s', this.server.address())
|
|
325
324
|
|
|
326
325
|
// NodeJS implementation tracks listening status with `this._handle` property.
|
|
327
326
|
// - Server.close() sets this._handle to null immediately. If this._handle is null, NotStartedError is thrown
|
package/src/socket-to-conn.ts
CHANGED
|
@@ -1,144 +1,231 @@
|
|
|
1
1
|
import { InvalidParametersError, TimeoutError } from '@libp2p/interface'
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
2
|
+
import { ipPortToMultiaddr as toMultiaddr } from '@libp2p/utils/ip-port-to-multiaddr'
|
|
3
|
+
import pDefer from 'p-defer'
|
|
4
|
+
import { raceEvent } from 'race-event'
|
|
5
|
+
import { duplex } from 'stream-to-it'
|
|
6
|
+
import { CLOSE_TIMEOUT, SOCKET_TIMEOUT } from './constants.js'
|
|
7
|
+
import { multiaddrToNetConfig } from './utils.js'
|
|
8
|
+
import type { ComponentLogger, MultiaddrConnection, CounterGroup } from '@libp2p/interface'
|
|
9
|
+
import type { AbortOptions, Multiaddr } from '@multiformats/multiaddr'
|
|
8
10
|
import type { Socket } from 'net'
|
|
9
|
-
import type {
|
|
11
|
+
import type { DeferredPromise } from 'p-defer'
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
interface ToConnectionOptions {
|
|
14
|
+
listeningAddr?: Multiaddr
|
|
13
15
|
remoteAddr?: Multiaddr
|
|
16
|
+
localAddr?: Multiaddr
|
|
17
|
+
socketInactivityTimeout?: number
|
|
18
|
+
socketCloseTimeout?: number
|
|
19
|
+
metrics?: CounterGroup
|
|
20
|
+
metricPrefix?: string
|
|
21
|
+
logger: ComponentLogger
|
|
22
|
+
direction: 'inbound' | 'outbound'
|
|
14
23
|
}
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Convert a socket into a MultiaddrConnection
|
|
27
|
+
* https://github.com/libp2p/interface-transport#multiaddrconnection
|
|
28
|
+
*/
|
|
29
|
+
export const toMultiaddrConnection = (socket: Socket, options: ToConnectionOptions): MultiaddrConnection => {
|
|
30
|
+
let closePromise: DeferredPromise<void>
|
|
31
|
+
const direction = options.direction
|
|
32
|
+
const metrics = options.metrics
|
|
33
|
+
const metricPrefix = options.metricPrefix ?? ''
|
|
34
|
+
const inactivityTimeout = options.socketInactivityTimeout ?? SOCKET_TIMEOUT
|
|
35
|
+
const closeTimeout = options.socketCloseTimeout ?? CLOSE_TIMEOUT
|
|
36
|
+
let timedOut = false
|
|
37
|
+
let errored = false
|
|
38
|
+
|
|
39
|
+
// Check if we are connected on a unix path
|
|
40
|
+
if (options.listeningAddr?.getPath() != null) {
|
|
41
|
+
options.remoteAddr = options.listeningAddr
|
|
42
|
+
}
|
|
18
43
|
|
|
19
|
-
|
|
20
|
-
|
|
44
|
+
if (options.remoteAddr?.getPath() != null) {
|
|
45
|
+
options.localAddr = options.remoteAddr
|
|
46
|
+
}
|
|
21
47
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} else if (remoteAddr == null) {
|
|
26
|
-
if (init.socket.remoteAddress == null || init.socket.remotePort == null) {
|
|
27
|
-
// this can be undefined if the socket is destroyed (for example, if the client disconnected)
|
|
28
|
-
// https://nodejs.org/dist/latest-v16.x/docs/api/net.html#socketremoteaddress
|
|
29
|
-
throw new InvalidParametersError('Could not determine remote address or port')
|
|
30
|
-
}
|
|
48
|
+
// handle socket errors
|
|
49
|
+
socket.on('error', err => {
|
|
50
|
+
errored = true
|
|
31
51
|
|
|
32
|
-
|
|
52
|
+
if (!timedOut) {
|
|
53
|
+
maConn.log.error('%s socket error - %e', direction, err)
|
|
54
|
+
metrics?.increment({ [`${metricPrefix}error`]: true })
|
|
33
55
|
}
|
|
34
56
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
this.socket = init.socket
|
|
41
|
-
|
|
42
|
-
// handle incoming data
|
|
43
|
-
this.socket.on('data', buf => {
|
|
44
|
-
this.onData(buf)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
// handle socket errors
|
|
48
|
-
this.socket.on('error', err => {
|
|
49
|
-
this.log('tcp error', remoteAddr, err)
|
|
50
|
-
|
|
51
|
-
this.abort(err)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
// by default there is no timeout
|
|
55
|
-
// https://nodejs.org/dist/latest-v16.x/docs/api/net.html#socketsettimeouttimeout-callback
|
|
56
|
-
this.socket.setTimeout(init.inactivityTimeout ?? (2 * 60 * 1_000))
|
|
57
|
-
|
|
58
|
-
this.socket.once('timeout', () => {
|
|
59
|
-
this.log('tcp timeout', remoteAddr)
|
|
60
|
-
// if the socket times out due to inactivity we must manually close the connection
|
|
61
|
-
// https://nodejs.org/dist/latest-v16.x/docs/api/net.html#event-timeout
|
|
62
|
-
this.abort(new TimeoutError())
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
this.socket.once('end', () => {
|
|
66
|
-
this.log('tcp end', remoteAddr)
|
|
67
|
-
|
|
68
|
-
// the remote sent a FIN packet which means no more data will be sent
|
|
69
|
-
// https://nodejs.org/dist/latest-v16.x/docs/api/net.html#event-end
|
|
70
|
-
// half open TCP sockets are disabled by default so Node.js should send a
|
|
71
|
-
// FIN in response to this event and then emit a 'close' event, during
|
|
72
|
-
// which we tear down the MultiaddrConnection so there is nothing to do
|
|
73
|
-
// until that occurs
|
|
74
|
-
this.onTransportClosed()
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
this.socket.once('close', hadError => {
|
|
78
|
-
this.log('tcp close', remoteAddr)
|
|
79
|
-
|
|
80
|
-
if (hadError) {
|
|
81
|
-
this.abort(new Error('TCP transmission error'))
|
|
82
|
-
return
|
|
83
|
-
}
|
|
57
|
+
socket.destroy()
|
|
58
|
+
maConn.timeline.close = Date.now()
|
|
59
|
+
})
|
|
84
60
|
|
|
85
|
-
|
|
86
|
-
})
|
|
61
|
+
let remoteAddr: Multiaddr
|
|
87
62
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
63
|
+
if (options.remoteAddr != null) {
|
|
64
|
+
remoteAddr = options.remoteAddr
|
|
65
|
+
} else {
|
|
66
|
+
if (socket.remoteAddress == null || socket.remotePort == null) {
|
|
67
|
+
// this can be undefined if the socket is destroyed (for example, if the client disconnected)
|
|
68
|
+
// https://nodejs.org/dist/latest-v16.x/docs/api/net.html#socketremoteaddress
|
|
69
|
+
throw new InvalidParametersError('Could not determine remote address or port')
|
|
70
|
+
}
|
|
91
71
|
|
|
92
|
-
|
|
93
|
-
})
|
|
72
|
+
remoteAddr = toMultiaddr(socket.remoteAddress, socket.remotePort)
|
|
94
73
|
}
|
|
95
74
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
75
|
+
const lOpts = multiaddrToNetConfig(remoteAddr)
|
|
76
|
+
const lOptsStr = lOpts.path ?? `${lOpts.host ?? ''}:${lOpts.port ?? ''}`
|
|
77
|
+
const { sink, source } = duplex(socket)
|
|
78
|
+
|
|
79
|
+
// by default there is no timeout
|
|
80
|
+
// https://nodejs.org/dist/latest-v16.x/docs/api/net.html#socketsettimeouttimeout-callback
|
|
81
|
+
socket.setTimeout(inactivityTimeout)
|
|
82
|
+
|
|
83
|
+
socket.once('timeout', () => {
|
|
84
|
+
timedOut = true
|
|
85
|
+
maConn.log('%s %s socket read timeout', direction, lOptsStr)
|
|
86
|
+
metrics?.increment({ [`${metricPrefix}timeout`]: true })
|
|
87
|
+
|
|
88
|
+
// if the socket times out due to inactivity we must manually close the connection
|
|
89
|
+
// https://nodejs.org/dist/latest-v16.x/docs/api/net.html#event-timeout
|
|
90
|
+
socket.destroy(new TimeoutError())
|
|
91
|
+
maConn.timeline.close = Date.now()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
socket.once('close', () => {
|
|
95
|
+
// record metric for clean exit
|
|
96
|
+
if (!timedOut && !errored) {
|
|
97
|
+
maConn.log('%s %s socket close', direction, lOptsStr)
|
|
98
|
+
metrics?.increment({ [`${metricPrefix}close`]: true })
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// In instances where `close` was not explicitly called,
|
|
102
|
+
// such as an iterable stream ending, ensure we have set the close
|
|
103
|
+
// timeline
|
|
104
|
+
socket.destroy()
|
|
105
|
+
maConn.timeline.close = Date.now()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
socket.once('end', () => {
|
|
109
|
+
// the remote sent a FIN packet which means no more data will be sent
|
|
110
|
+
// https://nodejs.org/dist/latest-v16.x/docs/api/net.html#event-end
|
|
111
|
+
maConn.log('%s %s socket end', direction, lOptsStr)
|
|
112
|
+
metrics?.increment({ [`${metricPrefix}end`]: true })
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const maConn: MultiaddrConnection = {
|
|
116
|
+
async sink (source) {
|
|
117
|
+
try {
|
|
118
|
+
await sink((async function * () {
|
|
119
|
+
for await (const buf of source) {
|
|
120
|
+
if (buf instanceof Uint8Array) {
|
|
121
|
+
yield buf
|
|
122
|
+
} else {
|
|
123
|
+
yield buf.subarray()
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
})())
|
|
127
|
+
} catch (err: any) {
|
|
128
|
+
// If aborted we can safely ignore
|
|
129
|
+
if (err.type !== 'aborted') {
|
|
130
|
+
// If the source errored the socket will already have been destroyed by
|
|
131
|
+
// duplex(). If the socket errored it will already be
|
|
132
|
+
// destroyed. There's nothing to do here except log the error & return.
|
|
133
|
+
maConn.log.error('%s %s error in sink - %e', direction, lOptsStr, err)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// we have finished writing, send the FIN message
|
|
138
|
+
socket.end()
|
|
139
|
+
},
|
|
99
140
|
|
|
100
|
-
|
|
101
|
-
sentBytes += buf.byteLength
|
|
102
|
-
canSendMore = this.socket.write(buf)
|
|
141
|
+
source,
|
|
103
142
|
|
|
104
|
-
|
|
105
|
-
|
|
143
|
+
// If the remote address was passed, use it - it may have the peer ID encapsulated
|
|
144
|
+
remoteAddr,
|
|
145
|
+
|
|
146
|
+
timeline: { open: Date.now() },
|
|
147
|
+
|
|
148
|
+
async close (options: AbortOptions = {}) {
|
|
149
|
+
if (socket.closed) {
|
|
150
|
+
maConn.log('the %s %s socket is already closed', direction, lOptsStr)
|
|
151
|
+
return
|
|
106
152
|
}
|
|
107
|
-
}
|
|
108
153
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
154
|
+
if (socket.destroyed) {
|
|
155
|
+
maConn.log('the %s %s socket is already destroyed', direction, lOptsStr)
|
|
156
|
+
return
|
|
157
|
+
}
|
|
114
158
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
159
|
+
if (closePromise != null) {
|
|
160
|
+
return closePromise.promise
|
|
161
|
+
}
|
|
119
162
|
|
|
120
|
-
|
|
163
|
+
try {
|
|
164
|
+
closePromise = pDefer()
|
|
165
|
+
|
|
166
|
+
// close writable end of socket
|
|
167
|
+
socket.end()
|
|
168
|
+
|
|
169
|
+
// convert EventEmitter to EventTarget
|
|
170
|
+
const eventTarget = socketToEventTarget(socket)
|
|
171
|
+
|
|
172
|
+
// don't wait forever to close
|
|
173
|
+
const signal = options.signal ?? AbortSignal.timeout(closeTimeout)
|
|
174
|
+
|
|
175
|
+
// wait for any unsent data to be sent
|
|
176
|
+
if (socket.writableLength > 0) {
|
|
177
|
+
maConn.log('%s %s draining socket', direction, lOptsStr)
|
|
178
|
+
await raceEvent(eventTarget, 'drain', signal, {
|
|
179
|
+
errorEvent: 'error'
|
|
180
|
+
})
|
|
181
|
+
maConn.log('%s %s socket drained', direction, lOptsStr)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
await Promise.all([
|
|
185
|
+
raceEvent(eventTarget, 'close', signal, {
|
|
186
|
+
errorEvent: 'error'
|
|
187
|
+
}),
|
|
188
|
+
|
|
189
|
+
// all bytes have been sent we can destroy the socket
|
|
190
|
+
// eslint-disable-next-line @typescript-eslint/await-thenable
|
|
191
|
+
socket.destroy()
|
|
192
|
+
])
|
|
193
|
+
} catch (err: any) {
|
|
194
|
+
this.abort(err)
|
|
195
|
+
} finally {
|
|
196
|
+
closePromise.resolve()
|
|
197
|
+
}
|
|
198
|
+
},
|
|
121
199
|
|
|
122
|
-
|
|
123
|
-
|
|
200
|
+
abort: (err: Error) => {
|
|
201
|
+
maConn.log('%s %s socket abort due to error - %e', direction, lOptsStr, err)
|
|
124
202
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
203
|
+
// the abortSignalListener may already destroyed the socket with an error
|
|
204
|
+
socket.destroy()
|
|
128
205
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
206
|
+
// closing a socket is always asynchronous (must wait for "close" event)
|
|
207
|
+
// but the tests expect this to be a synchronous operation so we have to
|
|
208
|
+
// set the close time here. the tests should be refactored to reflect
|
|
209
|
+
// reality.
|
|
210
|
+
maConn.timeline.close = Date.now()
|
|
211
|
+
},
|
|
132
212
|
|
|
133
|
-
|
|
134
|
-
this.socket.resume()
|
|
213
|
+
log: options.logger.forComponent('libp2p:tcp:connection')
|
|
135
214
|
}
|
|
215
|
+
|
|
216
|
+
return maConn
|
|
136
217
|
}
|
|
137
218
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
219
|
+
function socketToEventTarget (obj?: any): EventTarget {
|
|
220
|
+
const eventTarget = {
|
|
221
|
+
addEventListener: (type: any, cb: any) => {
|
|
222
|
+
obj.addListener(type, cb)
|
|
223
|
+
},
|
|
224
|
+
removeEventListener: (type: any, cb: any) => {
|
|
225
|
+
obj.removeListener(type, cb)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// @ts-expect-error partial implementation
|
|
230
|
+
return eventTarget
|
|
144
231
|
}
|
package/src/tcp.ts
CHANGED
|
@@ -75,7 +75,6 @@ export class TCP implements Transport<TCPDialEvents> {
|
|
|
75
75
|
async dial (ma: Multiaddr, options: TCPDialOptions): Promise<Connection> {
|
|
76
76
|
options.keepAlive = options.keepAlive ?? true
|
|
77
77
|
options.noDelay = options.noDelay ?? true
|
|
78
|
-
options.allowHalfOpen = options.allowHalfOpen ?? false
|
|
79
78
|
|
|
80
79
|
// options.signal destroys the socket before 'connect' event
|
|
81
80
|
const socket = await this._connect(ma, options)
|
|
@@ -83,13 +82,13 @@ export class TCP implements Transport<TCPDialEvents> {
|
|
|
83
82
|
let maConn: MultiaddrConnection
|
|
84
83
|
|
|
85
84
|
try {
|
|
86
|
-
maConn = toMultiaddrConnection({
|
|
87
|
-
socket,
|
|
88
|
-
inactivityTimeout: this.opts.outboundSocketInactivityTimeout,
|
|
89
|
-
metrics: this.metrics?.events,
|
|
90
|
-
direction: 'outbound',
|
|
85
|
+
maConn = toMultiaddrConnection(socket, {
|
|
91
86
|
remoteAddr: ma,
|
|
92
|
-
|
|
87
|
+
socketInactivityTimeout: this.opts.outboundSocketInactivityTimeout,
|
|
88
|
+
socketCloseTimeout: this.opts.socketCloseTimeout,
|
|
89
|
+
metrics: this.metrics?.events,
|
|
90
|
+
logger: this.components.logger,
|
|
91
|
+
direction: 'outbound'
|
|
93
92
|
})
|
|
94
93
|
} catch (err: any) {
|
|
95
94
|
this.metrics?.errors.increment({ outbound_to_connection: true })
|
|
@@ -121,7 +120,7 @@ export class TCP implements Transport<TCPDialEvents> {
|
|
|
121
120
|
...options
|
|
122
121
|
}) as (IpcSocketConnectOpts & TcpSocketConnectOpts)
|
|
123
122
|
|
|
124
|
-
this.log('dialing %a
|
|
123
|
+
this.log('dialing %a', ma)
|
|
125
124
|
rawSocket = net.connect(cOpts)
|
|
126
125
|
|
|
127
126
|
const onError = (err: Error): void => {
|
|
@@ -193,7 +192,8 @@ export class TCP implements Transport<TCPDialEvents> {
|
|
|
193
192
|
maxConnections: this.opts.maxConnections,
|
|
194
193
|
backlog: this.opts.backlog,
|
|
195
194
|
closeServerOnMaxConnections: this.opts.closeServerOnMaxConnections,
|
|
196
|
-
|
|
195
|
+
socketInactivityTimeout: this.opts.inboundSocketInactivityTimeout,
|
|
196
|
+
socketCloseTimeout: this.opts.socketCloseTimeout,
|
|
197
197
|
metrics: this.components.metrics,
|
|
198
198
|
logger: this.components.logger
|
|
199
199
|
})
|
package/src/utils.ts
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
import os from 'os'
|
|
2
2
|
import path from 'path'
|
|
3
|
-
import { InvalidParametersError } from '@libp2p/interface'
|
|
4
|
-
import { getNetConfig } from '@libp2p/utils'
|
|
5
|
-
import { CODE_UNIX } from '@multiformats/multiaddr'
|
|
6
|
-
import { Unix } from '@multiformats/multiaddr-matcher'
|
|
7
3
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
8
4
|
import type { ListenOptions, IpcSocketConnectOpts, TcpSocketConnectOpts } from 'net'
|
|
9
5
|
|
|
10
6
|
export type NetConfig = ListenOptions | (IpcSocketConnectOpts & TcpSocketConnectOpts)
|
|
11
7
|
|
|
12
|
-
export function multiaddrToNetConfig (addr: Multiaddr,
|
|
13
|
-
|
|
14
|
-
const listenPath = addr.getComponents().find(c => c.code === CODE_UNIX)?.value
|
|
8
|
+
export function multiaddrToNetConfig (addr: Multiaddr, config: NetConfig = {}): NetConfig {
|
|
9
|
+
const listenPath = addr.getPath()
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// unix socket listening
|
|
11
|
+
// unix socket listening
|
|
12
|
+
if (listenPath != null) {
|
|
21
13
|
if (os.platform() === 'win32') {
|
|
22
14
|
// Use named pipes on Windows systems.
|
|
23
15
|
return { path: path.join('\\\\.\\pipe\\', listenPath) }
|
|
@@ -26,15 +18,12 @@ export function multiaddrToNetConfig (addr: Multiaddr, options: NetConfig = {}):
|
|
|
26
18
|
}
|
|
27
19
|
}
|
|
28
20
|
|
|
29
|
-
const
|
|
30
|
-
const host = config.host
|
|
31
|
-
const port = config.port
|
|
21
|
+
const options = addr.toOptions()
|
|
32
22
|
|
|
33
23
|
// tcp listening
|
|
34
24
|
return {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
ipv6Only:
|
|
38
|
-
...options
|
|
25
|
+
...config,
|
|
26
|
+
...options,
|
|
27
|
+
ipv6Only: options.family === 6
|
|
39
28
|
}
|
|
40
29
|
}
|