Skip to content

Commit 9106d68

Browse files
authored
feat: add support for unix multiaddr listen (#10)
BREAKING CHANGE: The --sock param/flag has been replaced by --listen, which now expects a multiaddr string. Example: `jsp2pd --sock=/tmp/p2p.sock` would now be `jsp2pd --listen=/unix/tmp/p2p.sock` * feat: add support for unix multiaddr listen * feat: add support for hostAddrs flag * feat: add support for websockets * feat: add announceAddrs support * test: split up tests into files * feat: use multiaddr instead of path for everything * feat: update stream handler to use multiaddr bytes * chore: fix lint * chore: update multiaddr dep * test: fix test runners * fix: add a default host address * fix: catch decapsulate errors when no ipfs present * chore: fix feedback
1 parent c131161 commit 9106d68

15 files changed

+916
-600
lines changed

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
"scripts": {
1919
"lint": "aegir lint",
2020
"build": "aegir build",
21-
"test": "aegir test -t node -f test/**/*.js",
22-
"test:node": "aegir test -t node -f test/**/*.js",
21+
"test": "aegir test -t node -f test/*.js test/**/*.js",
22+
"test:node": "aegir test -t node -f test/*.js test/**/*.js",
2323
"release": "aegir release --no-build -t node",
2424
"release-minor": "aegir release --no-build --type minor -t node",
2525
"release-major": "aegir release --no-build --type major -t node"
@@ -54,7 +54,8 @@
5454
"libp2p-mplex": "~0.8.4",
5555
"libp2p-secio": "~0.11.1",
5656
"libp2p-tcp": "~0.13.0",
57-
"multiaddr": "^6.0.3",
57+
"libp2p-websockets": "~0.12.2",
58+
"multiaddr": "^6.0.5",
5859
"peer-book": "~0.9.0",
5960
"peer-id": "~0.12.0",
6061
"peer-info": "~0.15.0",

src/cli/bin.js

+13-3
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ const log = console.log
1212

1313
const main = async (processArgs) => {
1414
parser.yargs
15-
.option('sock', {
16-
desc: 'daemon control socket path',
15+
.option('listen', {
16+
desc: 'daemon control listen multiaddr',
1717
type: 'string',
18-
default: '/tmp/p2pd.sock'
18+
default: '/unix/tmp/p2pd.sock'
1919
})
2020
.option('quiet', {
2121
alias: 'q',
@@ -28,6 +28,16 @@ const main = async (processArgs) => {
2828
type: 'string',
2929
default: ''
3030
})
31+
.option('hostAddrs', {
32+
desc: 'Comma separated list of multiaddrs the host should listen on',
33+
type: 'string',
34+
default: ''
35+
})
36+
.option('announceAddrs', {
37+
desc: 'Comma separated list of multiaddrs the host should announce to the network',
38+
type: 'string',
39+
default: ''
40+
})
3141
.option('bootstrap', {
3242
alias: 'b',
3343
desc: 'Connects to bootstrap peers and bootstraps the dht if enabled',

src/client.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22

33
const net = require('net')
44
const Socket = net.Socket
5-
const path = require('path')
65
const { encode, decode } = require('length-prefixed-stream')
76
const { Request } = require('./protocol')
87
const LIMIT = 1 << 22 // 4MB
98

10-
const { ends } = require('../src/util')
9+
const { ends, multiaddrToNetConfig } = require('./util')
1110

1211
class Client {
13-
constructor (socketPath) {
14-
this.path = path.resolve(socketPath)
12+
constructor (addr) {
13+
this.multiaddr = addr
1514
this.server = null
1615
this.socket = new Socket({
1716
readable: true,
@@ -27,7 +26,8 @@ class Client {
2726
*/
2827
attach () {
2928
return new Promise((resolve, reject) => {
30-
this.socket.connect(this.path, (err) => {
29+
const options = multiaddrToNetConfig(this.multiaddr)
30+
this.socket.connect(options, (err) => {
3131
if (err) return reject(err)
3232
resolve()
3333
})
@@ -37,11 +37,11 @@ class Client {
3737
/**
3838
* Starts a server listening at `socketPath`. New connections
3939
* will be sent to the `connectionHandler`.
40-
* @param {string} socketPath
40+
* @param {Multiaddr} addr
4141
* @param {function(Stream)} connectionHandler
4242
* @returns {Promise}
4343
*/
44-
async startServer (socketPath, connectionHandler) {
44+
async startServer (addr, connectionHandler) {
4545
if (this.server) {
4646
await this.stopServer()
4747
}
@@ -50,7 +50,8 @@ class Client {
5050
allowHalfOpen: true
5151
}, connectionHandler)
5252

53-
this.server.listen(path.resolve(socketPath), (err) => {
53+
const options = multiaddrToNetConfig(addr)
54+
this.server.listen(options, (err) => {
5455
if (err) return reject(err)
5556
resolve()
5657
})

src/daemon.js

+26-15
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
'use strict'
22

33
const net = require('net')
4-
const path = require('path')
54
const Libp2p = require('./libp2p')
65
const PeerInfo = require('peer-info')
76
const PeerId = require('peer-id')
8-
const multiaddr = require('multiaddr')
7+
const ma = require('multiaddr')
98
const CID = require('cids')
109
const { encode, decode } = require('length-prefixed-stream')
10+
const { multiaddrToNetConfig } = require('./util')
1111
const {
1212
Request,
1313
DHTRequest,
@@ -23,14 +23,14 @@ class Daemon {
2323
/**
2424
* @constructor
2525
* @param {object} options
26-
* @param {string} options.socketPath
26+
* @param {string} options.multiaddr
2727
* @param {Libp2p} options.libp2pNode
2828
*/
2929
constructor ({
30-
socketPath,
30+
multiaddr,
3131
libp2pNode
3232
}) {
33-
this.socketPath = socketPath
33+
this.multiaddr = ma(multiaddr)
3434
this.libp2p = libp2pNode
3535
this.server = net.createServer({
3636
allowHalfOpen: true
@@ -53,7 +53,7 @@ class Daemon {
5353
PeerId.createFromBytes(peer)
5454
)
5555
addrs.forEach((a) => {
56-
peerInfo.multiaddrs.add(multiaddr(a))
56+
peerInfo.multiaddrs.add(ma(a))
5757
})
5858

5959
return this.libp2p.dial(peerInfo)
@@ -116,15 +116,16 @@ class Daemon {
116116
registerStreamHandler (request) {
117117
return new Promise((resolve, reject) => {
118118
const protocols = request.streamHandler.proto
119-
const socketPath = path.resolve(request.streamHandler.path)
119+
const addr = ma(request.streamHandler.addr)
120+
const addrString = addr.toString()
120121

121122
// If we have a handler, end it
122-
if (this.streamHandlers[socketPath]) {
123-
this.streamHandlers[socketPath].end()
124-
delete this.streamHandlers[socketPath]
123+
if (this.streamHandlers[addrString]) {
124+
this.streamHandlers[addrString].end()
125+
delete this.streamHandlers[addrString]
125126
}
126127

127-
const socket = this.streamHandlers[socketPath] = new net.Socket({
128+
const socket = this.streamHandlers[addrString] = new net.Socket({
128129
readable: true,
129130
writable: true,
130131
allowHalfOpen: true
@@ -153,7 +154,8 @@ class Daemon {
153154
})
154155
})
155156

156-
socket.connect(socketPath, (err) => {
157+
const options = multiaddrToNetConfig(addr)
158+
socket.connect(options, (err) => {
157159
if (err) return reject(err)
158160
resolve()
159161
})
@@ -181,7 +183,8 @@ class Daemon {
181183
async start () {
182184
await this.libp2p.start()
183185
return new Promise((resolve, reject) => {
184-
this.server.listen(path.resolve(this.socketPath), (err) => {
186+
const options = multiaddrToNetConfig(this.multiaddr)
187+
this.server.listen(options, (err) => {
185188
if (err) return reject(err)
186189
resolve()
187190
})
@@ -368,7 +371,15 @@ class Daemon {
368371
id: this.libp2p.peerInfo.id.toBytes(),
369372
// temporary removal of "/ipfs/..." from multiaddrs
370373
// this will be solved in: https://github.com/libp2p/js-libp2p/issues/323
371-
addrs: this.libp2p.peerInfo.multiaddrs.toArray().map(m => m.decapsulate('ipfs').buffer)
374+
addrs: this.libp2p.peerInfo.multiaddrs.toArray().map(m => {
375+
let buffer
376+
try {
377+
buffer = m.decapsulate('ipfs').buffer
378+
} catch (_) {
379+
buffer = m.buffer
380+
}
381+
return buffer
382+
})
372383
}
373384
}))
374385
break
@@ -486,7 +497,7 @@ function ErrorResponse (message) {
486497
const createDaemon = async (options) => {
487498
const libp2pNode = await Libp2p.createLibp2p(options)
488499
const daemon = new Daemon({
489-
socketPath: options.sock,
500+
multiaddr: options.listen,
490501
libp2pNode: libp2pNode
491502
})
492503

src/libp2p.js

+29-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const Libp2p = require('libp2p')
44
const TCP = require('libp2p-tcp')
5+
const WS = require('libp2p-websockets')
56
const Bootstrap = require('libp2p-bootstrap')
67
const MPLEX = require('libp2p-mplex')
78
const SECIO = require('libp2p-secio')
@@ -199,6 +200,10 @@ class DHT {
199200
}
200201

201202
class DaemonLibp2p extends Libp2p {
203+
constructor (libp2pOpts, { announceAddrs }) {
204+
super(libp2pOpts)
205+
this.announceAddrs = announceAddrs
206+
}
202207
get contentRouting () {
203208
return this._contentRouting
204209
}
@@ -227,6 +232,14 @@ class DaemonLibp2p extends Libp2p {
227232
return new Promise((resolve, reject) => {
228233
super.start((err) => {
229234
if (err) return reject(err)
235+
236+
// replace with announce addrs until libp2p supports this directly
237+
if (this.announceAddrs.length > 0) {
238+
this.peerInfo.multiaddrs.clear()
239+
this.announceAddrs.forEach(addr => {
240+
this.peerInfo.multiaddrs.add(addr)
241+
})
242+
}
230243
resolve()
231244
})
232245
})
@@ -295,27 +308,34 @@ class DaemonLibp2p extends Libp2p {
295308
* @param {boolean} opts.connMgr
296309
* @param {number} opts.connMgrLo
297310
* @param {number} opts.connMgrHi
298-
* @param {string} opts.sock
299311
* @param {string} opts.id
300312
* @param {string} opts.bootstrapPeers
313+
* @param {string} opts.hostAddrs
301314
* @returns {Libp2p}
302315
*/
303316
const createLibp2p = async ({
304317
bootstrap,
305318
bootstrapPeers,
319+
hostAddrs,
320+
announceAddrs,
306321
dht,
307322
dhtClient,
308323
connMgr,
309324
connMgrLo,
310325
connMgrHi,
311-
sock,
312326
id
313327
} = {}) => {
314328
const peerInfo = await getPeerInfo(id)
315329
const peerBook = new PeerBook()
316330
const bootstrapList = bootstrapPeers ? bootstrapPeers.split(',').filter(s => s !== '') : null
331+
const listenAddrs = hostAddrs ? hostAddrs.split(',').filter(s => s !== '') : ['/ip4/0.0.0.0/tcp/0']
317332

318-
peerInfo.multiaddrs.add(multiaddr('/ip4/0.0.0.0/tcp/0'))
333+
announceAddrs = announceAddrs ? announceAddrs.split(',').filter(s => s !== '') : []
334+
announceAddrs = announceAddrs.map(addr => multiaddr(addr))
335+
336+
listenAddrs.forEach(addr => {
337+
peerInfo.multiaddrs.add(multiaddr(addr))
338+
})
319339

320340
const libp2p = new DaemonLibp2p({
321341
peerBook,
@@ -326,7 +346,8 @@ const createLibp2p = async ({
326346
},
327347
modules: {
328348
transport: [
329-
TCP
349+
TCP,
350+
WS
330351
],
331352
streamMuxer: [
332353
MPLEX
@@ -362,6 +383,10 @@ const createLibp2p = async ({
362383
pubsub: false
363384
}
364385
}
386+
}, {
387+
// using a secondary config until https://github.com/libp2p/js-libp2p/issues/202
388+
// is completed
389+
announceAddrs
365390
})
366391

367392
return libp2p

src/protocol/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ message StreamOpenRequest {
5757
}
5858
5959
message StreamHandlerRequest {
60-
required string path = 1;
60+
required bytes addr = 1;
6161
repeated string proto = 2;
6262
}
6363

src/util/index.js

+21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
'use strict'
22

3+
const os = require('os')
4+
const { resolve } = require('path')
5+
36
exports.first = async iterator => {
47
for await (const value of iterator) return value
58
}
@@ -15,3 +18,21 @@ exports.ends = iterator => {
1518
iterator.last = () => exports.last(iterator)
1619
return iterator
1720
}
21+
22+
/**
23+
* Converts the multiaddr to a nodejs NET compliant option
24+
* for .connect or .listen
25+
* @param {Multiaddr} addr
26+
* @returns {string|object} A nodejs NET compliant option
27+
*/
28+
exports.multiaddrToNetConfig = function multiaddrToNetConfig (addr) {
29+
const listenPath = addr.getPath()
30+
// unix socket listening
31+
if (listenPath) {
32+
return resolve(listenPath)
33+
}
34+
// tcp listening
35+
return addr.nodeAddress()
36+
}
37+
38+
exports.isWindows = os.platform() === 'win32'

0 commit comments

Comments
 (0)