@libp2p/websockets 9.0.13 → 9.1.0-5c4a79e5a

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
@@ -18,43 +18,9 @@
18
18
  * })
19
19
  * await node.start()
20
20
  *
21
- * const ma = multiaddr('/ip4/127.0.0.1/tcp/9090/ws')
21
+ * const ma = multiaddr('/dns4/example.com/tcp/9090/tls/ws')
22
22
  * await node.dial(ma)
23
23
  * ```
24
- *
25
- * ## Filters
26
- *
27
- * When run in a browser by default this module will only connect to secure web socket addresses.
28
- *
29
- * To change this you should pass a filter to the factory function.
30
- *
31
- * You can create your own address filters for this transports, or rely in the filters [provided](./src/filters.js).
32
- *
33
- * The available filters are:
34
- *
35
- * - `filters.all`
36
- * - Returns all TCP and DNS based addresses, both with `ws` or `wss`.
37
- * - `filters.dnsWss`
38
- * - Returns all DNS based addresses with `wss`.
39
- * - `filters.dnsWsOrWss`
40
- * - Returns all DNS based addresses, both with `ws` or `wss`.
41
- *
42
- * @example Allow dialing insecure WebSockets
43
- *
44
- * ```TypeScript
45
- * import { createLibp2p } from 'libp2p'
46
- * import { webSockets } from '@libp2p/websockets'
47
- * import * as filters from '@libp2p/websockets/filters'
48
- *
49
- * const node = await createLibp2p({
50
- * transports: [
51
- * webSockets({
52
- * // connect to all sockets, even insecure ones
53
- * filter: filters.all
54
- * })
55
- * ]
56
- * })
57
- * ```
58
24
  */
59
25
 
60
26
  import { transportSymbol, serviceCapabilities, ConnectionFailedError } from '@libp2p/interface'
@@ -63,25 +29,50 @@ import { connect, type WebSocketOptions } from 'it-ws/client'
63
29
  import pDefer from 'p-defer'
64
30
  import { CustomProgressEvent } from 'progress-events'
65
31
  import { raceSignal } from 'race-signal'
66
- import { isBrowser, isWebWorker } from 'wherearewe'
67
32
  import * as filters from './filters.js'
68
33
  import { createListener } from './listener.js'
69
34
  import { socketToMaConn } from './socket-to-conn.js'
70
- import type { Transport, MultiaddrFilter, CreateListenerOptions, DialTransportOptions, Listener, AbortOptions, ComponentLogger, Logger, Connection, OutboundConnectionUpgradeEvents, Metrics, CounterGroup } from '@libp2p/interface'
35
+ import type { Transport, MultiaddrFilter, CreateListenerOptions, DialTransportOptions, Listener, AbortOptions, ComponentLogger, Logger, Connection, OutboundConnectionUpgradeEvents, Metrics, CounterGroup, TypedEventTarget, Libp2pEvents } from '@libp2p/interface'
71
36
  import type { Multiaddr } from '@multiformats/multiaddr'
72
- import type { Server } from 'http'
73
37
  import type { DuplexWebSocket } from 'it-ws/duplex'
38
+ import type http from 'node:http'
39
+ import type https from 'node:https'
74
40
  import type { ProgressEvent } from 'progress-events'
75
41
  import type { ClientOptions } from 'ws'
76
42
 
77
43
  export interface WebSocketsInit extends AbortOptions, WebSocketOptions {
44
+ /**
45
+ * @deprecated Use a ConnectionGater instead
46
+ */
78
47
  filter?: MultiaddrFilter
48
+
49
+ /**
50
+ * Options used to create WebSockets
51
+ */
79
52
  websocket?: ClientOptions
80
- server?: Server
53
+
54
+ /**
55
+ * Options used to create the HTTP server
56
+ */
57
+ http?: http.ServerOptions
58
+
59
+ /**
60
+ * Options used to create the HTTPs server. `options.http` will be used if
61
+ * unspecified.
62
+ */
63
+ https?: https.ServerOptions
64
+
65
+ /**
66
+ * Inbound connections must complete their upgrade within this many ms
67
+ *
68
+ * @default 5000
69
+ */
70
+ inboundConnectionUpgradeTimeout?: number
81
71
  }
82
72
 
83
73
  export interface WebSocketsComponents {
84
74
  logger: ComponentLogger
75
+ events: TypedEventTarget<Libp2pEvents>
85
76
  metrics?: Metrics
86
77
  }
87
78
 
@@ -95,12 +86,12 @@ export type WebSocketsDialEvents =
95
86
 
96
87
  class WebSockets implements Transport<WebSocketsDialEvents> {
97
88
  private readonly log: Logger
98
- private readonly init?: WebSocketsInit
89
+ private readonly init: WebSocketsInit
99
90
  private readonly logger: ComponentLogger
100
91
  private readonly metrics?: WebSocketsMetrics
101
92
  private readonly components: WebSocketsComponents
102
93
 
103
- constructor (components: WebSocketsComponents, init?: WebSocketsInit) {
94
+ constructor (components: WebSocketsComponents, init: WebSocketsInit = {}) {
104
95
  this.log = components.logger.forComponent('libp2p:websockets')
105
96
  this.logger = components.logger
106
97
  this.components = components
@@ -180,13 +171,14 @@ class WebSockets implements Transport<WebSocketsDialEvents> {
180
171
  }
181
172
 
182
173
  /**
183
- * Creates a Websockets listener. The provided `handler` function will be called
174
+ * Creates a WebSockets listener. The provided `handler` function will be called
184
175
  * anytime a new incoming Connection has been successfully upgraded via
185
176
  * `upgrader.upgradeInbound`
186
177
  */
187
178
  createListener (options: CreateListenerOptions): Listener {
188
179
  return createListener({
189
180
  logger: this.logger,
181
+ events: this.components.events,
190
182
  metrics: this.components.metrics
191
183
  }, {
192
184
  ...this.init,
@@ -195,7 +187,7 @@ class WebSockets implements Transport<WebSocketsDialEvents> {
195
187
  }
196
188
 
197
189
  /**
198
- * Takes a list of `Multiaddr`s and returns only valid Websockets addresses.
190
+ * Takes a list of `Multiaddr`s and returns only valid WebSockets addresses.
199
191
  * By default, in a browser environment only DNS+WSS multiaddr is accepted,
200
192
  * while in a Node.js environment DNS+{WS, WSS} multiaddrs are accepted.
201
193
  */
@@ -206,11 +198,6 @@ class WebSockets implements Transport<WebSocketsDialEvents> {
206
198
  return this.init?.filter(multiaddrs)
207
199
  }
208
200
 
209
- // Browser
210
- if (isBrowser || isWebWorker) {
211
- return filters.wss(multiaddrs)
212
- }
213
-
214
201
  return filters.all(multiaddrs)
215
202
  }
216
203
 
package/src/listener.ts CHANGED
@@ -1,145 +1,346 @@
1
- import os from 'os'
2
- import { TypedEventEmitter } from '@libp2p/interface'
1
+ import http from 'node:http'
2
+ import https from 'node:https'
3
+ import net from 'node:net'
4
+ import os from 'node:os'
5
+ import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
3
6
  import { ipPortToMultiaddr as toMultiaddr } from '@libp2p/utils/ip-port-to-multiaddr'
4
- import { multiaddr, protocols } from '@multiformats/multiaddr'
5
- import { createServer } from 'it-ws/server'
7
+ import { isLinkLocalIp } from '@libp2p/utils/link-local-ip'
8
+ import { multiaddr } from '@multiformats/multiaddr'
9
+ import { WebSockets, WebSocketsSecure } from '@multiformats/multiaddr-matcher'
10
+ import duplex from 'it-ws/duplex'
11
+ import { pEvent } from 'p-event'
12
+ import * as ws from 'ws'
6
13
  import { socketToMaConn } from './socket-to-conn.js'
7
- import type { ComponentLogger, Logger, Listener, ListenerEvents, CreateListenerOptions, CounterGroup, MetricGroup, Metrics } from '@libp2p/interface'
14
+ import type { ComponentLogger, Logger, Listener, ListenerEvents, CreateListenerOptions, CounterGroup, MetricGroup, Metrics, TLSCertificate, TypedEventTarget, Libp2pEvents, Upgrader, MultiaddrConnection } from '@libp2p/interface'
8
15
  import type { Multiaddr } from '@multiformats/multiaddr'
9
- import type { Server } from 'http'
10
16
  import type { DuplexWebSocket } from 'it-ws/duplex'
11
- import type { WebSocketServer } from 'it-ws/server'
17
+ import type { EventEmitter } from 'node:events'
18
+ import type { Server } from 'node:http'
19
+ import type { Duplex } from 'node:stream'
20
+ import type tls from 'node:tls'
12
21
 
13
22
  export interface WebSocketListenerComponents {
14
23
  logger: ComponentLogger
24
+ events: TypedEventTarget<Libp2pEvents>
15
25
  metrics?: Metrics
16
26
  }
17
27
 
18
28
  export interface WebSocketListenerInit extends CreateListenerOptions {
19
29
  server?: Server
30
+ inboundConnectionUpgradeTimeout?: number
31
+ cert?: string
32
+ key?: string
33
+ http?: http.ServerOptions
34
+ https?: http.ServerOptions
20
35
  }
21
36
 
22
37
  export interface WebSocketListenerMetrics {
23
- status: MetricGroup
24
- errors: CounterGroup
25
- events: CounterGroup
38
+ status?: MetricGroup
39
+ errors?: CounterGroup
40
+ events?: CounterGroup
26
41
  }
27
42
 
28
- class WebSocketListener extends TypedEventEmitter<ListenerEvents> implements Listener {
29
- private readonly connections: Set<DuplexWebSocket>
30
- private listeningMultiaddr?: Multiaddr
31
- private readonly server: WebSocketServer
43
+ export class WebSocketListener extends TypedEventEmitter<ListenerEvents> implements Listener {
32
44
  private readonly log: Logger
33
- private metrics?: WebSocketListenerMetrics
34
- private addr: string
45
+ private readonly logger: ComponentLogger
46
+ private readonly server: net.Server
47
+ private readonly wsServer: ws.WebSocketServer
48
+ private readonly metrics: WebSocketListenerMetrics
49
+ private readonly sockets: Set<net.Socket>
50
+ private readonly upgrader: Upgrader
51
+ private readonly inboundConnectionUpgradeTimeout: number
52
+ private readonly httpOptions?: http.ServerOptions
53
+ private readonly httpsOptions?: https.ServerOptions
54
+ private http?: http.Server
55
+ private https?: https.Server
56
+ private addr?: string
57
+ private listeningMultiaddr?: Multiaddr
35
58
 
36
59
  constructor (components: WebSocketListenerComponents, init: WebSocketListenerInit) {
37
60
  super()
38
61
 
39
62
  this.log = components.logger.forComponent('libp2p:websockets:listener')
40
- const metrics = components.metrics
41
- // Keep track of open connections to destroy when the listener is closed
42
- this.connections = new Set<DuplexWebSocket>()
63
+ this.logger = components.logger
64
+ this.upgrader = init.upgrader
65
+ this.httpOptions = init.http
66
+ this.httpsOptions = init.https ?? init.http
67
+ this.inboundConnectionUpgradeTimeout = init.inboundConnectionUpgradeTimeout ?? 5000
68
+ this.sockets = new Set()
43
69
 
44
- const self = this // eslint-disable-line @typescript-eslint/no-this-alias
70
+ this.wsServer = new ws.WebSocketServer({
71
+ noServer: true
72
+ })
73
+ this.wsServer.addListener('connection', this.onWsServerConnection.bind(this))
45
74
 
46
- this.addr = 'unknown'
75
+ components.metrics?.registerMetricGroup('libp2p_websockets_inbound_connections_total', {
76
+ label: 'address',
77
+ help: 'Current active connections in WebSocket listener',
78
+ calculate: () => {
79
+ if (this.addr == null) {
80
+ return {}
81
+ }
47
82
 
48
- this.server = createServer({
49
- ...init,
50
- onConnection: (stream: DuplexWebSocket) => {
51
- const maConn = socketToMaConn(stream, toMultiaddr(stream.remoteAddress ?? '', stream.remotePort ?? 0), {
52
- logger: components.logger,
53
- metrics: this.metrics?.events,
54
- metricPrefix: `${this.addr} `
55
- })
56
- this.log('new inbound connection %s', maConn.remoteAddr)
83
+ return {
84
+ [this.addr]: this.sockets.size
85
+ }
86
+ }
87
+ })
57
88
 
58
- this.connections.add(stream)
89
+ this.metrics = {
90
+ status: components.metrics?.registerMetricGroup('libp2p_websockets_listener_status_info', {
91
+ label: 'address',
92
+ help: 'Current status of the WebSocket listener socket'
93
+ }),
94
+ errors: components.metrics?.registerMetricGroup('libp2p_websockets_listener_errors_total', {
95
+ label: 'address',
96
+ help: 'Total count of WebSocket listener errors by type'
97
+ }),
98
+ events: components.metrics?.registerMetricGroup('libp2p_websockets_listener_events_total', {
99
+ label: 'address',
100
+ help: 'Total count of WebSocket listener events by type'
101
+ })
102
+ }
59
103
 
60
- stream.socket.on('close', function () {
61
- self.connections.delete(stream)
104
+ this.server = net.createServer({
105
+ pauseOnConnect: true
106
+ }, (socket) => {
107
+ this.onSocketConnection(socket)
108
+ .catch(err => {
109
+ this.log.error('error handling socket - %e', err)
110
+ socket.destroy()
62
111
  })
112
+ })
63
113
 
64
- init.upgrader.upgradeInbound(maConn)
65
- .catch(async err => {
66
- this.log.error('inbound connection failed to upgrade', err)
67
- this.metrics?.errors.increment({ [`${this.addr} inbound_upgrade`]: true })
114
+ components.events.addEventListener('certificate:provision', this.onCertificateProvision.bind(this))
115
+ components.events.addEventListener('certificate:renew', this.onCertificateRenew.bind(this))
116
+ }
68
117
 
69
- try {
70
- maConn.abort(err)
71
- } catch (err) {
72
- this.log.error('inbound connection failed to close after upgrade failed - %e', err)
73
- this.metrics?.errors.increment({ [`${this.addr} inbound_closing_failed`]: true })
74
- }
75
- })
76
- }
77
- })
118
+ async onSocketConnection (socket: net.Socket): Promise<void> {
119
+ this.metrics.events?.increment({ [`${this.addr} connection`]: true })
78
120
 
79
- this.server.on('listening', () => {
80
- if (metrics != null) {
81
- const { host, port } = this.listeningMultiaddr?.toOptions() ?? {}
82
- this.addr = `${host}:${port}`
83
-
84
- metrics.registerMetricGroup('libp2p_websockets_inbound_connections_total', {
85
- label: 'address',
86
- help: 'Current active connections in WebSocket listener',
87
- calculate: () => {
88
- return {
89
- [this.addr]: this.connections.size
90
- }
91
- }
92
- })
121
+ let buffer = socket.read(1)
93
122
 
94
- this.metrics = {
95
- status: metrics?.registerMetricGroup('libp2p_websockets_listener_status_info', {
96
- label: 'address',
97
- help: 'Current status of the WebSocket listener socket'
98
- }),
99
- errors: metrics?.registerMetricGroup('libp2p_websockets_listener_errors_total', {
100
- label: 'address',
101
- help: 'Total count of WebSocket listener errors by type'
102
- }),
103
- events: metrics?.registerMetricGroup('libp2p_websockets_listener_events_total', {
104
- label: 'address',
105
- help: 'Total count of WebSocket listener events by type'
106
- })
107
- }
108
- }
109
- this.dispatchEvent(new CustomEvent('listening'))
123
+ if (buffer == null) {
124
+ await pEvent(socket, 'readable')
125
+ buffer = socket.read(1)
126
+ }
127
+
128
+ // determine if this is an HTTP(s) request
129
+ const byte = buffer[0]
130
+ let server: EventEmitter | undefined = this.http
131
+
132
+ // https://github.com/mscdex/httpolyglot/blob/1c6c4af65f4cf95a32c918d0fdcc532e0c095740/lib/index.js#L92
133
+ if (byte < 32 || byte >= 127) {
134
+ server = this.https
135
+ }
136
+
137
+ if (server == null) {
138
+ this.log.error('no appropriate listener configured for byte %d', byte)
139
+ socket.destroy()
140
+ return
141
+ }
142
+
143
+ // store the socket so we can close it when the listener closes
144
+ this.sockets.add(socket)
145
+
146
+ socket.on('close', () => {
147
+ this.metrics.events?.increment({ [`${this.addr} close`]: true })
148
+ this.sockets.delete(socket)
110
149
  })
111
- this.server.on('error', (err: Error) => {
112
- this.metrics?.errors.increment({ [`${this.addr} listen_error`]: true })
113
- this.dispatchEvent(new CustomEvent('error', {
114
- detail: err
115
- }))
150
+
151
+ socket.on('error', (err) => {
152
+ this.log.error('socket error - %e', err)
153
+ this.metrics.events?.increment({ [`${this.addr} error`]: true })
154
+ socket.destroy()
116
155
  })
117
- this.server.on('close', () => {
118
- this.dispatchEvent(new CustomEvent('close'))
156
+
157
+ socket.once('timeout', () => {
158
+ this.metrics.events?.increment({ [`${this.addr} timeout`]: true })
119
159
  })
160
+
161
+ socket.once('end', () => {
162
+ this.metrics.events?.increment({ [`${this.addr} end`]: true })
163
+ })
164
+
165
+ // re-queue first data chunk
166
+ socket.unshift(buffer)
167
+
168
+ // hand the socket off to the appropriate server
169
+ server.emit('connection', socket)
120
170
  }
121
171
 
122
- async close (): Promise<void> {
123
- await Promise.all(
124
- Array.from(this.connections).map(async maConn => { await maConn.close() })
125
- )
172
+ onWsServerConnection (socket: ws.WebSocket, req: http.IncomingMessage): void {
173
+ let addr: string | ws.AddressInfo | null
174
+
175
+ try {
176
+ addr = this.server.address()
177
+
178
+ if (typeof addr === 'string') {
179
+ throw new Error('Cannot listen on unix sockets')
180
+ }
126
181
 
127
- if (this.server.address() == null) {
128
- // not listening, close will throw an error
182
+ if (addr == null) {
183
+ throw new Error('Server was closing or not running')
184
+ }
185
+ } catch (err: any) {
186
+ this.log.error('error obtaining remote socket address - %e', err)
187
+ req.destroy(err)
188
+ socket.close()
129
189
  return
130
190
  }
131
191
 
132
- await this.server.close()
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
+ let maConn: MultiaddrConnection
202
+
203
+ try {
204
+ maConn = socketToMaConn(stream, toMultiaddr(stream.remoteAddress ?? '', stream.remotePort ?? 0), {
205
+ logger: this.logger,
206
+ metrics: this.metrics?.events,
207
+ metricPrefix: `${this.addr} `
208
+ })
209
+ } catch (err: any) {
210
+ this.log.error('inbound connection failed', err)
211
+ this.metrics.errors?.increment({ [`${this.addr} inbound_to_connection`]: true })
212
+ socket.close()
213
+ return
214
+ }
215
+
216
+ this.log('new inbound connection %s', maConn.remoteAddr)
217
+ const signal = AbortSignal.timeout(this.inboundConnectionUpgradeTimeout)
218
+ setMaxListeners(Infinity, signal)
219
+
220
+ this.upgrader.upgradeInbound(maConn, {
221
+ signal
222
+ })
223
+ .catch(async err => {
224
+ this.log.error('inbound connection failed to upgrade - %e', err)
225
+ this.metrics.errors?.increment({ [`${this.addr} inbound_upgrade`]: true })
226
+
227
+ await maConn.close()
228
+ .catch(err => {
229
+ this.log.error('inbound connection failed to close after upgrade failed', err)
230
+ this.metrics.errors?.increment({ [`${this.addr} inbound_closing_failed`]: true })
231
+ })
232
+ })
233
+ }
234
+
235
+ onUpgrade (req: http.IncomingMessage, socket: Duplex, head: Buffer): void {
236
+ this.wsServer.handleUpgrade(req, socket, head, this.onWsServerConnection.bind(this))
237
+ }
238
+
239
+ onTLSClientError (err: Error, socket: tls.TLSSocket): void {
240
+ this.log.error('TLS client error - %e', err)
241
+ socket.destroy()
133
242
  }
134
243
 
135
244
  async listen (ma: Multiaddr): Promise<void> {
245
+ if (WebSockets.exactMatch(ma)) {
246
+ this.http = http.createServer(this.httpOptions ?? {}, this.httpRequestHandler.bind(this))
247
+ this.http.addListener('upgrade', this.onUpgrade.bind(this))
248
+ } else if (WebSocketsSecure.exactMatch(ma)) {
249
+ this.https = https.createServer(this.httpsOptions ?? {}, this.httpRequestHandler.bind(this))
250
+ this.https.addListener('upgrade', this.onUpgrade.bind(this))
251
+ this.https.addListener('tlsClientError', this.onTLSClientError.bind(this))
252
+ }
253
+
136
254
  this.listeningMultiaddr = ma
255
+ const { host, port } = ma.toOptions()
256
+ this.addr = `${host}:${port}`
257
+
258
+ this.server.listen(port, host)
259
+
260
+ await new Promise<void>((resolve, reject) => {
261
+ const onListening = (): void => {
262
+ removeListeners()
263
+ resolve()
264
+ }
265
+ const onError = (err: Error): void => {
266
+ this.metrics.errors?.increment({ [`${this.addr} listen_error`]: true })
267
+ removeListeners()
268
+ reject(err)
269
+ }
270
+ const onDrop = (): void => {
271
+ this.metrics.events?.increment({ [`${this.addr} drop`]: true })
272
+ }
273
+ const removeListeners = (): void => {
274
+ this.server.removeListener('listening', onListening)
275
+ this.server.removeListener('error', onError)
276
+ this.server.removeListener('drop', onDrop)
277
+ }
278
+
279
+ this.server.addListener('listening', onListening)
280
+ this.server.addListener('error', onError)
281
+ this.server.addListener('drop', onDrop)
282
+ })
137
283
 
138
- await this.server.listen(ma.toOptions())
284
+ this.safeDispatchEvent('listening')
285
+ }
286
+
287
+ onCertificateProvision (event: CustomEvent<TLSCertificate>): void {
288
+ if (this.https != null) {
289
+ this.log('auto-tls certificate found but already listening on https')
290
+ return
291
+ }
292
+
293
+ this.log('auto-tls certificate found, starting https server')
294
+ this.https = https.createServer({
295
+ ...this.httpsOptions,
296
+ ...event.detail
297
+ }, this.httpRequestHandler.bind(this))
298
+ this.https.addListener('upgrade', this.onUpgrade.bind(this))
299
+ this.https.addListener('tlsClientError', this.onTLSClientError.bind(this))
300
+
301
+ this.safeDispatchEvent('listening')
302
+ }
303
+
304
+ onCertificateRenew (event: CustomEvent<TLSCertificate>): void {
305
+ // stop accepting new connections
306
+ this.https?.close()
307
+
308
+ this.log('auto-tls certificate renewed, restarting https server')
309
+ this.https = https.createServer({
310
+ ...this.httpsOptions,
311
+ ...event.detail
312
+ }, this.httpRequestHandler.bind(this))
313
+ this.https.addListener('upgrade', this.onUpgrade.bind(this))
314
+ this.https.addListener('tlsClientError', this.onTLSClientError.bind(this))
315
+ }
316
+
317
+ async close (): Promise<void> {
318
+ this.server.close()
319
+ this.http?.close()
320
+ this.https?.close()
321
+ this.wsServer.close()
322
+
323
+ // close all connections, must be done after closing the server to prevent
324
+ // race conditions where a new connection is accepted while we are closing
325
+ // the existing ones
326
+ this.http?.closeAllConnections()
327
+ this.https?.closeAllConnections()
328
+
329
+ ;[...this.sockets].forEach(socket => {
330
+ socket.destroy()
331
+ })
332
+
333
+ await Promise.all([
334
+ pEvent(this.server, 'close'),
335
+ this.http == null ? null : pEvent(this.http, 'close'),
336
+ this.https == null ? null : pEvent(this.https, 'close'),
337
+ pEvent(this.wsServer, 'close')
338
+ ])
339
+
340
+ this.safeDispatchEvent('close')
139
341
  }
140
342
 
141
343
  getAddrs (): Multiaddr[] {
142
- const multiaddrs = []
143
344
  const address = this.server.address()
144
345
 
145
346
  if (address == null) {
@@ -154,38 +355,75 @@ class WebSocketListener extends TypedEventEmitter<ListenerEvents> implements Lis
154
355
  throw new Error('Listener is not ready yet')
155
356
  }
156
357
 
157
- const ipfsId = this.listeningMultiaddr.getPeerId()
158
- const protos = this.listeningMultiaddr.protos()
159
-
160
- // Because TCP will only return the IPv6 version
161
- // we need to capture from the passed multiaddr
162
- if (protos.some(proto => proto.code === protocols('ip4').code)) {
163
- const wsProto = protos.some(proto => proto.code === protocols('ws').code) ? '/ws' : '/wss'
164
- let m = this.listeningMultiaddr.decapsulate('tcp')
165
- m = m.encapsulate(`/tcp/${address.port}${wsProto}`)
166
- if (ipfsId != null) {
167
- m = m.encapsulate(`/p2p/${ipfsId}`)
168
- }
358
+ const options = this.listeningMultiaddr.toOptions()
359
+ const multiaddrs: Multiaddr[] = []
169
360
 
170
- if (m.toString().includes('0.0.0.0')) {
171
- const netInterfaces = os.networkInterfaces()
172
- Object.values(netInterfaces).forEach(niInfos => {
361
+ if (options.family === 4) {
362
+ if (options.host === '0.0.0.0') {
363
+ Object.values(os.networkInterfaces()).forEach(niInfos => {
173
364
  if (niInfos == null) {
174
365
  return
175
366
  }
176
367
 
177
368
  niInfos.forEach(ni => {
178
369
  if (ni.family === 'IPv4') {
179
- multiaddrs.push(multiaddr(m.toString().replace('0.0.0.0', ni.address)))
370
+ multiaddrs.push(multiaddr(`/ip${options.family}/${ni.address}/${options.transport}/${address.port}`))
180
371
  }
181
372
  })
182
373
  })
183
374
  } else {
184
- multiaddrs.push(m)
375
+ multiaddrs.push(multiaddr(`/ip${options.family}/${options.host}/${options.transport}/${address.port}`))
185
376
  }
377
+ } else if (options.family === 6) {
378
+ if (options.host === '::') {
379
+ Object.values(os.networkInterfaces()).forEach(niInfos => {
380
+ if (niInfos == null) {
381
+ return
382
+ }
383
+
384
+ for (const ni of niInfos) {
385
+ if (ni.family !== 'IPv6') {
386
+ continue
387
+ }
388
+
389
+ if (isLinkLocalIp(ni.address)) {
390
+ continue
391
+ }
392
+
393
+ multiaddrs.push(multiaddr(`/ip${options.family}/${ni.address}/${options.transport}/${address.port}`))
394
+ }
395
+ })
396
+ } else {
397
+ multiaddrs.push(multiaddr(`/ip${options.family}/${options.host}/${options.transport}/${address.port}`))
398
+ }
399
+ }
400
+
401
+ const insecureMultiaddrs: Multiaddr[] = []
402
+
403
+ if (this.http != null) {
404
+ multiaddrs.forEach(ma => {
405
+ insecureMultiaddrs.push(ma.encapsulate('/ws'))
406
+ })
407
+ }
408
+
409
+ const secureMultiaddrs: Multiaddr[] = []
410
+
411
+ if (this.https != null) {
412
+ multiaddrs.forEach(ma => {
413
+ secureMultiaddrs.push(ma.encapsulate('/tls/ws'))
414
+ })
186
415
  }
187
416
 
188
- return multiaddrs
417
+ return [
418
+ ...insecureMultiaddrs,
419
+ ...secureMultiaddrs
420
+ ]
421
+ }
422
+
423
+ private httpRequestHandler (req: http.IncomingMessage, res: http.ServerResponse): void {
424
+ res.writeHead(400)
425
+ res.write('Only WebSocket connections are supported')
426
+ res.end()
189
427
  }
190
428
  }
191
429
 
package/LICENSE DELETED
@@ -1,4 +0,0 @@
1
- This project is dual licensed under MIT and Apache-2.0.
2
-
3
- MIT: https://www.opensource.org/licenses/mit
4
- Apache-2.0: https://www.apache.org/licenses/license-2.0