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

Commit ce7bf4f

Browse files
vasco-santosjacobheun
authored andcommittedSep 30, 2019
refactor: async with multiaddr conn (#92)
BREAKING CHANGE: Switch to using async/await and async iterators. The transport and connection interfaces have changed. See the README for new usage.
1 parent 5a9434b commit ce7bf4f

15 files changed

+522
-467
lines changed
 

‎.aegir.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
'use strict'
22

33
const multiaddr = require('multiaddr')
4-
const pull = require('pull-stream')
5-
4+
const pipe = require('it-pipe')
65
const WS = require('./src')
76

7+
const mockUpgrader = {
8+
upgradeInbound: maConn => maConn,
9+
upgradeOutbound: maConn => maConn
10+
}
811
let listener
912

1013
function boot (done) {
11-
const ws = new WS()
14+
const ws = new WS({ upgrader: mockUpgrader })
1215
const ma = multiaddr('/ip4/127.0.0.1/tcp/9095/ws')
13-
listener = ws.createListener((conn) => pull(conn, conn))
14-
listener.listen(ma, done)
16+
listener = ws.createListener(conn => pipe(conn, conn))
17+
listener.listen(ma).then(() => done()).catch(done)
18+
listener.on('error', console.error)
1519
}
1620

1721
function shutdown (done) {
18-
listener.close(done)
22+
listener.close().then(done).catch(done)
1923
}
2024

2125
module.exports = {

‎.gitignore

+2-41
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,4 @@
1-
docs
1+
node_modules
22
package-lock.json
3-
yarn.lock
4-
5-
# Logs
6-
logs
7-
*.log
8-
npm-debug.log*
9-
10-
# Runtime data
11-
pids
12-
*.pid
13-
*.seed
14-
15-
# Directory for instrumented libs generated by jscoverage/JSCover
16-
lib-cov
17-
18-
# Coverage directory used by tools like istanbul
193
coverage
20-
21-
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
22-
.grunt
23-
24-
# node-waf configuration
25-
.lock-wscript
26-
27-
# Compiled binary addons (http://nodejs.org/api/addons.html)
28-
build/Release
29-
30-
# Dependency directory
31-
node_modules
32-
33-
# Optional npm cache directory
34-
.npm
35-
36-
# Optional REPL history
37-
.node_repl_history
38-
39-
# Vim editor swap files
40-
*.swp
41-
42-
dist
43-
4+
.nyc_output

‎.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424

2525
- stage: check
2626
script:
27-
- npx aegir commitlint --travis
27+
- npx aegir build --bundlesize
2828
- npx aegir dep-check -- -i wrtc -i electron-webrtc
2929
- npm run lint
3030

‎README.md

+18-22
Original file line numberDiff line numberDiff line change
@@ -40,37 +40,33 @@
4040
```js
4141
const WS = require('libp2p-websockets')
4242
const multiaddr = require('multiaddr')
43-
const pull = require('pull-stream')
43+
const pipe = require('it-pipe')
44+
const { collect } = require('streaming-iterables')
4445

45-
const mh = multiaddr('/ip4/0.0.0.0/tcp/9090/ws')
46+
const addr = multiaddr('/ip4/0.0.0.0/tcp/9090/ws')
4647

47-
const ws = new WS()
48+
const ws = new WS({ upgrader })
4849

4950
const listener = ws.createListener((socket) => {
5051
console.log('new connection opened')
51-
pull(
52-
pull.values(['hello']),
52+
pipe(
53+
['hello'],
5354
socket
5455
)
5556
})
5657

57-
listener.listen(mh, () => {
58-
console.log('listening')
59-
60-
pull(
61-
ws.dial(mh),
62-
pull.collect((err, values) => {
63-
if (!err) {
64-
console.log(`Value: ${values.toString()}`)
65-
} else {
66-
console.log(`Error: ${err}`)
67-
}
68-
69-
// Close connection after reading
70-
listener.close()
71-
}),
72-
)
73-
})
58+
await listener.listen(addr)
59+
console.log('listening')
60+
61+
const socket = await ws.dial(addr)
62+
const values = await pipe(
63+
socket,
64+
collect
65+
)
66+
console.log(`Value: ${values.toString()}`)
67+
68+
// Close connection after reading
69+
await listener.close()
7470
```
7571

7672
## API

‎ci/Jenkinsfile

-2
This file was deleted.

‎package.json

+15-12
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
"release": "aegir release -t node -t browser ",
1414
"release-minor": "aegir release --type minor -t node -t browser",
1515
"release-major": "aegir release --type major -t node -t browser",
16-
"coverage": "aegir coverage",
17-
"coverage-publish": "aegir coverage --provider coveralls"
16+
"coverage": "nyc --reporter=lcov --reporter=text npm run test:node"
1817
},
1918
"browser": {
2019
"src/listener": "./src/listener.browser.js"
@@ -24,8 +23,7 @@
2423
"dist"
2524
],
2625
"pre-push": [
27-
"lint",
28-
"test"
26+
"lint"
2927
],
3028
"repository": {
3129
"type": "git",
@@ -40,21 +38,26 @@
4038
},
4139
"homepage": "https://github.com/libp2p/js-libp2p-websockets#readme",
4240
"dependencies": {
41+
"abortable-iterator": "^2.1.0",
4342
"class-is": "^1.1.0",
4443
"debug": "^4.1.1",
45-
"interface-connection": "~0.3.3",
46-
"mafmt": "^6.0.7",
44+
"err-code": "^2.0.0",
45+
"it-ws": "vasco-santos/it-ws#v2.1.1-rc.0",
46+
"libp2p-utils": "~0.1.0",
47+
"mafmt": "^7.0.0",
48+
"multiaddr": "^7.1.0",
4749
"multiaddr-to-uri": "^5.0.0",
48-
"pull-ws": "hugomrdias/pull-ws#fix/bundle-size"
50+
"p-timeout": "^3.2.0"
4951
},
5052
"devDependencies": {
51-
"aegir": "^20.0.0",
53+
"abort-controller": "^3.0.0",
54+
"aegir": "^20.3.1",
5255
"chai": "^4.2.0",
5356
"dirty-chai": "^2.0.1",
54-
"interface-transport": "~0.3.7",
55-
"multiaddr": "^6.0.6",
56-
"pull-goodbye": "0.0.2",
57-
"pull-stream": "^3.6.9"
57+
"interface-transport": "^0.7.0",
58+
"it-goodbye": "^2.0.1",
59+
"it-pipe": "^1.0.1",
60+
"streaming-iterables": "^4.1.0"
5861
},
5962
"contributors": [
6063
"Chris Campbell <christopher.d.campbell@gmail.com>",

‎src/constants.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use strict'
2+
3+
// p2p multi-address code
4+
exports.CODE_P2P = 421
5+
exports.CODE_CIRCUIT = 290
6+
7+
// Time to wait for a connection to close gracefully before destroying it manually
8+
exports.CLOSE_TIMEOUT = 2000

‎src/index.js

+100-33
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,135 @@
11
'use strict'
22

3-
const connect = require('pull-ws/client')
3+
const connect = require('it-ws/client')
44
const mafmt = require('mafmt')
55
const withIs = require('class-is')
6-
const Connection = require('interface-connection').Connection
7-
86
const toUri = require('multiaddr-to-uri')
9-
const debug = require('debug')
10-
const log = debug('libp2p:websockets:dialer')
7+
const { AbortError } = require('abortable-iterator')
8+
9+
const log = require('debug')('libp2p:websockets')
10+
const assert = require('assert')
1111

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

16+
/**
17+
* @class WebSockets
18+
*/
1419
class WebSockets {
15-
dial (ma, options, callback) {
16-
if (typeof options === 'function') {
17-
callback = options
18-
options = {}
20+
/**
21+
* @constructor
22+
* @param {object} options
23+
* @param {Upgrader} options.upgrader
24+
*/
25+
constructor ({ upgrader }) {
26+
assert(upgrader, 'An upgrader must be provided. See https://github.com/libp2p/interface-transport#upgrader.')
27+
this._upgrader = upgrader
28+
}
29+
30+
/**
31+
* @async
32+
* @param {Multiaddr} ma
33+
* @param {object} [options]
34+
* @param {AbortSignal} [options.signal] Used to abort dial requests
35+
* @returns {Connection} An upgraded Connection
36+
*/
37+
async dial (ma, options = {}) {
38+
log('dialing %s', ma)
39+
40+
const socket = await this._connect(ma, options)
41+
const maConn = toConnection(socket, { remoteAddr: ma, signal: options.signal })
42+
log('new outbound connection %s', maConn.remoteAddr)
43+
44+
const conn = await this._upgrader.upgradeOutbound(maConn)
45+
log('outbound connection %s upgraded', maConn.remoteAddr)
46+
return conn
47+
}
48+
49+
/**
50+
* @private
51+
* @param {Multiaddr} ma
52+
* @param {object} [options]
53+
* @param {AbortSignal} [options.signal] Used to abort dial requests
54+
* @returns {Promise<WebSocket>} Resolves a extended duplex iterable on top of a WebSocket
55+
*/
56+
async _connect (ma, options = {}) {
57+
if (options.signal && options.signal.aborted) {
58+
throw new AbortError()
1959
}
60+
const cOpts = ma.toOptions()
61+
log('dialing %s:%s', cOpts.host, cOpts.port)
62+
63+
const rawSocket = connect(toUri(ma), Object.assign({ binary: true }, options))
2064

21-
callback = callback || function () { }
65+
if (!options.signal) {
66+
await rawSocket.connected()
2267

23-
const url = toUri(ma)
24-
log('dialing %s', url)
25-
const socket = connect(url, {
26-
binary: true,
27-
onConnect: (err) => {
28-
callback(err)
68+
log('connected %s', ma)
69+
return rawSocket
70+
}
71+
72+
// Allow abort via signal during connect
73+
let onAbort
74+
const abort = new Promise((resolve, reject) => {
75+
onAbort = () => {
76+
reject(new AbortError())
77+
rawSocket.close()
2978
}
79+
80+
// Already aborted?
81+
if (options.signal.aborted) return onAbort()
82+
options.signal.addEventListener('abort', onAbort)
3083
})
3184

32-
const conn = new Connection(socket)
33-
conn.getObservedAddrs = (cb) => cb(null, [ma])
34-
conn.close = (cb) => socket.close(cb)
85+
try {
86+
await Promise.race([abort, rawSocket.connected()])
87+
} finally {
88+
options.signal.removeEventListener('abort', onAbort)
89+
}
3590

36-
return conn
91+
log('connected %s', ma)
92+
return rawSocket
3793
}
3894

39-
createListener (options, handler) {
95+
/**
96+
* Creates a Websockets listener. The provided `handler` function will be called
97+
* anytime a new incoming Connection has been successfully upgraded via
98+
* `upgrader.upgradeInbound`.
99+
* @param {object} [options]
100+
* @param {http.Server} [options.server] A pre-created Node.js HTTP/S server.
101+
* @param {function (Connection)} handler
102+
* @returns {Listener} A Websockets listener
103+
*/
104+
createListener (options = {}, handler) {
40105
if (typeof options === 'function') {
41106
handler = options
42107
options = {}
43108
}
44109

45-
return createListener(options, handler)
110+
return createListener({ handler, upgrader: this._upgrader }, options)
46111
}
47112

113+
/**
114+
* Takes a list of `Multiaddr`s and returns only valid Websockets addresses
115+
* @param {Multiaddr[]} multiaddrs
116+
* @returns {Multiaddr[]} Valid Websockets multiaddrs
117+
*/
48118
filter (multiaddrs) {
49-
if (!Array.isArray(multiaddrs)) {
50-
multiaddrs = [multiaddrs]
51-
}
119+
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
52120

53121
return multiaddrs.filter((ma) => {
54-
if (ma.protoNames().includes('p2p-circuit')) {
122+
if (ma.protoCodes().includes(CODE_CIRCUIT)) {
55123
return false
56124
}
57125

58-
if (ma.protoNames().includes('ipfs')) {
59-
ma = ma.decapsulate('ipfs')
60-
}
61-
62-
return mafmt.WebSockets.matches(ma) ||
63-
mafmt.WebSocketsSecure.matches(ma)
126+
return mafmt.WebSockets.matches(ma.decapsulateCode(CODE_P2P)) ||
127+
mafmt.WebSocketsSecure.matches(ma.decapsulateCode(CODE_P2P))
64128
})
65129
}
66130
}
67131

68-
module.exports = withIs(WebSockets, { className: 'WebSockets', symbolName: '@libp2p/js-libp2p-websockets/websockets' })
132+
module.exports = withIs(WebSockets, {
133+
className: 'WebSockets',
134+
symbolName: '@libp2p/js-libp2p-websockets/websockets'
135+
})

0 commit comments

Comments
 (0)
This repository has been archived.