@libp2p/websockets 9.2.18 → 9.2.19-8484de8a2
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 +19 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +24 -27
- package/dist/src/index.js.map +1 -1
- package/dist/src/listener.d.ts +5 -1
- package/dist/src/listener.d.ts.map +1 -1
- package/dist/src/listener.js +23 -26
- package/dist/src/listener.js.map +1 -1
- package/dist/src/utils.d.ts +7 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +30 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/src/websocket-to-conn.d.ts +9 -0
- package/dist/src/websocket-to-conn.d.ts.map +1 -0
- package/dist/src/websocket-to-conn.js +83 -0
- package/dist/src/websocket-to-conn.js.map +1 -0
- package/package.json +14 -13
- package/src/index.ts +44 -30
- package/src/listener.ts +25 -28
- package/src/utils.ts +36 -0
- package/src/websocket-to-conn.ts +108 -0
- package/dist/src/socket-to-conn.d.ts +0 -11
- package/dist/src/socket-to-conn.d.ts.map +0 -1
- package/dist/src/socket-to-conn.js +0 -83
- package/dist/src/socket-to-conn.js.map +0 -1
- package/dist/typedoc-urls.json +0 -20
- package/src/socket-to-conn.ts +0 -109
package/src/index.ts
CHANGED
|
@@ -25,17 +25,14 @@
|
|
|
25
25
|
|
|
26
26
|
import { transportSymbol, serviceCapabilities, ConnectionFailedError } from '@libp2p/interface'
|
|
27
27
|
import { multiaddrToUri as toUri } from '@multiformats/multiaddr-to-uri'
|
|
28
|
-
import {
|
|
29
|
-
import pDefer from 'p-defer'
|
|
28
|
+
import { pEvent } from 'p-event'
|
|
30
29
|
import { CustomProgressEvent } from 'progress-events'
|
|
31
|
-
import { raceSignal } from 'race-signal'
|
|
32
30
|
import * as filters from './filters.js'
|
|
33
31
|
import { createListener } from './listener.js'
|
|
34
|
-
import {
|
|
32
|
+
import { webSocketToMaConn } from './websocket-to-conn.js'
|
|
35
33
|
import type { Transport, MultiaddrFilter, CreateListenerOptions, DialTransportOptions, Listener, AbortOptions, ComponentLogger, Logger, Connection, OutboundConnectionUpgradeEvents, Metrics, CounterGroup, Libp2pEvents } from '@libp2p/interface'
|
|
36
34
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
37
35
|
import type { WebSocketOptions } from 'it-ws/client'
|
|
38
|
-
import type { DuplexWebSocket } from 'it-ws/duplex'
|
|
39
36
|
import type { TypedEventTarget } from 'main-event'
|
|
40
37
|
import type http from 'node:http'
|
|
41
38
|
import type https from 'node:https'
|
|
@@ -50,6 +47,8 @@ export interface WebSocketsInit extends AbortOptions, WebSocketOptions {
|
|
|
50
47
|
|
|
51
48
|
/**
|
|
52
49
|
* Options used to create WebSockets
|
|
50
|
+
*
|
|
51
|
+
* @deprecated This option will be removed in a future release
|
|
53
52
|
*/
|
|
54
53
|
websocket?: ClientOptions
|
|
55
54
|
|
|
@@ -70,6 +69,25 @@ export interface WebSocketsInit extends AbortOptions, WebSocketOptions {
|
|
|
70
69
|
* @deprecated Use the `connectionManager.inboundUpgradeTimeout` libp2p config key instead
|
|
71
70
|
*/
|
|
72
71
|
inboundConnectionUpgradeTimeout?: number
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* How large the outgoing [bufferedAmount](https://websockets.spec.whatwg.org/#dom-websocket-bufferedamount)
|
|
75
|
+
* property of incoming and outgoing websockets is allowed to get in bytes.
|
|
76
|
+
*
|
|
77
|
+
* If this limit is exceeded, backpressure will be applied to the writer.
|
|
78
|
+
*
|
|
79
|
+
* @default 4_194_304
|
|
80
|
+
*/
|
|
81
|
+
maxBufferedAmount?: number
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* If the [bufferedAmount](https://websockets.spec.whatwg.org/#dom-websocket-bufferedamount)
|
|
85
|
+
* property of a WebSocket exceeds `maxBufferedAmount`, poll the field every
|
|
86
|
+
* this number of ms to see if the socket can accept new data.
|
|
87
|
+
*
|
|
88
|
+
* @default 500
|
|
89
|
+
*/
|
|
90
|
+
bufferedAmountPollInterval?: number
|
|
73
91
|
}
|
|
74
92
|
|
|
75
93
|
export interface WebSocketsComponents {
|
|
@@ -121,10 +139,14 @@ class WebSockets implements Transport<WebSocketsDialEvents> {
|
|
|
121
139
|
this.log('dialing %s', ma)
|
|
122
140
|
options = options ?? {}
|
|
123
141
|
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
metrics: this.metrics?.dialerEvents
|
|
142
|
+
const maConn = webSocketToMaConn({
|
|
143
|
+
websocket: await this._connect(ma, options),
|
|
144
|
+
remoteAddr: ma,
|
|
145
|
+
metrics: this.metrics?.dialerEvents,
|
|
146
|
+
direction: 'outbound',
|
|
147
|
+
log: this.components.logger.forComponent('libp2p:websockets:connection'),
|
|
148
|
+
maxBufferedAmount: this.init.maxBufferedAmount,
|
|
149
|
+
bufferedAmountPollInterval: this.init.bufferedAmountPollInterval
|
|
128
150
|
})
|
|
129
151
|
this.log('new outbound connection %s', maConn.remoteAddr)
|
|
130
152
|
|
|
@@ -133,43 +155,35 @@ class WebSockets implements Transport<WebSocketsDialEvents> {
|
|
|
133
155
|
return conn
|
|
134
156
|
}
|
|
135
157
|
|
|
136
|
-
async _connect (ma: Multiaddr, options: DialTransportOptions<WebSocketsDialEvents>): Promise<
|
|
158
|
+
async _connect (ma: Multiaddr, options: DialTransportOptions<WebSocketsDialEvents>): Promise<WebSocket> {
|
|
137
159
|
options?.signal?.throwIfAborted()
|
|
138
160
|
|
|
139
|
-
const
|
|
140
|
-
this.log('
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const rawSocket = connect(toUri(ma), this.init)
|
|
144
|
-
rawSocket.socket.addEventListener('error', () => {
|
|
145
|
-
// the WebSocket.ErrorEvent type doesn't actually give us any useful
|
|
146
|
-
// information about what happened
|
|
147
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/error_event
|
|
148
|
-
const err = new ConnectionFailedError(`Could not connect to ${ma.toString()}`)
|
|
149
|
-
this.log.error('connection error:', err)
|
|
150
|
-
this.metrics?.dialerEvents.increment({ error: true })
|
|
151
|
-
errorPromise.reject(err)
|
|
152
|
-
})
|
|
161
|
+
const uri = toUri(ma)
|
|
162
|
+
this.log('create websocket connection to %s', uri)
|
|
163
|
+
const websocket = new WebSocket(uri)
|
|
164
|
+
websocket.binaryType = 'arraybuffer'
|
|
153
165
|
|
|
154
166
|
try {
|
|
155
167
|
options.onProgress?.(new CustomProgressEvent('websockets:open-connection'))
|
|
156
|
-
await
|
|
168
|
+
await pEvent(websocket, 'open', options)
|
|
157
169
|
} catch (err: any) {
|
|
158
170
|
if (options.signal?.aborted) {
|
|
159
171
|
this.metrics?.dialerEvents.increment({ abort: true })
|
|
172
|
+
throw new ConnectionFailedError(`Could not connect to ${uri}`)
|
|
173
|
+
} else {
|
|
174
|
+
this.metrics?.dialerEvents.increment({ error: true })
|
|
160
175
|
}
|
|
161
176
|
|
|
162
|
-
|
|
163
|
-
.
|
|
164
|
-
|
|
165
|
-
})
|
|
177
|
+
try {
|
|
178
|
+
websocket.close()
|
|
179
|
+
} catch {}
|
|
166
180
|
|
|
167
181
|
throw err
|
|
168
182
|
}
|
|
169
183
|
|
|
170
184
|
this.log('connected %s', ma)
|
|
171
185
|
this.metrics?.dialerEvents.increment({ connect: true })
|
|
172
|
-
return
|
|
186
|
+
return websocket
|
|
173
187
|
}
|
|
174
188
|
|
|
175
189
|
/**
|
package/src/listener.ts
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import http from 'node:http'
|
|
2
2
|
import https from 'node:https'
|
|
3
3
|
import net from 'node:net'
|
|
4
|
-
import { getThinWaistAddresses } from '@libp2p/utils
|
|
5
|
-
import { ipPortToMultiaddr as toMultiaddr } from '@libp2p/utils/ip-port-to-multiaddr'
|
|
4
|
+
import { getNetConfig, getThinWaistAddresses, ipPortToMultiaddr as toMultiaddr } from '@libp2p/utils'
|
|
6
5
|
import { multiaddr } from '@multiformats/multiaddr'
|
|
7
6
|
import { WebSockets, WebSocketsSecure } from '@multiformats/multiaddr-matcher'
|
|
8
|
-
import duplex from 'it-ws/duplex'
|
|
9
7
|
import { TypedEventEmitter, setMaxListeners } from 'main-event'
|
|
10
8
|
import { pEvent } from 'p-event'
|
|
11
9
|
import * as ws from 'ws'
|
|
12
|
-
import {
|
|
10
|
+
import { toWebSocket } from './utils.ts'
|
|
11
|
+
import { webSocketToMaConn } from './websocket-to-conn.js'
|
|
13
12
|
import type { ComponentLogger, Logger, Listener, ListenerEvents, CreateListenerOptions, CounterGroup, MetricGroup, Metrics, TLSCertificate, Libp2pEvents, Upgrader, MultiaddrConnection } from '@libp2p/interface'
|
|
14
13
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
15
|
-
import type { DuplexWebSocket } from 'it-ws/duplex'
|
|
16
14
|
import type { TypedEventTarget } from 'main-event'
|
|
17
15
|
import type { EventEmitter } from 'node:events'
|
|
18
16
|
import type { Server } from 'node:http'
|
|
@@ -31,6 +29,8 @@ export interface WebSocketListenerInit extends CreateListenerOptions {
|
|
|
31
29
|
key?: string
|
|
32
30
|
http?: http.ServerOptions
|
|
33
31
|
https?: http.ServerOptions
|
|
32
|
+
maxBufferedAmount?: number
|
|
33
|
+
bufferedAmountPollInterval?: number
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export interface WebSocketListenerMetrics {
|
|
@@ -40,8 +40,8 @@ export interface WebSocketListenerMetrics {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export class WebSocketListener extends TypedEventEmitter<ListenerEvents> implements Listener {
|
|
43
|
+
private components: WebSocketListenerComponents
|
|
43
44
|
private readonly log: Logger
|
|
44
|
-
private readonly logger: ComponentLogger
|
|
45
45
|
private readonly server: net.Server
|
|
46
46
|
private readonly wsServer: ws.WebSocketServer
|
|
47
47
|
private readonly metrics: WebSocketListenerMetrics
|
|
@@ -54,15 +54,19 @@ export class WebSocketListener extends TypedEventEmitter<ListenerEvents> impleme
|
|
|
54
54
|
private https?: https.Server
|
|
55
55
|
private addr?: string
|
|
56
56
|
private listeningMultiaddr?: Multiaddr
|
|
57
|
+
private maxBufferedAmount?: number
|
|
58
|
+
private bufferedAmountPollInterval?: number
|
|
57
59
|
|
|
58
60
|
constructor (components: WebSocketListenerComponents, init: WebSocketListenerInit) {
|
|
59
61
|
super()
|
|
60
62
|
|
|
63
|
+
this.components = components
|
|
61
64
|
this.log = components.logger.forComponent('libp2p:websockets:listener')
|
|
62
|
-
this.logger = components.logger
|
|
63
65
|
this.upgrader = init.upgrader
|
|
64
66
|
this.httpOptions = init.http
|
|
65
67
|
this.httpsOptions = init.https ?? init.http
|
|
68
|
+
this.maxBufferedAmount = init.maxBufferedAmount
|
|
69
|
+
this.bufferedAmountPollInterval = init.bufferedAmountPollInterval
|
|
66
70
|
this.sockets = new Set()
|
|
67
71
|
this.shutdownController = new AbortController()
|
|
68
72
|
setMaxListeners(Infinity, this.shutdownController.signal)
|
|
@@ -171,6 +175,7 @@ export class WebSocketListener extends TypedEventEmitter<ListenerEvents> impleme
|
|
|
171
175
|
|
|
172
176
|
onWsServerConnection (socket: ws.WebSocket, req: http.IncomingMessage): void {
|
|
173
177
|
let addr: string | ws.AddressInfo | null
|
|
178
|
+
socket.binaryType = 'arraybuffer'
|
|
174
179
|
|
|
175
180
|
try {
|
|
176
181
|
addr = this.server.address()
|
|
@@ -189,22 +194,18 @@ export class WebSocketListener extends TypedEventEmitter<ListenerEvents> impleme
|
|
|
189
194
|
return
|
|
190
195
|
}
|
|
191
196
|
|
|
192
|
-
const stream: DuplexWebSocket = {
|
|
193
|
-
...duplex(socket, {
|
|
194
|
-
remoteAddress: req.socket.remoteAddress ?? '0.0.0.0',
|
|
195
|
-
remotePort: req.socket.remotePort ?? 0
|
|
196
|
-
}),
|
|
197
|
-
localAddress: addr.address,
|
|
198
|
-
localPort: addr.port
|
|
199
|
-
}
|
|
200
|
-
|
|
201
197
|
let maConn: MultiaddrConnection
|
|
202
198
|
|
|
203
199
|
try {
|
|
204
|
-
maConn =
|
|
205
|
-
|
|
200
|
+
maConn = webSocketToMaConn({
|
|
201
|
+
websocket: toWebSocket(socket),
|
|
202
|
+
remoteAddr: toMultiaddr(req.socket.remoteAddress ?? '0.0.0.0', req.socket.remotePort ?? 0).encapsulate('/ws'),
|
|
206
203
|
metrics: this.metrics?.events,
|
|
207
|
-
metricPrefix: `${this.addr}
|
|
204
|
+
metricPrefix: `${this.addr} `,
|
|
205
|
+
direction: 'inbound',
|
|
206
|
+
log: this.components.logger.forComponent('libp2p:websockets:connection'),
|
|
207
|
+
maxBufferedAmount: this.maxBufferedAmount,
|
|
208
|
+
bufferedAmountPollInterval: this.bufferedAmountPollInterval
|
|
208
209
|
})
|
|
209
210
|
} catch (err: any) {
|
|
210
211
|
this.log.error('inbound connection failed', err)
|
|
@@ -222,11 +223,7 @@ export class WebSocketListener extends TypedEventEmitter<ListenerEvents> impleme
|
|
|
222
223
|
this.log.error('inbound connection failed to upgrade - %e', err)
|
|
223
224
|
this.metrics.errors?.increment({ [`${this.addr} inbound_upgrade`]: true })
|
|
224
225
|
|
|
225
|
-
|
|
226
|
-
.catch(err => {
|
|
227
|
-
this.log.error('inbound connection failed to close after upgrade failed', err)
|
|
228
|
-
this.metrics.errors?.increment({ [`${this.addr} inbound_closing_failed`]: true })
|
|
229
|
-
})
|
|
226
|
+
maConn.close()
|
|
230
227
|
})
|
|
231
228
|
}
|
|
232
229
|
|
|
@@ -249,12 +246,12 @@ export class WebSocketListener extends TypedEventEmitter<ListenerEvents> impleme
|
|
|
249
246
|
this.https.addListener('tlsClientError', this.onTLSClientError.bind(this))
|
|
250
247
|
}
|
|
251
248
|
|
|
252
|
-
const
|
|
253
|
-
this.addr = `${
|
|
249
|
+
const config = getNetConfig(ma)
|
|
250
|
+
this.addr = `${config.host}:${config.port}`
|
|
254
251
|
|
|
255
252
|
this.server.listen({
|
|
256
|
-
...
|
|
257
|
-
ipv6Only:
|
|
253
|
+
...config,
|
|
254
|
+
ipv6Only: config.type === 'ip6'
|
|
258
255
|
})
|
|
259
256
|
|
|
260
257
|
await new Promise<void>((resolve, reject) => {
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { WebSocket as WSSWebSocket } from 'ws'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adds properties/methods to a `WebSocket` instance from the `ws` module to be
|
|
5
|
+
* compatible with the `globalThis.WebSocket` API
|
|
6
|
+
*/
|
|
7
|
+
export function toWebSocket (ws: WSSWebSocket): WebSocket {
|
|
8
|
+
Object.defineProperty(ws, 'url', {
|
|
9
|
+
value: '',
|
|
10
|
+
writable: false
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
// @ts-expect-error not a WS/WebSocket method
|
|
14
|
+
ws.dispatchEvent = (evt: Event) => {
|
|
15
|
+
if (evt.type === 'close') {
|
|
16
|
+
ws.emit('close')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (evt.type === 'open') {
|
|
20
|
+
ws.emit('open')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (evt.type === 'message') {
|
|
24
|
+
const m = evt as MessageEvent
|
|
25
|
+
ws.emit('data', m.data)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (evt.type === 'error') {
|
|
29
|
+
ws.emit('error', new Error('An error occurred'))
|
|
30
|
+
}
|
|
31
|
+
ws.emit(evt.type, evt)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// @ts-expect-error ws is now WebSocket
|
|
35
|
+
return ws
|
|
36
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { AbstractMultiaddrConnection, repeatingTask } from '@libp2p/utils'
|
|
2
|
+
import { Uint8ArrayList } from 'uint8arraylist'
|
|
3
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
4
|
+
import type { AbortOptions, MultiaddrConnection } from '@libp2p/interface'
|
|
5
|
+
import type { AbstractMultiaddrConnectionInit, RepeatingTask, SendResult } from '@libp2p/utils'
|
|
6
|
+
|
|
7
|
+
const DEFAULT_MAX_BUFFERED_AMOUNT = 1024 * 1024 * 4
|
|
8
|
+
const DEFAULT_BUFFERED_AMOUNT_POLL_INTERVAL = 10
|
|
9
|
+
|
|
10
|
+
export interface WebSocketMultiaddrConnectionInit extends Omit<AbstractMultiaddrConnectionInit, 'name'> {
|
|
11
|
+
websocket: WebSocket
|
|
12
|
+
maxBufferedAmount?: number
|
|
13
|
+
bufferedAmountPollInterval?: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class WebSocketMultiaddrConnection extends AbstractMultiaddrConnection {
|
|
17
|
+
private websocket: WebSocket
|
|
18
|
+
private maxBufferedAmount: number
|
|
19
|
+
private checkBufferedAmountTask: RepeatingTask
|
|
20
|
+
|
|
21
|
+
constructor (init: WebSocketMultiaddrConnectionInit) {
|
|
22
|
+
super(init)
|
|
23
|
+
|
|
24
|
+
this.websocket = init.websocket
|
|
25
|
+
this.maxBufferedAmount = init.maxBufferedAmount ?? DEFAULT_MAX_BUFFERED_AMOUNT
|
|
26
|
+
this.checkBufferedAmountTask = repeatingTask(this.checkBufferedAmount.bind(this), init.bufferedAmountPollInterval ?? DEFAULT_BUFFERED_AMOUNT_POLL_INTERVAL)
|
|
27
|
+
|
|
28
|
+
this.websocket.addEventListener('close', (evt) => {
|
|
29
|
+
this.log('closed - code %d, reason "%s", wasClean %s', evt.code, evt.reason, evt.wasClean)
|
|
30
|
+
this.checkBufferedAmountTask.stop()
|
|
31
|
+
|
|
32
|
+
if (!evt.wasClean) {
|
|
33
|
+
this.onRemoteReset()
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.onTransportClosed()
|
|
38
|
+
}, { once: true })
|
|
39
|
+
|
|
40
|
+
this.websocket.addEventListener('message', (evt) => {
|
|
41
|
+
try {
|
|
42
|
+
let buf: Uint8Array
|
|
43
|
+
|
|
44
|
+
if (typeof evt.data === 'string') {
|
|
45
|
+
buf = uint8ArrayFromString(evt.data)
|
|
46
|
+
} else if (evt.data instanceof ArrayBuffer) {
|
|
47
|
+
buf = new Uint8Array(evt.data, 0, evt.data.byteLength)
|
|
48
|
+
} else {
|
|
49
|
+
this.abort(new Error('Incorrect binary type'))
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.onData(buf)
|
|
54
|
+
} catch (err: any) {
|
|
55
|
+
this.log.error('error receiving data - %e', err)
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
sendData (data: Uint8ArrayList): SendResult {
|
|
61
|
+
for (const buf of data) {
|
|
62
|
+
this.websocket.send(buf)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const canSendMore = this.websocket.bufferedAmount < this.maxBufferedAmount
|
|
66
|
+
|
|
67
|
+
if (!canSendMore) {
|
|
68
|
+
this.checkBufferedAmountTask.start()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
sentBytes: data.byteLength,
|
|
73
|
+
canSendMore
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
sendReset (): void {
|
|
78
|
+
this.websocket.close(1006) // abnormal closure
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async sendClose (options?: AbortOptions): Promise<void> {
|
|
82
|
+
this.websocket.close()
|
|
83
|
+
options?.signal?.throwIfAborted()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
sendPause (): void {
|
|
87
|
+
// read backpressure is not supported
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
sendResume (): void {
|
|
91
|
+
// read backpressure is not supported
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private checkBufferedAmount (): void {
|
|
95
|
+
this.log('buffered amount now %d', this.websocket.bufferedAmount)
|
|
96
|
+
|
|
97
|
+
if (this.websocket.bufferedAmount === 0) {
|
|
98
|
+
this.checkBufferedAmountTask.stop()
|
|
99
|
+
this.safeDispatchEvent('drain')
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Convert a stream into a MultiaddrConnection
|
|
105
|
+
// https://github.com/libp2p/interface-transport#multiaddrconnection
|
|
106
|
+
export function webSocketToMaConn (init: WebSocketMultiaddrConnectionInit): MultiaddrConnection {
|
|
107
|
+
return new WebSocketMultiaddrConnection(init)
|
|
108
|
+
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { ComponentLogger, CounterGroup, MultiaddrConnection } from '@libp2p/interface';
|
|
2
|
-
import type { Multiaddr } from '@multiformats/multiaddr';
|
|
3
|
-
import type { DuplexWebSocket } from 'it-ws/duplex';
|
|
4
|
-
export interface SocketToConnOptions {
|
|
5
|
-
localAddr?: Multiaddr;
|
|
6
|
-
logger: ComponentLogger;
|
|
7
|
-
metrics?: CounterGroup;
|
|
8
|
-
metricPrefix?: string;
|
|
9
|
-
}
|
|
10
|
-
export declare function socketToMaConn(stream: DuplexWebSocket, remoteAddr: Multiaddr, options: SocketToConnOptions): MultiaddrConnection;
|
|
11
|
-
//# sourceMappingURL=socket-to-conn.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"socket-to-conn.d.ts","sourceRoot":"","sources":["../../src/socket-to-conn.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgB,eAAe,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACzG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAEnD,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,MAAM,EAAE,eAAe,CAAA;IACvB,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAID,wBAAgB,cAAc,CAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,GAAG,mBAAmB,CA6FjI"}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { AbortError } from '@libp2p/interface';
|
|
2
|
-
import { CLOSE_TIMEOUT } from './constants.js';
|
|
3
|
-
// Convert a stream into a MultiaddrConnection
|
|
4
|
-
// https://github.com/libp2p/interface-transport#multiaddrconnection
|
|
5
|
-
export function socketToMaConn(stream, remoteAddr, options) {
|
|
6
|
-
const log = options.logger.forComponent('libp2p:websockets:maconn');
|
|
7
|
-
const metrics = options.metrics;
|
|
8
|
-
const metricPrefix = options.metricPrefix ?? '';
|
|
9
|
-
const maConn = {
|
|
10
|
-
log,
|
|
11
|
-
async sink(source) {
|
|
12
|
-
try {
|
|
13
|
-
await stream.sink((async function* () {
|
|
14
|
-
for await (const buf of source) {
|
|
15
|
-
if (buf instanceof Uint8Array) {
|
|
16
|
-
yield buf;
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
yield buf.subarray();
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
})());
|
|
23
|
-
}
|
|
24
|
-
catch (err) {
|
|
25
|
-
if (err.type !== 'aborted') {
|
|
26
|
-
log.error(err);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
source: stream.source,
|
|
31
|
-
remoteAddr,
|
|
32
|
-
timeline: { open: Date.now() },
|
|
33
|
-
async close(options = {}) {
|
|
34
|
-
const start = Date.now();
|
|
35
|
-
if (options.signal == null) {
|
|
36
|
-
const signal = AbortSignal.timeout(CLOSE_TIMEOUT);
|
|
37
|
-
options = {
|
|
38
|
-
...options,
|
|
39
|
-
signal
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
const listener = () => {
|
|
43
|
-
const { host, port } = maConn.remoteAddr.toOptions();
|
|
44
|
-
log('timeout closing stream to %s:%s after %dms, destroying it manually', host, port, Date.now() - start);
|
|
45
|
-
this.abort(new AbortError('Socket close timeout'));
|
|
46
|
-
};
|
|
47
|
-
options.signal?.addEventListener('abort', listener);
|
|
48
|
-
try {
|
|
49
|
-
await stream.close();
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
log.error('error closing WebSocket gracefully', err);
|
|
53
|
-
this.abort(err);
|
|
54
|
-
}
|
|
55
|
-
finally {
|
|
56
|
-
options.signal?.removeEventListener('abort', listener);
|
|
57
|
-
maConn.timeline.close = Date.now();
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
abort(err) {
|
|
61
|
-
const { host, port } = maConn.remoteAddr.toOptions();
|
|
62
|
-
log('timeout closing stream to %s:%s due to error', host, port, err);
|
|
63
|
-
stream.destroy();
|
|
64
|
-
maConn.timeline.close = Date.now();
|
|
65
|
-
// ws WebSocket.terminate does not accept an Error arg to emit an 'error'
|
|
66
|
-
// event on destroy like other node streams so we can't update a metric
|
|
67
|
-
// with an event listener
|
|
68
|
-
// https://github.com/websockets/ws/issues/1752#issuecomment-622380981
|
|
69
|
-
metrics?.increment({ [`${metricPrefix}error`]: true });
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
stream.socket.addEventListener('close', () => {
|
|
73
|
-
metrics?.increment({ [`${metricPrefix}close`]: true });
|
|
74
|
-
// In instances where `close` was not explicitly called,
|
|
75
|
-
// such as an iterable stream ending, ensure we have set the close
|
|
76
|
-
// timeline
|
|
77
|
-
if (maConn.timeline.close == null) {
|
|
78
|
-
maConn.timeline.close = Date.now();
|
|
79
|
-
}
|
|
80
|
-
}, { once: true });
|
|
81
|
-
return maConn;
|
|
82
|
-
}
|
|
83
|
-
//# sourceMappingURL=socket-to-conn.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"socket-to-conn.js","sourceRoot":"","sources":["../../src/socket-to-conn.ts"],"names":[],"mappings":"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"}
|
package/dist/typedoc-urls.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"all": "https://libp2p.github.io/js-libp2p/functions/_libp2p_websockets.filters.all.html",
|
|
3
|
-
"./filters:all": "https://libp2p.github.io/js-libp2p/functions/_libp2p_websockets.filters.all.html",
|
|
4
|
-
"dnsWsOrWss": "https://libp2p.github.io/js-libp2p/functions/_libp2p_websockets.filters.dnsWsOrWss.html",
|
|
5
|
-
"./filters:dnsWsOrWss": "https://libp2p.github.io/js-libp2p/functions/_libp2p_websockets.filters.dnsWsOrWss.html",
|
|
6
|
-
"dnsWss": "https://libp2p.github.io/js-libp2p/functions/_libp2p_websockets.filters.dnsWss.html",
|
|
7
|
-
"./filters:dnsWss": "https://libp2p.github.io/js-libp2p/functions/_libp2p_websockets.filters.dnsWss.html",
|
|
8
|
-
"wss": "https://libp2p.github.io/js-libp2p/functions/_libp2p_websockets.filters.wss.html",
|
|
9
|
-
"./filters:wss": "https://libp2p.github.io/js-libp2p/functions/_libp2p_websockets.filters.wss.html",
|
|
10
|
-
"WebSocketsComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_websockets.index.WebSocketsComponents.html",
|
|
11
|
-
".:WebSocketsComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_websockets.index.WebSocketsComponents.html",
|
|
12
|
-
"WebSocketsInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_websockets.index.WebSocketsInit.html",
|
|
13
|
-
".:WebSocketsInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_websockets.index.WebSocketsInit.html",
|
|
14
|
-
"WebSocketsMetrics": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_websockets.index.WebSocketsMetrics.html",
|
|
15
|
-
".:WebSocketsMetrics": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_websockets.index.WebSocketsMetrics.html",
|
|
16
|
-
"WebSocketsDialEvents": "https://libp2p.github.io/js-libp2p/types/_libp2p_websockets.index.WebSocketsDialEvents.html",
|
|
17
|
-
".:WebSocketsDialEvents": "https://libp2p.github.io/js-libp2p/types/_libp2p_websockets.index.WebSocketsDialEvents.html",
|
|
18
|
-
"webSockets": "https://libp2p.github.io/js-libp2p/functions/_libp2p_websockets.index.webSockets.html",
|
|
19
|
-
".:webSockets": "https://libp2p.github.io/js-libp2p/functions/_libp2p_websockets.index.webSockets.html"
|
|
20
|
-
}
|
package/src/socket-to-conn.ts
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { AbortError } from '@libp2p/interface'
|
|
2
|
-
import { CLOSE_TIMEOUT } from './constants.js'
|
|
3
|
-
import type { AbortOptions, ComponentLogger, CounterGroup, MultiaddrConnection } from '@libp2p/interface'
|
|
4
|
-
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
5
|
-
import type { DuplexWebSocket } from 'it-ws/duplex'
|
|
6
|
-
|
|
7
|
-
export interface SocketToConnOptions {
|
|
8
|
-
localAddr?: Multiaddr
|
|
9
|
-
logger: ComponentLogger
|
|
10
|
-
metrics?: CounterGroup
|
|
11
|
-
metricPrefix?: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Convert a stream into a MultiaddrConnection
|
|
15
|
-
// https://github.com/libp2p/interface-transport#multiaddrconnection
|
|
16
|
-
export function socketToMaConn (stream: DuplexWebSocket, remoteAddr: Multiaddr, options: SocketToConnOptions): MultiaddrConnection {
|
|
17
|
-
const log = options.logger.forComponent('libp2p:websockets:maconn')
|
|
18
|
-
const metrics = options.metrics
|
|
19
|
-
const metricPrefix = options.metricPrefix ?? ''
|
|
20
|
-
|
|
21
|
-
const maConn: MultiaddrConnection = {
|
|
22
|
-
log,
|
|
23
|
-
|
|
24
|
-
async sink (source) {
|
|
25
|
-
try {
|
|
26
|
-
await stream.sink((async function * () {
|
|
27
|
-
for await (const buf of source) {
|
|
28
|
-
if (buf instanceof Uint8Array) {
|
|
29
|
-
yield buf
|
|
30
|
-
} else {
|
|
31
|
-
yield buf.subarray()
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
})())
|
|
35
|
-
} catch (err: any) {
|
|
36
|
-
if (err.type !== 'aborted') {
|
|
37
|
-
log.error(err)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
source: stream.source,
|
|
43
|
-
|
|
44
|
-
remoteAddr,
|
|
45
|
-
|
|
46
|
-
timeline: { open: Date.now() },
|
|
47
|
-
|
|
48
|
-
async close (options: AbortOptions = {}) {
|
|
49
|
-
const start = Date.now()
|
|
50
|
-
|
|
51
|
-
if (options.signal == null) {
|
|
52
|
-
const signal = AbortSignal.timeout(CLOSE_TIMEOUT)
|
|
53
|
-
|
|
54
|
-
options = {
|
|
55
|
-
...options,
|
|
56
|
-
signal
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const listener = (): void => {
|
|
61
|
-
const { host, port } = maConn.remoteAddr.toOptions()
|
|
62
|
-
log('timeout closing stream to %s:%s after %dms, destroying it manually',
|
|
63
|
-
host, port, Date.now() - start)
|
|
64
|
-
|
|
65
|
-
this.abort(new AbortError('Socket close timeout'))
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
options.signal?.addEventListener('abort', listener)
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
await stream.close()
|
|
72
|
-
} catch (err: any) {
|
|
73
|
-
log.error('error closing WebSocket gracefully', err)
|
|
74
|
-
this.abort(err)
|
|
75
|
-
} finally {
|
|
76
|
-
options.signal?.removeEventListener('abort', listener)
|
|
77
|
-
maConn.timeline.close = Date.now()
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
abort (err: Error): void {
|
|
82
|
-
const { host, port } = maConn.remoteAddr.toOptions()
|
|
83
|
-
log('timeout closing stream to %s:%s due to error',
|
|
84
|
-
host, port, err)
|
|
85
|
-
|
|
86
|
-
stream.destroy()
|
|
87
|
-
maConn.timeline.close = Date.now()
|
|
88
|
-
|
|
89
|
-
// ws WebSocket.terminate does not accept an Error arg to emit an 'error'
|
|
90
|
-
// event on destroy like other node streams so we can't update a metric
|
|
91
|
-
// with an event listener
|
|
92
|
-
// https://github.com/websockets/ws/issues/1752#issuecomment-622380981
|
|
93
|
-
metrics?.increment({ [`${metricPrefix}error`]: true })
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
stream.socket.addEventListener('close', () => {
|
|
98
|
-
metrics?.increment({ [`${metricPrefix}close`]: true })
|
|
99
|
-
|
|
100
|
-
// In instances where `close` was not explicitly called,
|
|
101
|
-
// such as an iterable stream ending, ensure we have set the close
|
|
102
|
-
// timeline
|
|
103
|
-
if (maConn.timeline.close == null) {
|
|
104
|
-
maConn.timeline.close = Date.now()
|
|
105
|
-
}
|
|
106
|
-
}, { once: true })
|
|
107
|
-
|
|
108
|
-
return maConn
|
|
109
|
-
}
|