Skip to content
This repository was archived by the owner on Jul 21, 2023. It is now read-only.

Commit 711c721

Browse files
authored
feat: custom address filter (#116)
* feat: custom address filter BREAKING CHANGE: Only DNS+WSS addresses are now returned on filter by default in the browser. This can be overritten by the filter option and filters are provided in the module.
1 parent 662d041 commit 711c721

File tree

8 files changed

+253
-57
lines changed

8 files changed

+253
-57
lines changed

README.md

+46-23
Original file line numberDiff line numberDiff line change
@@ -35,40 +35,63 @@
3535
> npm i libp2p-websockets
3636
```
3737

38-
### Example
38+
### Constructor properties
3939

4040
```js
4141
const WS = require('libp2p-websockets')
42-
const multiaddr = require('multiaddr')
43-
const pipe = require('it-pipe')
44-
const { collect } = require('streaming-iterables')
4542

46-
const addr = multiaddr('/ip4/0.0.0.0/tcp/9090/ws')
43+
const properties = {
44+
upgrader,
45+
filter
46+
}
4747

48-
const ws = new WS({ upgrader })
48+
const ws = new WS(properties)
49+
```
4950

50-
const listener = ws.createListener((socket) => {
51-
console.log('new connection opened')
52-
pipe(
53-
['hello'],
54-
socket
55-
)
56-
})
51+
| Name | Type | Description | Default |
52+
|------|------|-------------|---------|
53+
| upgrader | [`Upgrader`](https://github.com/libp2p/interface-transport#upgrader) | connection upgrader object with `upgradeOutbound` and `upgradeInbound` | **REQUIRED** |
54+
| filter | `(multiaddrs: Array<Multiaddr>) => Array<Multiaddr>` | override transport addresses filter | **Browser:** DNS+WSS multiaddrs / **Node.js:** DNS+{WS, WSS} multiaddrs |
55+
56+
You can create your own address filters for this transports, or rely in the filters [provided](./src/filters.js).
57+
58+
The available filters are:
5759

58-
await listener.listen(addr)
59-
console.log('listening')
60+
- `filters.all`
61+
- Returns all TCP and DNS based addresses, both with `ws` or `wss`.
62+
- `filters.dnsWss`
63+
- Returns all DNS based addresses with `wss`.
64+
- `filters.dnsWsOrWss`
65+
- Returns all DNS based addresses, both with `ws` or `wss`.
6066

61-
const socket = await ws.dial(addr)
62-
const values = await pipe(
63-
socket,
64-
collect
65-
)
66-
console.log(`Value: ${values.toString()}`)
67+
## Libp2p Usage Example
6768

68-
// Close connection after reading
69-
await listener.close()
69+
```js
70+
const Libp2p = require('libp2p')
71+
const Websockets = require('libp2p-websockets')
72+
const filters = require('libp2p-websockets/src/filters')
73+
const MPLEX = require('libp2p-mplex')
74+
const { NOISE } = require('libp2p-noise')
75+
76+
const transportKey = Websockets.prototype[Symbol.toStringTag]
77+
const node = await Libp2p.create({
78+
modules: {
79+
transport: [Websockets],
80+
streamMuxer: [MPLEX],
81+
connEncryption: [NOISE]
82+
},
83+
config: {
84+
transport: {
85+
[transportKey]: { // Transport properties -- Libp2p upgrader is automatically added
86+
filter: filters.dnsWsOrWss
87+
}
88+
}
89+
}
90+
})
7091
```
7192

93+
For more information see [libp2p/js-libp2p/doc/CONFIGURATION.md#customizing-transports](https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#customizing-transports).
94+
7295
## API
7396

7497
### Transport

package.json

+10-8
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,24 @@
4040
"dependencies": {
4141
"abortable-iterator": "^3.0.0",
4242
"class-is": "^1.1.0",
43-
"debug": "^4.1.1",
44-
"err-code": "^2.0.0",
45-
"it-ws": "^3.0.0",
46-
"libp2p-utils": "^0.2.0",
47-
"mafmt": "^8.0.0",
48-
"multiaddr": "^8.0.0",
43+
"debug": "^4.2.0",
44+
"err-code": "^2.0.3",
45+
"ipfs-utils": "^4.0.1",
46+
"it-ws": "^3.0.2",
47+
"libp2p-utils": "^0.2.1",
48+
"mafmt": "^8.0.1",
49+
"multiaddr": "^8.1.1",
4950
"multiaddr-to-uri": "^6.0.0",
5051
"p-timeout": "^3.2.0"
5152
},
5253
"devDependencies": {
5354
"abort-controller": "^3.0.0",
54-
"aegir": "^25.0.0",
55+
"aegir": "^28.1.0",
5556
"bl": "^4.0.0",
57+
"is-loopback-addr": "^1.0.1",
5658
"it-goodbye": "^2.0.1",
5759
"it-pipe": "^1.0.1",
58-
"libp2p-interfaces": "^0.4.0",
60+
"libp2p-interfaces": "^0.7.1",
5961
"streaming-iterables": "^5.0.2",
6062
"uint8arrays": "^1.1.0"
6163
},

src/constants.js

+4
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,9 @@
44
exports.CODE_P2P = 421
55
exports.CODE_CIRCUIT = 290
66

7+
exports.CODE_TCP = 6
8+
exports.CODE_WS = 477
9+
exports.CODE_WSS = 478
10+
711
// Time to wait for a connection to close gracefully before destroying it manually
812
exports.CLOSE_TIMEOUT = 2000

src/filters.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use strict'
2+
3+
const mafmt = require('mafmt')
4+
const {
5+
CODE_CIRCUIT,
6+
CODE_P2P,
7+
CODE_TCP,
8+
CODE_WS,
9+
CODE_WSS
10+
} = require('./constants')
11+
12+
module.exports = {
13+
all: (multiaddrs) => multiaddrs.filter((ma) => {
14+
if (ma.protoCodes().includes(CODE_CIRCUIT)) {
15+
return false
16+
}
17+
18+
const testMa = ma.decapsulateCode(CODE_P2P)
19+
20+
return mafmt.WebSockets.matches(testMa) ||
21+
mafmt.WebSocketsSecure.matches(testMa)
22+
}),
23+
dnsWss: (multiaddrs) => multiaddrs.filter((ma) => {
24+
if (ma.protoCodes().includes(CODE_CIRCUIT)) {
25+
return false
26+
}
27+
28+
const testMa = ma.decapsulateCode(CODE_P2P)
29+
30+
return mafmt.WebSocketsSecure.matches(testMa) &&
31+
mafmt.DNS.matches(testMa.decapsulateCode(CODE_TCP).decapsulateCode(CODE_WSS))
32+
}),
33+
dnsWsOrWss: (multiaddrs) => multiaddrs.filter((ma) => {
34+
if (ma.protoCodes().includes(CODE_CIRCUIT)) {
35+
return false
36+
}
37+
38+
const testMa = ma.decapsulateCode(CODE_P2P)
39+
40+
// WS
41+
if (mafmt.WebSockets.matches(testMa)) {
42+
return mafmt.DNS.matches(testMa.decapsulateCode(CODE_TCP).decapsulateCode(CODE_WS))
43+
}
44+
45+
// WSS
46+
return mafmt.WebSocketsSecure.matches(testMa) &&
47+
mafmt.DNS.matches(testMa.decapsulateCode(CODE_TCP).decapsulateCode(CODE_WSS))
48+
})
49+
}

src/index.js

+23-15
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,40 @@
11
'use strict'
22

33
const connect = require('it-ws/client')
4-
const mafmt = require('mafmt')
54
const withIs = require('class-is')
65
const toUri = require('multiaddr-to-uri')
76
const { AbortError } = require('abortable-iterator')
87

98
const log = require('debug')('libp2p:websockets')
9+
const env = require('ipfs-utils/src/env')
1010

1111
const createListener = require('./listener')
1212
const toConnection = require('./socket-to-conn')
13-
const { CODE_CIRCUIT, CODE_P2P } = require('./constants')
13+
const filters = require('./filters')
1414

1515
/**
1616
* @class WebSockets
1717
*/
1818
class WebSockets {
1919
/**
20-
* @constructor
20+
* @class
2121
* @param {object} options
2222
* @param {Upgrader} options.upgrader
23+
* @param {(multiaddrs: Array<Multiaddr>) => Array<Multiaddr>} options.filter - override transport addresses filter
2324
*/
24-
constructor ({ upgrader }) {
25+
constructor ({ upgrader, filter }) {
2526
if (!upgrader) {
2627
throw new Error('An upgrader must be provided. See https://github.com/libp2p/interface-transport#upgrader.')
2728
}
2829
this._upgrader = upgrader
30+
this._filter = filter
2931
}
3032

3133
/**
3234
* @async
3335
* @param {Multiaddr} ma
3436
* @param {object} [options]
35-
* @param {AbortSignal} [options.signal] Used to abort dial requests
37+
* @param {AbortSignal} [options.signal] - Used to abort dial requests
3638
* @returns {Connection} An upgraded Connection
3739
*/
3840
async dial (ma, options = {}) {
@@ -51,7 +53,7 @@ class WebSockets {
5153
* @private
5254
* @param {Multiaddr} ma
5355
* @param {object} [options]
54-
* @param {AbortSignal} [options.signal] Used to abort dial requests
56+
* @param {AbortSignal} [options.signal] - Used to abort dial requests
5557
* @returns {Promise<WebSocket>} Resolves a extended duplex iterable on top of a WebSocket
5658
*/
5759
async _connect (ma, options = {}) {
@@ -97,8 +99,9 @@ class WebSockets {
9799
* Creates a Websockets listener. The provided `handler` function will be called
98100
* anytime a new incoming Connection has been successfully upgraded via
99101
* `upgrader.upgradeInbound`.
102+
*
100103
* @param {object} [options]
101-
* @param {http.Server} [options.server] A pre-created Node.js HTTP/S server.
104+
* @param {http.Server} [options.server] - A pre-created Node.js HTTP/S server.
102105
* @param {function (Connection)} handler
103106
* @returns {Listener} A Websockets listener
104107
*/
@@ -112,21 +115,26 @@ class WebSockets {
112115
}
113116

114117
/**
115-
* Takes a list of `Multiaddr`s and returns only valid Websockets addresses
118+
* Takes a list of `Multiaddr`s and returns only valid Websockets addresses.
119+
* By default, in a browser environment only DNS+WSS multiaddr is accepted,
120+
* while in a Node.js environment DNS+{WS, WSS} multiaddrs are accepted.
121+
*
116122
* @param {Multiaddr[]} multiaddrs
117123
* @returns {Multiaddr[]} Valid Websockets multiaddrs
118124
*/
119125
filter (multiaddrs) {
120126
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
121127

122-
return multiaddrs.filter((ma) => {
123-
if (ma.protoCodes().includes(CODE_CIRCUIT)) {
124-
return false
125-
}
128+
if (this._filter) {
129+
return this._filter(multiaddrs)
130+
}
126131

127-
return mafmt.WebSockets.matches(ma.decapsulateCode(CODE_P2P)) ||
128-
mafmt.WebSocketsSecure.matches(ma.decapsulateCode(CODE_P2P))
129-
})
132+
// Browser
133+
if (env.isBrowser || env.isWebWorker) {
134+
return filters.dnsWss(multiaddrs)
135+
}
136+
137+
return filters.all(multiaddrs)
130138
}
131139
}
132140

test/browser.js

+10
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ describe('libp2p-websockets', () => {
3434
expect(results).to.eql([message])
3535
})
3636

37+
it('should filter out no DNS websocket addresses', function () {
38+
const ma1 = multiaddr('/ip4/127.0.0.1/tcp/80/ws')
39+
const ma2 = multiaddr('/ip4/127.0.0.1/tcp/443/wss')
40+
const ma3 = multiaddr('/ip6/::1/tcp/80/ws')
41+
const ma4 = multiaddr('/ip6/::1/tcp/443/wss')
42+
43+
const valid = ws.filter([ma1, ma2, ma3, ma4])
44+
expect(valid.length).to.equal(0)
45+
})
46+
3747
describe('stress', () => {
3848
it('one big write', async () => {
3949
const rawMessage = new Uint8Array(1000000).fill('a')

test/compliance.node.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ const tests = require('libp2p-interfaces/src/transport/tests')
55
const multiaddr = require('multiaddr')
66
const http = require('http')
77
const WS = require('../src')
8+
const filters = require('../src/filters')
89

910
describe('interface-transport compliance', () => {
1011
tests({
1112
async setup ({ upgrader }) { // eslint-disable-line require-await
12-
const ws = new WS({ upgrader })
13+
const ws = new WS({ upgrader, filter: filters.all })
1314
const addrs = [
1415
multiaddr('/ip4/127.0.0.1/tcp/9091/ws'),
1516
multiaddr('/ip4/127.0.0.1/tcp/9092/ws'),

0 commit comments

Comments
 (0)