@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/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 { connect } from 'it-ws/client'
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 { socketToMaConn } from './socket-to-conn.js'
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 socket = await this._connect(ma, options)
125
- const maConn = socketToMaConn(socket, ma, {
126
- logger: this.logger,
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<DuplexWebSocket> {
158
+ async _connect (ma: Multiaddr, options: DialTransportOptions<WebSocketsDialEvents>): Promise<WebSocket> {
137
159
  options?.signal?.throwIfAborted()
138
160
 
139
- const cOpts = ma.toOptions()
140
- this.log('dialing %s:%s', cOpts.host, cOpts.port)
141
-
142
- const errorPromise = pDefer()
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 raceSignal(Promise.race([rawSocket.connected(), errorPromise.promise]), options.signal)
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
- rawSocket.close()
163
- .catch(err => {
164
- this.log.error('error closing raw socket', err)
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 rawSocket
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/get-thin-waist-addresses'
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 { socketToMaConn } from './socket-to-conn.js'
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 = socketToMaConn(stream, toMultiaddr(stream.remoteAddress ?? '', stream.remotePort ?? 0), {
205
- logger: this.logger,
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
- await maConn.close()
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 options = ma.toOptions()
253
- this.addr = `${options.host}:${options.port}`
249
+ const config = getNetConfig(ma)
250
+ this.addr = `${config.host}:${config.port}`
254
251
 
255
252
  this.server.listen({
256
- ...options,
257
- ipv6Only: options.family === 6
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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"}
@@ -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
- }
@@ -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
- }