@libp2p/tcp 10.0.6-24fa1d5af → 10.0.6-d9c7e0f7e
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/src/constants.js +1 -1
- package/dist/src/index.d.ts +1 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/listener.d.ts.map +1 -1
- package/dist/src/listener.js +6 -7
- package/dist/src/listener.js.map +1 -1
- package/dist/src/socket-to-conn.d.ts +0 -1
- package/dist/src/socket-to-conn.d.ts.map +1 -1
- package/dist/src/socket-to-conn.js +74 -74
- package/dist/src/socket-to-conn.js.map +1 -1
- package/dist/src/tcp.d.ts.map +1 -1
- package/dist/src/tcp.js +19 -34
- package/dist/src/tcp.js.map +1 -1
- package/package.json +6 -7
- package/src/constants.ts +1 -1
- package/src/index.ts +1 -2
- package/src/listener.ts +8 -9
- package/src/socket-to-conn.ts +77 -81
- package/src/tcp.ts +21 -35
package/src/socket-to-conn.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { InvalidParametersError, TimeoutError } from '@libp2p/interface'
|
|
1
|
+
import { AbortError, InvalidParametersError, TimeoutError } from '@libp2p/interface'
|
|
2
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
3
|
import { duplex } from 'stream-to-it'
|
|
6
4
|
import { CLOSE_TIMEOUT, SOCKET_TIMEOUT } from './constants.js'
|
|
7
5
|
import { multiaddrToNetConfig } from './utils.js'
|
|
8
6
|
import type { ComponentLogger, MultiaddrConnection, CounterGroup } from '@libp2p/interface'
|
|
9
7
|
import type { AbortOptions, Multiaddr } from '@multiformats/multiaddr'
|
|
10
8
|
import type { Socket } from 'net'
|
|
11
|
-
import type { DeferredPromise } from 'p-defer'
|
|
12
9
|
|
|
13
10
|
interface ToConnectionOptions {
|
|
14
11
|
listeningAddr?: Multiaddr
|
|
@@ -19,7 +16,6 @@ interface ToConnectionOptions {
|
|
|
19
16
|
metrics?: CounterGroup
|
|
20
17
|
metricPrefix?: string
|
|
21
18
|
logger: ComponentLogger
|
|
22
|
-
direction: 'inbound' | 'outbound'
|
|
23
19
|
}
|
|
24
20
|
|
|
25
21
|
/**
|
|
@@ -27,15 +23,12 @@ interface ToConnectionOptions {
|
|
|
27
23
|
* https://github.com/libp2p/interface-transport#multiaddrconnection
|
|
28
24
|
*/
|
|
29
25
|
export const toMultiaddrConnection = (socket: Socket, options: ToConnectionOptions): MultiaddrConnection => {
|
|
30
|
-
let closePromise:
|
|
26
|
+
let closePromise: Promise<void> | null = null
|
|
31
27
|
const log = options.logger.forComponent('libp2p:tcp:socket')
|
|
32
|
-
const direction = options.direction
|
|
33
28
|
const metrics = options.metrics
|
|
34
29
|
const metricPrefix = options.metricPrefix ?? ''
|
|
35
30
|
const inactivityTimeout = options.socketInactivityTimeout ?? SOCKET_TIMEOUT
|
|
36
31
|
const closeTimeout = options.socketCloseTimeout ?? CLOSE_TIMEOUT
|
|
37
|
-
let timedout = false
|
|
38
|
-
let errored = false
|
|
39
32
|
|
|
40
33
|
// Check if we are connected on a unix path
|
|
41
34
|
if (options.listeningAddr?.getPath() != null) {
|
|
@@ -46,19 +39,6 @@ export const toMultiaddrConnection = (socket: Socket, options: ToConnectionOptio
|
|
|
46
39
|
options.localAddr = options.remoteAddr
|
|
47
40
|
}
|
|
48
41
|
|
|
49
|
-
// handle socket errors
|
|
50
|
-
socket.on('error', err => {
|
|
51
|
-
errored = true
|
|
52
|
-
|
|
53
|
-
if (!timedout) {
|
|
54
|
-
log.error('%s socket error - %e', direction, err)
|
|
55
|
-
metrics?.increment({ [`${metricPrefix}error`]: true })
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
socket.destroy()
|
|
59
|
-
maConn.timeline.close = Date.now()
|
|
60
|
-
})
|
|
61
|
-
|
|
62
42
|
let remoteAddr: Multiaddr
|
|
63
43
|
|
|
64
44
|
if (options.remoteAddr != null) {
|
|
@@ -79,37 +59,37 @@ export const toMultiaddrConnection = (socket: Socket, options: ToConnectionOptio
|
|
|
79
59
|
|
|
80
60
|
// by default there is no timeout
|
|
81
61
|
// https://nodejs.org/dist/latest-v16.x/docs/api/net.html#socketsettimeouttimeout-callback
|
|
82
|
-
socket.setTimeout(inactivityTimeout)
|
|
83
|
-
|
|
84
|
-
socket.once('timeout', () => {
|
|
85
|
-
timedout = true
|
|
86
|
-
log('%s %s socket read timeout', direction, lOptsStr)
|
|
62
|
+
socket.setTimeout(inactivityTimeout, () => {
|
|
63
|
+
log('%s socket read timeout', lOptsStr)
|
|
87
64
|
metrics?.increment({ [`${metricPrefix}timeout`]: true })
|
|
88
65
|
|
|
66
|
+
// only destroy with an error if the remote has not sent the FIN message
|
|
67
|
+
let err: Error | undefined
|
|
68
|
+
if (socket.readable) {
|
|
69
|
+
err = new TimeoutError('Socket read timeout')
|
|
70
|
+
}
|
|
71
|
+
|
|
89
72
|
// if the socket times out due to inactivity we must manually close the connection
|
|
90
73
|
// https://nodejs.org/dist/latest-v16.x/docs/api/net.html#event-timeout
|
|
91
|
-
socket.destroy(
|
|
92
|
-
maConn.timeline.close = Date.now()
|
|
74
|
+
socket.destroy(err)
|
|
93
75
|
})
|
|
94
76
|
|
|
95
77
|
socket.once('close', () => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
log('%s %s socket close', direction, lOptsStr)
|
|
99
|
-
metrics?.increment({ [`${metricPrefix}close`]: true })
|
|
100
|
-
}
|
|
78
|
+
log('%s socket close', lOptsStr)
|
|
79
|
+
metrics?.increment({ [`${metricPrefix}close`]: true })
|
|
101
80
|
|
|
102
81
|
// In instances where `close` was not explicitly called,
|
|
103
82
|
// such as an iterable stream ending, ensure we have set the close
|
|
104
83
|
// timeline
|
|
105
|
-
|
|
106
|
-
|
|
84
|
+
if (maConn.timeline.close == null) {
|
|
85
|
+
maConn.timeline.close = Date.now()
|
|
86
|
+
}
|
|
107
87
|
})
|
|
108
88
|
|
|
109
89
|
socket.once('end', () => {
|
|
110
90
|
// the remote sent a FIN packet which means no more data will be sent
|
|
111
91
|
// https://nodejs.org/dist/latest-v16.x/docs/api/net.html#event-end
|
|
112
|
-
log('%s
|
|
92
|
+
log('%s socket end', lOptsStr)
|
|
113
93
|
metrics?.increment({ [`${metricPrefix}end`]: true })
|
|
114
94
|
})
|
|
115
95
|
|
|
@@ -131,7 +111,7 @@ export const toMultiaddrConnection = (socket: Socket, options: ToConnectionOptio
|
|
|
131
111
|
// If the source errored the socket will already have been destroyed by
|
|
132
112
|
// duplex(). If the socket errored it will already be
|
|
133
113
|
// destroyed. There's nothing to do here except log the error & return.
|
|
134
|
-
log.error('%s
|
|
114
|
+
log.error('%s error in sink', lOptsStr, err)
|
|
135
115
|
}
|
|
136
116
|
}
|
|
137
117
|
|
|
@@ -148,66 +128,96 @@ export const toMultiaddrConnection = (socket: Socket, options: ToConnectionOptio
|
|
|
148
128
|
|
|
149
129
|
async close (options: AbortOptions = {}) {
|
|
150
130
|
if (socket.closed) {
|
|
151
|
-
log('
|
|
131
|
+
log('The %s socket is already closed', lOptsStr)
|
|
152
132
|
return
|
|
153
133
|
}
|
|
154
134
|
|
|
155
135
|
if (socket.destroyed) {
|
|
156
|
-
log('
|
|
136
|
+
log('The %s socket is already destroyed', lOptsStr)
|
|
157
137
|
return
|
|
158
138
|
}
|
|
159
139
|
|
|
160
|
-
|
|
161
|
-
|
|
140
|
+
const abortSignalListener = (): void => {
|
|
141
|
+
socket.destroy(new AbortError('Destroying socket after timeout'))
|
|
162
142
|
}
|
|
163
143
|
|
|
164
144
|
try {
|
|
165
|
-
closePromise
|
|
145
|
+
if (closePromise != null) {
|
|
146
|
+
log('The %s socket is already closing', lOptsStr)
|
|
147
|
+
await closePromise
|
|
148
|
+
return
|
|
149
|
+
}
|
|
166
150
|
|
|
167
|
-
|
|
168
|
-
|
|
151
|
+
if (options.signal == null) {
|
|
152
|
+
const signal = AbortSignal.timeout(closeTimeout)
|
|
169
153
|
|
|
170
|
-
|
|
171
|
-
|
|
154
|
+
options = {
|
|
155
|
+
...options,
|
|
156
|
+
signal
|
|
157
|
+
}
|
|
158
|
+
}
|
|
172
159
|
|
|
173
|
-
|
|
174
|
-
const signal = options.signal ?? AbortSignal.timeout(closeTimeout)
|
|
160
|
+
options.signal?.addEventListener('abort', abortSignalListener)
|
|
175
161
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
162
|
+
log('%s closing socket', lOptsStr)
|
|
163
|
+
closePromise = new Promise<void>((resolve, reject) => {
|
|
164
|
+
socket.once('close', () => {
|
|
165
|
+
// socket completely closed
|
|
166
|
+
log('%s socket closed', lOptsStr)
|
|
167
|
+
resolve()
|
|
181
168
|
})
|
|
182
|
-
|
|
183
|
-
|
|
169
|
+
socket.once('error', (err: Error) => {
|
|
170
|
+
log('%s socket error', lOptsStr, err)
|
|
184
171
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
172
|
+
if (!socket.destroyed) {
|
|
173
|
+
reject(err)
|
|
174
|
+
}
|
|
175
|
+
// if socket is destroyed, 'closed' event will be emitted later to resolve the promise
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// shorten inactivity timeout
|
|
179
|
+
socket.setTimeout(closeTimeout)
|
|
180
|
+
|
|
181
|
+
// close writable end of the socket
|
|
182
|
+
socket.end()
|
|
183
|
+
|
|
184
|
+
if (socket.writableLength > 0) {
|
|
185
|
+
// there are outgoing bytes waiting to be sent
|
|
186
|
+
socket.once('drain', () => {
|
|
187
|
+
log('%s socket drained', lOptsStr)
|
|
189
188
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
// all bytes have been sent we can destroy the socket (maybe) before the timeout
|
|
190
|
+
socket.destroy()
|
|
191
|
+
})
|
|
192
|
+
} else {
|
|
193
|
+
// nothing to send, destroy immediately, no need for the timeout
|
|
194
|
+
socket.destroy()
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
await closePromise
|
|
193
199
|
} catch (err: any) {
|
|
194
200
|
this.abort(err)
|
|
195
201
|
} finally {
|
|
196
|
-
|
|
202
|
+
options.signal?.removeEventListener('abort', abortSignalListener)
|
|
197
203
|
}
|
|
198
204
|
},
|
|
199
205
|
|
|
200
206
|
abort: (err: Error) => {
|
|
201
|
-
log('%s
|
|
207
|
+
log('%s socket abort due to error', lOptsStr, err)
|
|
202
208
|
|
|
203
209
|
// the abortSignalListener may already destroyed the socket with an error
|
|
204
|
-
socket.
|
|
210
|
+
if (!socket.destroyed) {
|
|
211
|
+
socket.destroy(err)
|
|
212
|
+
}
|
|
205
213
|
|
|
206
214
|
// closing a socket is always asynchronous (must wait for "close" event)
|
|
207
215
|
// but the tests expect this to be a synchronous operation so we have to
|
|
208
216
|
// set the close time here. the tests should be refactored to reflect
|
|
209
217
|
// reality.
|
|
210
|
-
maConn.timeline.close
|
|
218
|
+
if (maConn.timeline.close == null) {
|
|
219
|
+
maConn.timeline.close = Date.now()
|
|
220
|
+
}
|
|
211
221
|
},
|
|
212
222
|
|
|
213
223
|
log
|
|
@@ -215,17 +225,3 @@ export const toMultiaddrConnection = (socket: Socket, options: ToConnectionOptio
|
|
|
215
225
|
|
|
216
226
|
return maConn
|
|
217
227
|
}
|
|
218
|
-
|
|
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
|
|
231
|
-
}
|
package/src/tcp.ts
CHANGED
|
@@ -36,7 +36,7 @@ import { TCPListener } from './listener.js'
|
|
|
36
36
|
import { toMultiaddrConnection } from './socket-to-conn.js'
|
|
37
37
|
import { multiaddrToNetConfig } from './utils.js'
|
|
38
38
|
import type { TCPComponents, TCPCreateListenerOptions, TCPDialEvents, TCPDialOptions, TCPMetrics, TCPOptions } from './index.js'
|
|
39
|
-
import type { Logger, Connection, Transport, Listener
|
|
39
|
+
import type { Logger, Connection, Transport, Listener } from '@libp2p/interface'
|
|
40
40
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
41
41
|
import type { Socket, IpcSocketConnectOpts, TcpSocketConnectOpts } from 'net'
|
|
42
42
|
|
|
@@ -53,11 +53,7 @@ export class TCP implements Transport<TCPDialEvents> {
|
|
|
53
53
|
|
|
54
54
|
if (components.metrics != null) {
|
|
55
55
|
this.metrics = {
|
|
56
|
-
|
|
57
|
-
label: 'event',
|
|
58
|
-
help: 'Total count of TCP dialer events by type'
|
|
59
|
-
}),
|
|
60
|
-
errors: components.metrics.registerCounterGroup('libp2p_tcp_dialer_errors_total', {
|
|
56
|
+
dialerEvents: components.metrics.registerCounterGroup('libp2p_tcp_dialer_events_total', {
|
|
61
57
|
label: 'event',
|
|
62
58
|
help: 'Total count of TCP dialer events by type'
|
|
63
59
|
})
|
|
@@ -80,28 +76,23 @@ export class TCP implements Transport<TCPDialEvents> {
|
|
|
80
76
|
// options.signal destroys the socket before 'connect' event
|
|
81
77
|
const socket = await this._connect(ma, options)
|
|
82
78
|
|
|
83
|
-
|
|
79
|
+
// Avoid uncaught errors caused by unstable connections
|
|
80
|
+
socket.on('error', err => {
|
|
81
|
+
this.log('socket error', err)
|
|
82
|
+
})
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
direction: 'outbound'
|
|
93
|
-
})
|
|
94
|
-
} catch (err: any) {
|
|
95
|
-
this.metrics?.errors.increment({ outbound_to_connection: true })
|
|
96
|
-
socket.destroy(err)
|
|
97
|
-
throw err
|
|
98
|
-
}
|
|
84
|
+
const maConn = toMultiaddrConnection(socket, {
|
|
85
|
+
remoteAddr: ma,
|
|
86
|
+
socketInactivityTimeout: this.opts.outboundSocketInactivityTimeout,
|
|
87
|
+
socketCloseTimeout: this.opts.socketCloseTimeout,
|
|
88
|
+
metrics: this.metrics?.dialerEvents,
|
|
89
|
+
logger: this.components.logger
|
|
90
|
+
})
|
|
99
91
|
|
|
100
92
|
try {
|
|
101
93
|
this.log('new outbound connection %s', maConn.remoteAddr)
|
|
102
94
|
return await options.upgrader.upgradeOutbound(maConn, options)
|
|
103
95
|
} catch (err: any) {
|
|
104
|
-
this.metrics?.errors.increment({ outbound_upgrade: true })
|
|
105
96
|
this.log.error('error upgrading outbound connection', err)
|
|
106
97
|
maConn.abort(err)
|
|
107
98
|
throw err
|
|
@@ -112,8 +103,6 @@ export class TCP implements Transport<TCPDialEvents> {
|
|
|
112
103
|
options.signal?.throwIfAborted()
|
|
113
104
|
options.onProgress?.(new CustomProgressEvent('tcp:open-connection'))
|
|
114
105
|
|
|
115
|
-
let rawSocket: Socket
|
|
116
|
-
|
|
117
106
|
return new Promise<Socket>((resolve, reject) => {
|
|
118
107
|
const start = Date.now()
|
|
119
108
|
const cOpts = multiaddrToNetConfig(ma, {
|
|
@@ -122,34 +111,35 @@ export class TCP implements Transport<TCPDialEvents> {
|
|
|
122
111
|
}) as (IpcSocketConnectOpts & TcpSocketConnectOpts)
|
|
123
112
|
|
|
124
113
|
this.log('dialing %a', ma)
|
|
125
|
-
rawSocket = net.connect(cOpts)
|
|
114
|
+
const rawSocket = net.connect(cOpts)
|
|
126
115
|
|
|
127
116
|
const onError = (err: Error): void => {
|
|
128
|
-
this.log.error('dial to %a errored - %e', ma, err)
|
|
129
117
|
const cOptsStr = cOpts.path ?? `${cOpts.host ?? ''}:${cOpts.port}`
|
|
130
118
|
err.message = `connection error ${cOptsStr}: ${err.message}`
|
|
131
|
-
this.metrics?.
|
|
119
|
+
this.metrics?.dialerEvents.increment({ error: true })
|
|
120
|
+
|
|
132
121
|
done(err)
|
|
133
122
|
}
|
|
134
123
|
|
|
135
124
|
const onTimeout = (): void => {
|
|
136
125
|
this.log('connection timeout %a', ma)
|
|
137
|
-
this.metrics?.
|
|
126
|
+
this.metrics?.dialerEvents.increment({ timeout: true })
|
|
138
127
|
|
|
139
|
-
const err = new TimeoutError(`
|
|
128
|
+
const err = new TimeoutError(`connection timeout after ${Date.now() - start}ms`)
|
|
140
129
|
// Note: this will result in onError() being called
|
|
141
130
|
rawSocket.emit('error', err)
|
|
142
131
|
}
|
|
143
132
|
|
|
144
133
|
const onConnect = (): void => {
|
|
145
134
|
this.log('connection opened %a', ma)
|
|
146
|
-
this.metrics?.
|
|
135
|
+
this.metrics?.dialerEvents.increment({ connect: true })
|
|
147
136
|
done()
|
|
148
137
|
}
|
|
149
138
|
|
|
150
139
|
const onAbort = (): void => {
|
|
151
140
|
this.log('connection aborted %a', ma)
|
|
152
|
-
this.metrics?.
|
|
141
|
+
this.metrics?.dialerEvents.increment({ abort: true })
|
|
142
|
+
rawSocket.destroy()
|
|
153
143
|
done(new AbortError())
|
|
154
144
|
}
|
|
155
145
|
|
|
@@ -177,10 +167,6 @@ export class TCP implements Transport<TCPDialEvents> {
|
|
|
177
167
|
options.signal.addEventListener('abort', onAbort)
|
|
178
168
|
}
|
|
179
169
|
})
|
|
180
|
-
.catch(err => {
|
|
181
|
-
rawSocket?.destroy()
|
|
182
|
-
throw err
|
|
183
|
-
})
|
|
184
170
|
}
|
|
185
171
|
|
|
186
172
|
/**
|