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

Commit d76a1f2

Browse files
authored
feat: add path multiaddr support (#118)
* chore: update deps * test: skip paths on windows
1 parent 7702646 commit d76a1f2

File tree

7 files changed

+126
-66
lines changed

7 files changed

+126
-66
lines changed

package.json

+8-6
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,23 @@
3333
"npm": ">=3.0.0"
3434
},
3535
"devDependencies": {
36-
"aegir": "^20.3.1",
36+
"aegir": "^20.4.1",
3737
"chai": "^4.2.0",
3838
"dirty-chai": "^2.0.1",
39-
"interface-transport": "^0.7.0",
40-
"sinon": "^7.3.1"
39+
"libp2p-interfaces": "^0.1.6",
40+
"it-pipe": "^1.1.0",
41+
"sinon": "^7.5.0",
42+
"streaming-iterables": "^4.1.1"
4143
},
4244
"dependencies": {
43-
"abortable-iterator": "^2.1.0",
45+
"abortable-iterator": "^3.0.0",
4446
"class-is": "^1.1.0",
4547
"debug": "^4.1.1",
4648
"err-code": "^2.0.0",
4749
"libp2p-utils": "~0.1.0",
4850
"mafmt": "^7.0.0",
49-
"multiaddr": "^7.1.0",
50-
"stream-to-it": "^0.1.1"
51+
"multiaddr": "^7.2.1",
52+
"stream-to-it": "^0.2.0"
5153
},
5254
"contributors": [
5355
"Alan Shaw <[email protected]>",

src/index.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const errCode = require('err-code')
77
const log = require('debug')('libp2p:tcp')
88
const toConnection = require('./socket-to-conn')
99
const createListener = require('./listener')
10+
const { multiaddrToNetConfig } = require('./utils')
1011
const { AbortError } = require('abortable-iterator')
1112
const { CODE_CIRCUIT, CODE_P2P } = require('./constants')
1213
const assert = require('assert')
@@ -56,9 +57,9 @@ class TCP {
5657

5758
return new Promise((resolve, reject) => {
5859
const start = Date.now()
59-
const cOpts = ma.toOptions()
60+
const cOpts = multiaddrToNetConfig(ma)
6061

61-
log('dialing %s:%s', cOpts.host, cOpts.port)
62+
log('dialing %j', cOpts)
6263
const rawSocket = net.connect(cOpts)
6364

6465
const onError = err => {
@@ -74,12 +75,12 @@ class TCP {
7475
}
7576

7677
const onConnect = () => {
77-
log('connection opened %s:%s', cOpts.host, cOpts.port)
78+
log('connection opened %j', cOpts)
7879
done()
7980
}
8081

8182
const onAbort = () => {
82-
log('connection aborted %s:%s', cOpts.host, cOpts.port)
83+
log('connection aborted %j', cOpts)
8384
rawSocket.destroy()
8485
done(new AbortError())
8586
}

src/listener.js

+9-32
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
'use strict'
22

3-
const multiaddr = require('multiaddr')
4-
const os = require('os')
53
const net = require('net')
64
const EventEmitter = require('events')
75
const log = require('debug')('libp2p:tcp:listener')
86
const toConnection = require('./socket-to-conn')
97
const { CODE_P2P } = require('./constants')
10-
const ProtoFamily = { ip4: 'IPv4', ip6: 'IPv6' }
8+
const {
9+
getMultiaddrs,
10+
multiaddrToNetConfig
11+
} = require('./utils')
1112

1213
module.exports = ({ handler, upgrader }, options) => {
1314
const listener = new EventEmitter()
@@ -16,7 +17,7 @@ module.exports = ({ handler, upgrader }, options) => {
1617
// Avoid uncaught errors caused by unstable connections
1718
socket.on('error', err => log('socket error', err))
1819

19-
const maConn = toConnection(socket)
20+
const maConn = toConnection(socket, { listeningAddr })
2021
log('new inbound connection %s', maConn.remoteAddr)
2122

2223
const conn = await upgrader.upgradeInbound(maConn)
@@ -56,8 +57,8 @@ module.exports = ({ handler, upgrader }, options) => {
5657
}
5758

5859
return new Promise((resolve, reject) => {
59-
const { host, port } = listeningAddr.toOptions()
60-
server.listen(port, host, err => {
60+
const options = multiaddrToNetConfig(listeningAddr)
61+
server.listen(options, err => {
6162
if (err) return reject(err)
6263
log('Listening on %s', server.address())
6364
resolve()
@@ -76,9 +77,9 @@ module.exports = ({ handler, upgrader }, options) => {
7677
// Because TCP will only return the IPv6 version
7778
// we need to capture from the passed multiaddr
7879
if (listeningAddr.toString().startsWith('/ip4')) {
79-
addrs = addrs.concat(getMulitaddrs('ip4', address.address, address.port))
80+
addrs = addrs.concat(getMultiaddrs('ip4', address.address, address.port))
8081
} else if (address.family === 'IPv6') {
81-
addrs = addrs.concat(getMulitaddrs('ip6', address.address, address.port))
82+
addrs = addrs.concat(getMultiaddrs('ip6', address.address, address.port))
8283
}
8384

8485
return addrs.map(ma => peerId ? ma.encapsulate(`/p2p/${peerId}`) : ma)
@@ -87,30 +88,6 @@ module.exports = ({ handler, upgrader }, options) => {
8788
return listener
8889
}
8990

90-
function getMulitaddrs (proto, ip, port) {
91-
const toMa = ip => multiaddr(`/${proto}/${ip}/tcp/${port}`)
92-
return (isAnyAddr(ip) ? getNetworkAddrs(ProtoFamily[proto]) : [ip]).map(toMa)
93-
}
94-
95-
function isAnyAddr (ip) {
96-
return ['0.0.0.0', '::'].includes(ip)
97-
}
98-
99-
/**
100-
* @private
101-
* @param {string} family One of ['IPv6', 'IPv4']
102-
* @returns {string[]} an array of ip address strings
103-
*/
104-
function getNetworkAddrs (family) {
105-
return Object.values(os.networkInterfaces()).reduce((addresses, netAddrs) => {
106-
netAddrs.forEach(netAddr => {
107-
// Add the ip of each matching network interface
108-
if (netAddr.family === family) addresses.push(netAddr.address)
109-
})
110-
return addresses
111-
}, [])
112-
}
113-
11491
function trackConn (server, maConn) {
11592
server.__connections.push(maConn)
11693

src/socket-to-conn.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ const { CLOSE_TIMEOUT } = require('./constants')
1111
module.exports = (socket, options) => {
1212
options = options || {}
1313

14+
// Check if we are connected on a unix path
15+
if (options.listeningAddr && options.listeningAddr.getPath()) {
16+
options.remoteAddr = options.listeningAddr
17+
}
18+
19+
if (options.remoteAddr && options.remoteAddr.getPath()) {
20+
options.localAddr = options.remoteAddr
21+
}
22+
1423
const { sink, source } = toIterable.duplex(socket)
1524
const maConn = {
1625
async sink (source) {
@@ -40,7 +49,7 @@ module.exports = (socket, options) => {
4049

4150
conn: socket,
4251

43-
localAddr: toMultiaddr(socket.localAddress, socket.localPort),
52+
localAddr: options.localAddr || toMultiaddr(socket.localAddress, socket.localPort),
4453

4554
// If the remote address was passed, use it - it may have the peer ID encapsulated
4655
remoteAddr: options.remoteAddr || toMultiaddr(socket.remoteAddress, socket.remotePort),

src/utils.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict'
2+
3+
const multiaddr = require('multiaddr')
4+
const os = require('os')
5+
const { resolve } = require('path')
6+
const ProtoFamily = { ip4: 'IPv4', ip6: 'IPv6' }
7+
8+
function multiaddrToNetConfig (addr) {
9+
const listenPath = addr.getPath()
10+
// unix socket listening
11+
if (listenPath) {
12+
return resolve(listenPath)
13+
}
14+
// tcp listening
15+
return addr.toOptions()
16+
}
17+
18+
function getMultiaddrs (proto, ip, port) {
19+
const toMa = ip => multiaddr(`/${proto}/${ip}/tcp/${port}`)
20+
return (isAnyAddr(ip) ? getNetworkAddrs(ProtoFamily[proto]) : [ip]).map(toMa)
21+
}
22+
23+
function isAnyAddr (ip) {
24+
return ['0.0.0.0', '::'].includes(ip)
25+
}
26+
27+
/**
28+
* @private
29+
* @param {string} family One of ['IPv6', 'IPv4']
30+
* @returns {string[]} an array of ip address strings
31+
*/
32+
function getNetworkAddrs (family) {
33+
return Object.values(os.networkInterfaces()).reduce((addresses, netAddrs) => {
34+
netAddrs.forEach(netAddr => {
35+
// Add the ip of each matching network interface
36+
if (netAddr.family === family) addresses.push(netAddr.address)
37+
})
38+
return addresses
39+
}, [])
40+
}
41+
42+
module.exports = {
43+
multiaddrToNetConfig,
44+
isAnyAddr,
45+
getMultiaddrs
46+
}

test/compliance.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
'use strict'
33

44
const sinon = require('sinon')
5-
const tests = require('interface-transport')
5+
const tests = require('libp2p-interfaces/src/transport/tests')
66
const multiaddr = require('multiaddr')
77
const net = require('net')
88
const TCP = require('../src')

test/listen-dial.spec.js

+47-22
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@ const expect = chai.expect
77
chai.use(dirtyChai)
88
const TCP = require('../src')
99
const net = require('net')
10+
const os = require('os')
11+
const path = require('path')
1012
const multiaddr = require('multiaddr')
1113
const pipe = require('it-pipe')
1214
const { collect, map } = require('streaming-iterables')
1315
const isCI = process.env.CI
16+
const isWindows = os.platform() === 'win32'
17+
18+
const skipOnWindows = isWindows ? it.skip : it
1419

1520
describe('construction', () => {
1621
it('requires an upgrader', () => {
@@ -20,6 +25,7 @@ describe('construction', () => {
2025

2126
describe('listen', () => {
2227
let tcp
28+
let listener
2329

2430
beforeEach(() => {
2531
tcp = new TCP({
@@ -29,10 +35,13 @@ describe('listen', () => {
2935
}
3036
})
3137
})
38+
afterEach(async () => {
39+
listener && await listener.close()
40+
})
3241

3342
it('close listener with connections, through timeout', async () => {
3443
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
35-
const listener = tcp.createListener((conn) => {
44+
listener = tcp.createListener((conn) => {
3645
pipe(conn, conn)
3746
})
3847

@@ -54,87 +63,82 @@ describe('listen', () => {
5463
})
5564
})
5665

66+
// Windows doesn't support unix paths
67+
skipOnWindows('listen on path', async () => {
68+
const mh = multiaddr(`/unix${path.resolve(os.tmpdir(), '/tmp/p2pd.sock')}`)
69+
70+
listener = tcp.createListener((conn) => {})
71+
await listener.listen(mh)
72+
})
73+
5774
it('listen on port 0', async () => {
5875
const mh = multiaddr('/ip4/127.0.0.1/tcp/0')
59-
const listener = tcp.createListener((conn) => {})
76+
listener = tcp.createListener((conn) => {})
6077
await listener.listen(mh)
61-
await listener.close()
6278
})
6379

6480
it('listen on IPv6 addr', async () => {
6581
if (isCI) {
6682
return
6783
}
6884
const mh = multiaddr('/ip6/::/tcp/9090')
69-
const listener = tcp.createListener((conn) => {})
85+
listener = tcp.createListener((conn) => {})
7086
await listener.listen(mh)
71-
await listener.close()
7287
})
7388

7489
it('listen on any Interface', async () => {
7590
const mh = multiaddr('/ip4/0.0.0.0/tcp/9090')
76-
const listener = tcp.createListener((conn) => {})
91+
listener = tcp.createListener((conn) => {})
7792
await listener.listen(mh)
78-
await listener.close()
7993
})
8094

8195
it('getAddrs', async () => {
8296
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090')
83-
const listener = tcp.createListener((conn) => {})
97+
listener = tcp.createListener((conn) => {})
8498
await listener.listen(mh)
8599

86100
const multiaddrs = listener.getAddrs()
87101
expect(multiaddrs.length).to.equal(1)
88102
expect(multiaddrs[0]).to.deep.equal(mh)
89-
90-
await listener.close()
91103
})
92104

93105
it('getAddrs on port 0 listen', async () => {
94106
const mh = multiaddr('/ip4/127.0.0.1/tcp/0')
95-
const listener = tcp.createListener((conn) => {})
107+
listener = tcp.createListener((conn) => {})
96108
await listener.listen(mh)
97109

98110
const multiaddrs = listener.getAddrs()
99111
expect(multiaddrs.length).to.equal(1)
100-
101-
await listener.close()
102112
})
103113

104114
it('getAddrs from listening on 0.0.0.0', async () => {
105115
const mh = multiaddr('/ip4/0.0.0.0/tcp/9090')
106-
const listener = tcp.createListener((conn) => {})
116+
listener = tcp.createListener((conn) => {})
107117
await listener.listen(mh)
108118

109119
const multiaddrs = listener.getAddrs()
110120
expect(multiaddrs.length > 0).to.equal(true)
111121
expect(multiaddrs[0].toString().indexOf('0.0.0.0')).to.equal(-1)
112-
113-
await listener.close()
114122
})
115123

116124
it('getAddrs from listening on 0.0.0.0 and port 0', async () => {
117125
const mh = multiaddr('/ip4/0.0.0.0/tcp/0')
118-
const listener = tcp.createListener((conn) => {})
126+
listener = tcp.createListener((conn) => {})
119127
await listener.listen(mh)
120128

121129
const multiaddrs = listener.getAddrs()
122130
expect(multiaddrs.length > 0).to.equal(true)
123131
expect(multiaddrs[0].toString().indexOf('0.0.0.0')).to.equal(-1)
124-
125-
await listener.close()
126132
})
127133

128134
it('getAddrs preserves IPFS Id', async () => {
129135
const mh = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw')
130-
const listener = tcp.createListener((conn) => {})
136+
listener = tcp.createListener((conn) => {})
131137
await listener.listen(mh)
132138

133139
const multiaddrs = listener.getAddrs()
134140
expect(multiaddrs.length).to.equal(1)
135141
expect(multiaddrs[0]).to.deep.equal(mh)
136-
137-
await listener.close()
138142
})
139143
})
140144

@@ -192,6 +196,27 @@ describe('dial', () => {
192196
await listener.close()
193197
})
194198

199+
// Windows doesn't support unix paths
200+
skipOnWindows('dial on path', async () => {
201+
const ma = multiaddr(`/unix${path.resolve(os.tmpdir(), '/tmp/p2pd.sock')}`)
202+
203+
const listener = tcp.createListener((conn) => {
204+
pipe(conn, conn)
205+
})
206+
await listener.listen(ma)
207+
208+
const connection = await tcp.dial(ma)
209+
210+
const values = await pipe(
211+
['hey'],
212+
connection,
213+
collect
214+
)
215+
216+
expect(values).to.be.eql([Buffer.from('hey')])
217+
await listener.close()
218+
})
219+
195220
it('dial and destroy on listener', async () => {
196221
let handled
197222
const handledPromise = new Promise((resolve) => {

0 commit comments

Comments
 (0)