@libp2p/websockets 9.2.19-6059227cb → 9.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libp2p/websockets",
3
- "version": "9.2.19-6059227cb",
3
+ "version": "9.2.19-87bc8d4fb",
4
4
  "description": "JavaScript implementation of the WebSockets module that libp2p uses and that implements the interface-transport spec",
5
5
  "license": "Apache-2.0 OR MIT",
6
6
  "homepage": "https://github.com/libp2p/js-libp2p/tree/main/packages/transport-websockets#readme",
@@ -67,28 +67,27 @@
67
67
  "test:electron-main": "aegir test -t electron-main -f ./dist/test/node.js --cov"
68
68
  },
69
69
  "dependencies": {
70
- "@libp2p/interface": "2.11.0-6059227cb",
71
- "@libp2p/utils": "6.7.2-6059227cb",
72
- "@multiformats/multiaddr": "^13.0.1",
73
- "@multiformats/multiaddr-matcher": "^3.0.1",
74
- "@multiformats/multiaddr-to-uri": "^12.0.0",
70
+ "@libp2p/interface": "2.11.0-87bc8d4fb",
71
+ "@libp2p/utils": "6.7.2-87bc8d4fb",
72
+ "@multiformats/multiaddr": "^12.4.4",
73
+ "@multiformats/multiaddr-matcher": "^2.0.0",
74
+ "@multiformats/multiaddr-to-uri": "^11.0.0",
75
75
  "@types/ws": "^8.18.1",
76
76
  "it-ws": "^6.1.5",
77
77
  "main-event": "^1.0.1",
78
+ "p-defer": "^4.0.1",
78
79
  "p-event": "^6.0.1",
79
80
  "progress-events": "^1.0.1",
80
- "uint8arraylist": "^2.4.8",
81
- "uint8arrays": "^5.1.0",
82
- "ws": "^8.18.3"
81
+ "race-signal": "^1.1.3",
82
+ "ws": "^8.18.2"
83
83
  },
84
84
  "devDependencies": {
85
- "@libp2p/logger": "5.2.0-6059227cb",
86
- "aegir": "^47.0.22",
85
+ "@libp2p/logger": "5.2.0-87bc8d4fb",
86
+ "aegir": "^47.0.14",
87
87
  "is-loopback-addr": "^2.0.2",
88
88
  "p-wait-for": "^5.0.2",
89
- "sinon": "^21.0.0",
90
- "sinon-ts": "^2.0.0",
91
- "undici": "^7.15.0"
89
+ "sinon": "^20.0.0",
90
+ "sinon-ts": "^2.0.0"
92
91
  },
93
92
  "browser": {
94
93
  "./dist/src/listener.js": "./dist/src/listener.browser.js"
package/src/filters.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { WebSocketsSecure, WebSockets, DNS } from '@multiformats/multiaddr-matcher'
2
+ import type { Multiaddr } from '@multiformats/multiaddr'
3
+
4
+ /**
5
+ * @deprecated Configure this globally by passing a `connectionGater` to `createLibp2p` with a `denyDialMultiaddr` method that returns `false`
6
+ */
7
+ export function all (multiaddrs: Multiaddr[]): Multiaddr[] {
8
+ return multiaddrs.filter((ma) => {
9
+ return WebSocketsSecure.exactMatch(ma) || WebSockets.exactMatch(ma)
10
+ })
11
+ }
12
+
13
+ /**
14
+ * @deprecated Configure this globally by passing a `connectionGater` to `createLibp2p`
15
+ */
16
+ export function wss (multiaddrs: Multiaddr[]): Multiaddr[] {
17
+ return multiaddrs.filter((ma) => {
18
+ return WebSocketsSecure.exactMatch(ma)
19
+ })
20
+ }
21
+
22
+ /**
23
+ * @deprecated Configure this globally by passing a `connectionGater` to `createLibp2p`
24
+ */
25
+ export function dnsWss (multiaddrs: Multiaddr[]): Multiaddr[] {
26
+ return multiaddrs.filter((ma) => {
27
+ return DNS.matches(ma) && WebSocketsSecure.exactMatch(ma)
28
+ })
29
+ }
30
+
31
+ /**
32
+ * @deprecated Configure this globally by passing a `connectionGater` to `createLibp2p`
33
+ */
34
+ export function dnsWsOrWss (multiaddrs: Multiaddr[]): Multiaddr[] {
35
+ return multiaddrs.filter((ma) => {
36
+ return DNS.matches(ma) && (WebSocketsSecure.exactMatch(ma) || WebSockets.exactMatch(ma))
37
+ })
38
+ }
package/src/index.ts CHANGED
@@ -24,21 +24,35 @@
24
24
  */
25
25
 
26
26
  import { transportSymbol, serviceCapabilities, ConnectionFailedError } from '@libp2p/interface'
27
- import { WebSockets as WebSocketsMatcher, WebSocketsSecure } from '@multiformats/multiaddr-matcher'
28
27
  import { multiaddrToUri as toUri } from '@multiformats/multiaddr-to-uri'
29
- import { pEvent } from 'p-event'
28
+ import { connect } from 'it-ws/client'
29
+ import pDefer from 'p-defer'
30
30
  import { CustomProgressEvent } from 'progress-events'
31
+ import { raceSignal } from 'race-signal'
32
+ import * as filters from './filters.js'
31
33
  import { createListener } from './listener.js'
32
- import { webSocketToMaConn } from './websocket-to-conn.js'
33
- import type { Transport, CreateListenerOptions, DialTransportOptions, Listener, AbortOptions, ComponentLogger, Logger, Connection, OutboundConnectionUpgradeEvents, Metrics, CounterGroup, Libp2pEvents } from '@libp2p/interface'
34
+ import { socketToMaConn } from './socket-to-conn.js'
35
+ import type { Transport, MultiaddrFilter, CreateListenerOptions, DialTransportOptions, Listener, AbortOptions, ComponentLogger, Logger, Connection, OutboundConnectionUpgradeEvents, Metrics, CounterGroup, Libp2pEvents } from '@libp2p/interface'
34
36
  import type { Multiaddr } from '@multiformats/multiaddr'
35
37
  import type { WebSocketOptions } from 'it-ws/client'
38
+ import type { DuplexWebSocket } from 'it-ws/duplex'
36
39
  import type { TypedEventTarget } from 'main-event'
37
40
  import type http from 'node:http'
38
41
  import type https from 'node:https'
39
42
  import type { ProgressEvent } from 'progress-events'
43
+ import type { ClientOptions } from 'ws'
40
44
 
41
45
  export interface WebSocketsInit extends AbortOptions, WebSocketOptions {
46
+ /**
47
+ * @deprecated Use a ConnectionGater instead
48
+ */
49
+ filter?: MultiaddrFilter
50
+
51
+ /**
52
+ * Options used to create WebSockets
53
+ */
54
+ websocket?: ClientOptions
55
+
42
56
  /**
43
57
  * Options used to create the HTTP server
44
58
  */
@@ -51,23 +65,11 @@ export interface WebSocketsInit extends AbortOptions, WebSocketOptions {
51
65
  https?: https.ServerOptions
52
66
 
53
67
  /**
54
- * How large the outgoing [bufferedAmount](https://websockets.spec.whatwg.org/#dom-websocket-bufferedamount)
55
- * property of incoming and outgoing websockets is allowed to get in bytes.
56
- *
57
- * If this limit is exceeded, backpressure will be applied to the writer.
58
- *
59
- * @default 4_194_304
60
- */
61
- maxBufferedAmount?: number
62
-
63
- /**
64
- * If the [bufferedAmount](https://websockets.spec.whatwg.org/#dom-websocket-bufferedamount)
65
- * property of a WebSocket exceeds `maxBufferedAmount`, poll the field every
66
- * this number of ms to see if the socket can accept new data.
68
+ * Inbound connections must complete their upgrade within this many ms
67
69
  *
68
- * @default 500
70
+ * @deprecated Use the `connectionManager.inboundUpgradeTimeout` libp2p config key instead
69
71
  */
70
- bufferedAmountPollInterval?: number
72
+ inboundConnectionUpgradeTimeout?: number
71
73
  }
72
74
 
73
75
  export interface WebSocketsComponents {
@@ -119,14 +121,10 @@ class WebSockets implements Transport<WebSocketsDialEvents> {
119
121
  this.log('dialing %s', ma)
120
122
  options = options ?? {}
121
123
 
122
- const maConn = webSocketToMaConn({
123
- websocket: await this._connect(ma, options),
124
- remoteAddr: ma,
125
- metrics: this.metrics?.dialerEvents,
126
- direction: 'outbound',
127
- log: this.components.logger.forComponent('libp2p:websockets:connection'),
128
- maxBufferedAmount: this.init.maxBufferedAmount,
129
- bufferedAmountPollInterval: this.init.bufferedAmountPollInterval
124
+ const socket = await this._connect(ma, options)
125
+ const maConn = socketToMaConn(socket, ma, {
126
+ logger: this.logger,
127
+ metrics: this.metrics?.dialerEvents
130
128
  })
131
129
  this.log('new outbound connection %s', maConn.remoteAddr)
132
130
 
@@ -135,35 +133,43 @@ class WebSockets implements Transport<WebSocketsDialEvents> {
135
133
  return conn
136
134
  }
137
135
 
138
- async _connect (ma: Multiaddr, options: DialTransportOptions<WebSocketsDialEvents>): Promise<WebSocket> {
136
+ async _connect (ma: Multiaddr, options: DialTransportOptions<WebSocketsDialEvents>): Promise<DuplexWebSocket> {
139
137
  options?.signal?.throwIfAborted()
140
138
 
141
- const uri = toUri(ma)
142
- this.log('create websocket connection to %s', uri)
143
- const websocket = new WebSocket(uri)
144
- websocket.binaryType = 'arraybuffer'
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
+ })
145
153
 
146
154
  try {
147
155
  options.onProgress?.(new CustomProgressEvent('websockets:open-connection'))
148
- await pEvent(websocket, 'open', options)
156
+ await raceSignal(Promise.race([rawSocket.connected(), errorPromise.promise]), options.signal)
149
157
  } catch (err: any) {
150
158
  if (options.signal?.aborted) {
151
159
  this.metrics?.dialerEvents.increment({ abort: true })
152
- throw new ConnectionFailedError(`Could not connect to ${uri}`)
153
- } else {
154
- this.metrics?.dialerEvents.increment({ error: true })
155
160
  }
156
161
 
157
- try {
158
- websocket.close()
159
- } catch {}
162
+ rawSocket.close()
163
+ .catch(err => {
164
+ this.log.error('error closing raw socket', err)
165
+ })
160
166
 
161
167
  throw err
162
168
  }
163
169
 
164
170
  this.log('connected %s', ma)
165
171
  this.metrics?.dialerEvents.increment({ connect: true })
166
- return websocket
172
+ return rawSocket
167
173
  }
168
174
 
169
175
  /**
@@ -182,10 +188,24 @@ class WebSockets implements Transport<WebSocketsDialEvents> {
182
188
  })
183
189
  }
184
190
 
191
+ /**
192
+ * Takes a list of `Multiaddr`s and returns only valid WebSockets addresses.
193
+ * By default, in a browser environment only DNS+WSS multiaddr is accepted,
194
+ * while in a Node.js environment DNS+{WS, WSS} multiaddrs are accepted.
195
+ */
185
196
  listenFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
186
- return multiaddrs.filter(ma => WebSocketsMatcher.exactMatch(ma) || WebSocketsSecure.exactMatch(ma))
197
+ multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
198
+
199
+ if (this.init?.filter != null) {
200
+ return this.init?.filter(multiaddrs)
201
+ }
202
+
203
+ return filters.all(multiaddrs)
187
204
  }
188
205
 
206
+ /**
207
+ * Filter check for all Multiaddrs that this transport can dial
208
+ */
189
209
  dialFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
190
210
  return this.listenFilter(multiaddrs)
191
211
  }
package/src/listener.ts CHANGED
@@ -1,16 +1,18 @@
1
1
  import http from 'node:http'
2
2
  import https from 'node:https'
3
3
  import net from 'node:net'
4
- import { getNetConfig, getThinWaistAddresses, ipPortToMultiaddr as toMultiaddr } from '@libp2p/utils'
4
+ import { getThinWaistAddresses } from '@libp2p/utils/get-thin-waist-addresses'
5
+ import { ipPortToMultiaddr as toMultiaddr } from '@libp2p/utils/ip-port-to-multiaddr'
5
6
  import { multiaddr } from '@multiformats/multiaddr'
6
7
  import { WebSockets, WebSocketsSecure } from '@multiformats/multiaddr-matcher'
8
+ import duplex from 'it-ws/duplex'
7
9
  import { TypedEventEmitter, setMaxListeners } from 'main-event'
8
10
  import { pEvent } from 'p-event'
9
11
  import * as ws from 'ws'
10
- import { toWebSocket } from './utils.ts'
11
- import { webSocketToMaConn } from './websocket-to-conn.js'
12
+ import { socketToMaConn } from './socket-to-conn.js'
12
13
  import type { ComponentLogger, Logger, Listener, ListenerEvents, CreateListenerOptions, CounterGroup, MetricGroup, Metrics, TLSCertificate, Libp2pEvents, Upgrader, MultiaddrConnection } from '@libp2p/interface'
13
14
  import type { Multiaddr } from '@multiformats/multiaddr'
15
+ import type { DuplexWebSocket } from 'it-ws/duplex'
14
16
  import type { TypedEventTarget } from 'main-event'
15
17
  import type { EventEmitter } from 'node:events'
16
18
  import type { Server } from 'node:http'
@@ -29,8 +31,6 @@ export interface WebSocketListenerInit extends CreateListenerOptions {
29
31
  key?: string
30
32
  http?: http.ServerOptions
31
33
  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
44
43
  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,19 +54,15 @@ 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
59
57
 
60
58
  constructor (components: WebSocketListenerComponents, init: WebSocketListenerInit) {
61
59
  super()
62
60
 
63
- this.components = components
64
61
  this.log = components.logger.forComponent('libp2p:websockets:listener')
62
+ this.logger = components.logger
65
63
  this.upgrader = init.upgrader
66
64
  this.httpOptions = init.http
67
65
  this.httpsOptions = init.https ?? init.http
68
- this.maxBufferedAmount = init.maxBufferedAmount
69
- this.bufferedAmountPollInterval = init.bufferedAmountPollInterval
70
66
  this.sockets = new Set()
71
67
  this.shutdownController = new AbortController()
72
68
  setMaxListeners(Infinity, this.shutdownController.signal)
@@ -175,7 +171,6 @@ export class WebSocketListener extends TypedEventEmitter<ListenerEvents> impleme
175
171
 
176
172
  onWsServerConnection (socket: ws.WebSocket, req: http.IncomingMessage): void {
177
173
  let addr: string | ws.AddressInfo | null
178
- socket.binaryType = 'arraybuffer'
179
174
 
180
175
  try {
181
176
  addr = this.server.address()
@@ -194,18 +189,22 @@ export class WebSocketListener extends TypedEventEmitter<ListenerEvents> impleme
194
189
  return
195
190
  }
196
191
 
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
+
197
201
  let maConn: MultiaddrConnection
198
202
 
199
203
  try {
200
- maConn = webSocketToMaConn({
201
- websocket: toWebSocket(socket),
202
- remoteAddr: toMultiaddr(req.socket.remoteAddress ?? '0.0.0.0', req.socket.remotePort ?? 0).encapsulate('/ws'),
204
+ maConn = socketToMaConn(stream, toMultiaddr(stream.remoteAddress ?? '', stream.remotePort ?? 0), {
205
+ logger: this.logger,
203
206
  metrics: this.metrics?.events,
204
- metricPrefix: `${this.addr} `,
205
- direction: 'inbound',
206
- log: this.components.logger.forComponent('libp2p:websockets:connection'),
207
- maxBufferedAmount: this.maxBufferedAmount,
208
- bufferedAmountPollInterval: this.bufferedAmountPollInterval
207
+ metricPrefix: `${this.addr} `
209
208
  })
210
209
  } catch (err: any) {
211
210
  this.log.error('inbound connection failed', err)
@@ -223,7 +222,11 @@ export class WebSocketListener extends TypedEventEmitter<ListenerEvents> impleme
223
222
  this.log.error('inbound connection failed to upgrade - %e', err)
224
223
  this.metrics.errors?.increment({ [`${this.addr} inbound_upgrade`]: true })
225
224
 
226
- maConn.close()
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
+ })
227
230
  })
228
231
  }
229
232
 
@@ -246,12 +249,12 @@ export class WebSocketListener extends TypedEventEmitter<ListenerEvents> impleme
246
249
  this.https.addListener('tlsClientError', this.onTLSClientError.bind(this))
247
250
  }
248
251
 
249
- const config = getNetConfig(ma)
250
- this.addr = `${config.host}:${config.port}`
252
+ const options = ma.toOptions()
253
+ this.addr = `${options.host}:${options.port}`
251
254
 
252
255
  this.server.listen({
253
- ...config,
254
- ipv6Only: config.type === 'ip6'
256
+ ...options,
257
+ ipv6Only: options.family === 6
255
258
  })
256
259
 
257
260
  await new Promise<void>((resolve, reject) => {
@@ -331,20 +334,14 @@ export class WebSocketListener extends TypedEventEmitter<ListenerEvents> impleme
331
334
  // abort and in-flight connection upgrades
332
335
  this.shutdownController.abort()
333
336
 
334
- const events = [
337
+ await Promise.all([
335
338
  pEvent(this.server, 'close'),
339
+ // eslint-disable-next-line @typescript-eslint/await-thenable
340
+ this.http == null ? null : pEvent(this.http, 'close'),
341
+ // eslint-disable-next-line @typescript-eslint/await-thenable
342
+ this.https == null ? null : pEvent(this.https, 'close'),
336
343
  pEvent(this.wsServer, 'close')
337
- ]
338
-
339
- if (this.http != null) {
340
- events.push(pEvent(this.http, 'close'))
341
- }
342
-
343
- if (this.https != null) {
344
- events.push(pEvent(this.https, 'close'))
345
- }
346
-
347
- await Promise.all(events)
344
+ ])
348
345
 
349
346
  this.safeDispatchEvent('close')
350
347
  }
@@ -0,0 +1,114 @@
1
+ import { AbortError, ConnectionFailedError } 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 metrics = options.metrics
18
+ const metricPrefix = options.metricPrefix ?? ''
19
+
20
+ const maConn: MultiaddrConnection = {
21
+ log: options.logger.forComponent('libp2p:websockets:connection'),
22
+
23
+ async sink (source) {
24
+ try {
25
+ await stream.sink((async function * () {
26
+ for await (const buf of source) {
27
+ if (buf instanceof Uint8Array) {
28
+ yield buf
29
+ } else {
30
+ yield buf.subarray()
31
+ }
32
+ }
33
+ })())
34
+ } catch (err: any) {
35
+ if (err.type !== 'aborted') {
36
+ maConn.log.error(err)
37
+ }
38
+ }
39
+ },
40
+
41
+ source: stream.source,
42
+
43
+ remoteAddr,
44
+
45
+ timeline: { open: Date.now() },
46
+
47
+ async close (options: AbortOptions = {}) {
48
+ const start = Date.now()
49
+
50
+ if (options.signal == null) {
51
+ const signal = AbortSignal.timeout(CLOSE_TIMEOUT)
52
+
53
+ options = {
54
+ ...options,
55
+ signal
56
+ }
57
+ }
58
+
59
+ const listener = (): void => {
60
+ const { host, port } = maConn.remoteAddr.toOptions()
61
+ maConn.log('timeout closing stream to %s:%s after %dms, destroying it manually',
62
+ host, port, Date.now() - start)
63
+
64
+ this.abort(new AbortError('Socket close timeout'))
65
+ }
66
+
67
+ options.signal?.addEventListener('abort', listener)
68
+
69
+ try {
70
+ await stream.close()
71
+ } catch (err: any) {
72
+ maConn.log.error('error closing WebSocket gracefully - %e', err)
73
+ this.abort(err)
74
+ } finally {
75
+ options.signal?.removeEventListener('abort', listener)
76
+ maConn.timeline.close = Date.now()
77
+ }
78
+ },
79
+
80
+ abort (err: Error): void {
81
+ maConn.log.error('destroying WebSocket after error - %e', err)
82
+ stream.destroy()
83
+ maConn.timeline.close = Date.now()
84
+
85
+ // ws WebSocket.terminate does not accept an Error arg to emit an 'error'
86
+ // event on destroy like other node streams so we can't update a metric
87
+ // with an event listener
88
+ // https://github.com/websockets/ws/issues/1752#issuecomment-622380981
89
+ metrics?.increment({ [`${metricPrefix}error`]: true })
90
+ }
91
+ }
92
+
93
+ // track local vs remote closing
94
+ let closedLocally = false
95
+ const close = stream.socket.close.bind(stream.socket)
96
+ stream.socket.close = (...args) => {
97
+ closedLocally = true
98
+ return close(...args)
99
+ }
100
+
101
+ stream.socket.addEventListener('close', (evt) => {
102
+ maConn.log('closed %s, code %d, reason "%s", wasClean %s', closedLocally ? 'locally' : 'by remote', evt.code, evt.reason, evt.wasClean)
103
+
104
+ if (!evt.wasClean) {
105
+ maConn.abort(new ConnectionFailedError(`${closedLocally ? 'Local' : 'Remote'} did not close WebSocket cleanly`))
106
+ return
107
+ }
108
+
109
+ metrics?.increment({ [`${metricPrefix}close`]: true })
110
+ maConn.timeline.close = Date.now()
111
+ }, { once: true })
112
+
113
+ return maConn
114
+ }
@@ -1,7 +0,0 @@
1
- import type { WebSocket as WSSWebSocket } from 'ws';
2
- /**
3
- * Adds properties/methods to a `WebSocket` instance from the `ws` module to be
4
- * compatible with the `globalThis.WebSocket` API
5
- */
6
- export declare function toWebSocket(ws: WSSWebSocket): WebSocket;
7
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,IAAI,YAAY,EAAE,MAAM,IAAI,CAAA;AAEnD;;;GAGG;AACH,wBAAgB,WAAW,CAAE,EAAE,EAAE,YAAY,GAAG,SAAS,CA6BxD"}
package/dist/src/utils.js DELETED
@@ -1,30 +0,0 @@
1
- /**
2
- * Adds properties/methods to a `WebSocket` instance from the `ws` module to be
3
- * compatible with the `globalThis.WebSocket` API
4
- */
5
- export function toWebSocket(ws) {
6
- Object.defineProperty(ws, 'url', {
7
- value: '',
8
- writable: false
9
- });
10
- // @ts-expect-error not a WS/WebSocket method
11
- ws.dispatchEvent = (evt) => {
12
- if (evt.type === 'close') {
13
- ws.emit('close');
14
- }
15
- if (evt.type === 'open') {
16
- ws.emit('open');
17
- }
18
- if (evt.type === 'message') {
19
- const m = evt;
20
- ws.emit('data', m.data);
21
- }
22
- if (evt.type === 'error') {
23
- ws.emit('error', new Error('An error occurred'));
24
- }
25
- ws.emit(evt.type, evt);
26
- };
27
- // @ts-expect-error ws is now WebSocket
28
- return ws;
29
- }
30
- //# sourceMappingURL=utils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAE,EAAgB;IAC3C,MAAM,CAAC,cAAc,CAAC,EAAE,EAAE,KAAK,EAAE;QAC/B,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAA;IAEF,6CAA6C;IAC7C,EAAE,CAAC,aAAa,GAAG,CAAC,GAAU,EAAE,EAAE;QAChC,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACzB,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAClB,CAAC;QAED,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACxB,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACjB,CAAC;QAED,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,GAAmB,CAAA;YAC7B,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;QACzB,CAAC;QAED,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACzB,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAA;QAClD,CAAC;QACD,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACxB,CAAC,CAAA;IAED,uCAAuC;IACvC,OAAO,EAAE,CAAA;AACX,CAAC"}
@@ -1,9 +0,0 @@
1
- import type { MultiaddrConnection } from '@libp2p/interface';
2
- import type { AbstractMultiaddrConnectionInit } from '@libp2p/utils';
3
- export interface WebSocketMultiaddrConnectionInit extends Omit<AbstractMultiaddrConnectionInit, 'name'> {
4
- websocket: WebSocket;
5
- maxBufferedAmount?: number;
6
- bufferedAmountPollInterval?: number;
7
- }
8
- export declare function webSocketToMaConn(init: WebSocketMultiaddrConnectionInit): MultiaddrConnection;
9
- //# sourceMappingURL=websocket-to-conn.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"websocket-to-conn.d.ts","sourceRoot":"","sources":["../../src/websocket-to-conn.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC1E,OAAO,KAAK,EAAE,+BAA+B,EAA6B,MAAM,eAAe,CAAA;AAK/F,MAAM,WAAW,gCAAiC,SAAQ,IAAI,CAAC,+BAA+B,EAAE,MAAM,CAAC;IACrG,SAAS,EAAE,SAAS,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,0BAA0B,CAAC,EAAE,MAAM,CAAA;CACpC;AA4FD,wBAAgB,iBAAiB,CAAE,IAAI,EAAE,gCAAgC,GAAG,mBAAmB,CAE9F"}
@@ -1,83 +0,0 @@
1
- import { AbstractMultiaddrConnection, repeatingTask } from '@libp2p/utils';
2
- import { Uint8ArrayList } from 'uint8arraylist';
3
- import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
4
- const DEFAULT_MAX_BUFFERED_AMOUNT = 1024 * 1024 * 4;
5
- const DEFAULT_BUFFERED_AMOUNT_POLL_INTERVAL = 10;
6
- class WebSocketMultiaddrConnection extends AbstractMultiaddrConnection {
7
- websocket;
8
- maxBufferedAmount;
9
- checkBufferedAmountTask;
10
- constructor(init) {
11
- super(init);
12
- this.websocket = init.websocket;
13
- this.maxBufferedAmount = init.maxBufferedAmount ?? DEFAULT_MAX_BUFFERED_AMOUNT;
14
- this.checkBufferedAmountTask = repeatingTask(this.checkBufferedAmount.bind(this), init.bufferedAmountPollInterval ?? DEFAULT_BUFFERED_AMOUNT_POLL_INTERVAL);
15
- this.websocket.addEventListener('close', (evt) => {
16
- this.log('closed - code %d, reason "%s", wasClean %s', evt.code, evt.reason, evt.wasClean);
17
- this.checkBufferedAmountTask.stop();
18
- if (!evt.wasClean) {
19
- this.onRemoteReset();
20
- return;
21
- }
22
- this.onTransportClosed();
23
- }, { once: true });
24
- this.websocket.addEventListener('message', (evt) => {
25
- try {
26
- let buf;
27
- if (typeof evt.data === 'string') {
28
- buf = uint8ArrayFromString(evt.data);
29
- }
30
- else if (evt.data instanceof ArrayBuffer) {
31
- buf = new Uint8Array(evt.data, 0, evt.data.byteLength);
32
- }
33
- else {
34
- this.abort(new Error('Incorrect binary type'));
35
- return;
36
- }
37
- this.onData(buf);
38
- }
39
- catch (err) {
40
- this.log.error('error receiving data - %e', err);
41
- }
42
- });
43
- }
44
- sendData(data) {
45
- for (const buf of data) {
46
- this.websocket.send(buf);
47
- }
48
- const canSendMore = this.websocket.bufferedAmount < this.maxBufferedAmount;
49
- if (!canSendMore) {
50
- this.checkBufferedAmountTask.start();
51
- }
52
- return {
53
- sentBytes: data.byteLength,
54
- canSendMore
55
- };
56
- }
57
- sendReset() {
58
- this.websocket.close(1006); // abnormal closure
59
- }
60
- async sendClose(options) {
61
- this.websocket.close();
62
- options?.signal?.throwIfAborted();
63
- }
64
- sendPause() {
65
- // read backpressure is not supported
66
- }
67
- sendResume() {
68
- // read backpressure is not supported
69
- }
70
- checkBufferedAmount() {
71
- this.log('buffered amount now %d', this.websocket.bufferedAmount);
72
- if (this.websocket.bufferedAmount === 0) {
73
- this.checkBufferedAmountTask.stop();
74
- this.safeDispatchEvent('drain');
75
- }
76
- }
77
- }
78
- // Convert a stream into a MultiaddrConnection
79
- // https://github.com/libp2p/interface-transport#multiaddrconnection
80
- export function webSocketToMaConn(init) {
81
- return new WebSocketMultiaddrConnection(init);
82
- }
83
- //# sourceMappingURL=websocket-to-conn.js.map