diff --git a/README.md b/README.md index f47baba..1e3d2ce 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,16 @@ js-libp2p-tcp [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) -[![Build Status](https://travis-ci.org/diasdavid/js-libp2p-tcp.svg?style=flat-square)](https://travis-ci.org/diasdavid/js-libp2p-tcp) +[![Build Status](https://travis-ci.org/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://travis-ci.org/libp2p/js-libp2p-tcp) ![](https://img.shields.io/badge/coverage-%3F-yellow.svg?style=flat-square) -[![Dependency Status](https://david-dm.org/diasdavid/js-libp2p-tcp.svg?style=flat-square)](https://david-dm.org/diasdavid/js-libp2p-tcp) +[![Dependency Status](https://david-dm.org/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-tcp) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard) -![](https://raw.githubusercontent.com/diasdavid/abstract-connection/master/img/badge.png) -![](https://raw.githubusercontent.com/diasdavid/abstract-transport/master/img/badge.png) +![](https://raw.githubusercontent.com/libp2p/interface-connection/master/img/badge.png) +![](https://raw.githubusercontent.com/libp2p/interface-transport/master/img/badge.png) > Node.js implementation of the TCP module that libp2p uses, which implements -> the [interface-connection](https://github.com/diasdavid/interface-connection) +> the [interface-connection](https://github.com/libp2p/interface-connection) > interface for dial/listen. ## Description @@ -24,7 +24,7 @@ transports. ## Example ```js -const Tcp = require('libp2p-tcp') +const TCP = require('libp2p-tcp') const multiaddr = require('multiaddr') const mh1 = multiaddr('/ip4/127.0.0.1/tcp/9090') @@ -32,10 +32,12 @@ const mh2 = multiaddr('/ip6/::/tcp/9092') const tcp = new Tcp() -tcp.createListener([mh1, mh2], function handler (socket) { +var listener = tcp.createListener(mh1, function handler (socket) { console.log('connection') socket.end('bye') -}, function ready () { +}) + +var listener.listen(function ready () { console.log('ready') const client = tcp.dial(mh1) @@ -65,31 +67,14 @@ bye ## API -```js -const Tcp = require('libp2p-tcp') -``` +[![](https://raw.githubusercontent.com/diasdavid/interface-transport/master/img/badge.png)](https://github.com/diasdavid/interface-transport) -### var tcp = new Tcp() +`libp2p-tcp` accepts TCP addresses both IPFS and non IPFS encapsulated addresses, i.e: -Creates a new TCP object. This does nothing on its own, but provides access to -`dial` and `createListener`. +`/ip4/127.0.0.1/tcp/4001` +`/ip4/127.0.0.1/tcp/4001/ipfs/QmHash` -### tcp.createListener(multiaddrs, handler, ready) - -Creates TCP servers that listen on the addresses described in the array -`multiaddrs`. Each connection will call `handler` with a connection stream. -`ready` is called once all servers are listening. - -### tcp.dial(multiaddr, options={}) - -Connects to the multiaddress `multiaddr` using TCP, returning the socket stream. -If `options.ready` is set to a function, it is called when a connection is -established. - -### tcp.close(callback) - -Closes all the listening TCP servers, calling `callback` once all of them have -been shut down. +Both for dialing and listening. ## License diff --git a/package.json b/package.json index d4c62ce..e0f3b88 100644 --- a/package.json +++ b/package.json @@ -34,12 +34,13 @@ "devDependencies": { "aegir": "^3.0.4", "chai": "^3.5.0", - "interface-connection": "0.0.3", - "interface-transport": "^0.1.1", + "interface-transport": "^0.2.0", "pre-commit": "^1.1.2", "tape": "^4.5.1" }, "dependencies": { + "duplexify": "^3.4.3", + "interface-connection": "0.1.2", "ip-address": "^5.8.0", "lodash.contains": "^2.4.3", "mafmt": "^2.1.0", @@ -53,4 +54,4 @@ "Stephen Whitmore ", "dignifiedquire " ] -} \ No newline at end of file +} diff --git a/src/index.js b/src/index.js index 037aa10..688810a 100644 --- a/src/index.js +++ b/src/index.js @@ -6,68 +6,166 @@ const tcp = require('net') const multiaddr = require('multiaddr') const Address6 = require('ip-address').Address6 const mafmt = require('mafmt') -const parallel = require('run-parallel') +// const parallel = require('run-parallel') const contains = require('lodash.contains') +const os = require('os') +const Connection = require('interface-connection').Connection exports = module.exports = TCP const IPFS_CODE = 421 -const CLOSE_TIMEOUT = 300 +const CLOSE_TIMEOUT = 2000 function TCP () { if (!(this instanceof TCP)) { return new TCP() } - const listeners = [] - - this.dial = function (multiaddr, options) { - if (!options) { + this.dial = function (ma, options, callback) { + if (typeof options === 'function') { + callback = options options = {} } - options.ready = options.ready || function noop () {} - const conn = tcp.connect(multiaddr.toOptions(), options.ready) - conn.getObservedAddrs = () => { - return [multiaddr] + + if (!callback) { + callback = function noop () {} } + + const socket = tcp.connect(ma.toOptions()) + const conn = new Connection(socket) + + socket.on('timeout', () => { + conn.emit('timeout') + }) + + socket.on('error', (err) => { + callback(err) + conn.emit('error', err) + }) + + socket.on('connect', () => { + callback(null, conn) + conn.emit('connect') + }) + + conn.getObservedAddrs = (cb) => { + return cb(null, [ma]) + } + return conn } - this.createListener = (multiaddrs, handler, callback) => { - if (!Array.isArray(multiaddrs)) { - multiaddrs = [multiaddrs] + this.createListener = (options, handler) => { + if (typeof options === 'function') { + handler = options + options = {} } - const freshMultiaddrs = [] + const listener = tcp.createServer((socket) => { + const conn = new Connection(socket) - parallel(multiaddrs.map((m) => (cb) => { - let ipfsHashId - if (contains(m.protoNames(), 'ipfs')) { - ipfsHashId = m.stringTuples().filter((tuple) => { + conn.getObservedAddrs = (cb) => { + return cb(null, [getMultiaddr(socket)]) + } + handler(conn) + }) + + let ipfsId + let listeningMultiaddr + + listener._listen = listener.listen + listener.listen = (ma, callback) => { + listeningMultiaddr = ma + if (contains(ma.protoNames(), 'ipfs')) { + ipfsId = ma.stringTuples().filter((tuple) => { if (tuple[0] === IPFS_CODE) { return true } })[0][1] - m = m.decapsulate('ipfs') + listeningMultiaddr = ma.decapsulate('ipfs') } - const listener = tcp.createServer((conn) => { - conn.getObservedAddrs = () => { - return [getMultiaddr(conn)] + listener._listen(listeningMultiaddr.toOptions(), callback) + } + + listener._close = listener.close + listener.close = (options, callback) => { + if (typeof options === 'function') { + callback = options + options = {} + } + if (!callback) { callback = function noop () {} } + if (!options) { options = {} } + + let closed = false + listener._close(callback) + listener.once('close', () => { + closed = true + }) + setTimeout(() => { + if (closed) { + return } - handler(conn) - }) - - listener.__connections = {} - listener.on('connection', (conn) => { - const key = `${conn.remoteAddress}:${conn.remotePort}` - listener.__connections[key] = conn - - conn.on('close', () => { - delete listener.__connections[key] + log('unable to close graciously, destroying conns') + Object.keys(listener.__connections).forEach((key) => { + log('destroying %s', key) + listener.__connections[key].destroy() }) - }) + }, options.timeout || CLOSE_TIMEOUT) + } + // Keep track of open connections to destroy in case of timeout + listener.__connections = {} + listener.on('connection', (socket) => { + const key = `${socket.remoteAddress}:${socket.remotePort}` + listener.__connections[key] = socket + + socket.on('close', () => { + delete listener.__connections[key] + }) + }) + + listener.getAddrs = (callback) => { + const multiaddrs = [] + const address = listener.address() + + // Because TCP will only return the IPv6 version + // we need to capture from the passed multiaddr + if (listeningMultiaddr.toString().indexOf('ip4') !== -1) { + let m = listeningMultiaddr.decapsulate('tcp') + m = m.encapsulate('/tcp/' + address.port) + if (ipfsId) { + m = m.encapsulate('/ipfs/' + ipfsId) + } + + if (m.toString().indexOf('0.0.0.0') !== -1) { + const netInterfaces = os.networkInterfaces() + Object.keys(netInterfaces).forEach((niKey) => { + netInterfaces[niKey].forEach((ni) => { + if (ni.family === 'IPv4') { + multiaddrs.push(multiaddr(m.toString().replace('0.0.0.0', ni.address))) + } + }) + }) + } else { + multiaddrs.push(m) + } + } + + if (address.family === 'IPv6') { + let ma = multiaddr('/ip6/' + address.address + '/tcp/' + address.port) + if (ipfsId) { + ma = ma.encapsulate('/ipfs/' + ipfsId) + } + + multiaddrs.push(ma) + } + + callback(null, multiaddrs) + } + + return listener + /* listener.listen(m.toOptions(), () => { // Node.js likes to convert addr to IPv6 (when 0.0.0.0 for e.g) const address = listener.address() @@ -92,28 +190,7 @@ function TCP () { cb() }) listeners.push(listener) - }), (err) => { - callback(err, freshMultiaddrs) - }) - } - - this.close = (callback) => { - log('closing') - if (listeners.length === 0) { - log('Called close with no active listeners') - return callback() - } - - parallel(listeners.map((listener) => (cb) => { - setTimeout(() => { - Object.keys(listener.__connections).forEach((key) => { - log('destroying %s', key) - listener.__connections[key].destroy() - }) - }, CLOSE_TIMEOUT) - - listener.close(cb) - }), callback) + */ } this.filter = (multiaddrs) => { @@ -129,19 +206,19 @@ function TCP () { } } -function getMultiaddr (conn) { +function getMultiaddr (socket) { var mh - if (conn.remoteFamily === 'IPv6') { - var addr = new Address6(conn.remoteAddress) + if (socket.remoteFamily === 'IPv6') { + var addr = new Address6(socket.remoteAddress) if (addr.v4) { var ip4 = addr.to4().correctForm() - mh = multiaddr('/ip4/' + ip4 + '/tcp/' + conn.remotePort) + mh = multiaddr('/ip4/' + ip4 + '/tcp/' + socket.remotePort) } else { - mh = multiaddr('/ip6/' + conn.remoteAddress + '/tcp/' + conn.remotePort) + mh = multiaddr('/ip6/' + socket.remoteAddress + '/tcp/' + socket.remotePort) } } else { - mh = multiaddr('/ip4/' + conn.remoteAddress + '/tcp/' + conn.remotePort) + mh = multiaddr('/ip4/' + socket.remoteAddress + '/tcp/' + socket.remotePort) } return mh diff --git a/test/index.spec.js b/test/index.spec.js deleted file mode 100644 index df3ed8a..0000000 --- a/test/index.spec.js +++ /dev/null @@ -1,182 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const expect = require('chai').expect -const TCPlibp2p = require('../src') -const net = require('net') -const multiaddr = require('multiaddr') - -describe('libp2p-tcp', function () { - this.timeout(10000) - var tcp - - it('create', (done) => { - tcp = new TCPlibp2p() - expect(tcp).to.exist - done() - }) - - it('create without new', (done) => { - tcp = TCPlibp2p() - expect(tcp).to.exist - done() - }) - - it('close /wo listeners', (done) => { - tcp = new TCPlibp2p() - expect(tcp).to.exist - expect(function () { tcp.close() }).to.throw(Error) - done() - }) - - it('listen', (done) => { - const mh = multiaddr('/ip4/127.0.0.1/tcp/9090') - tcp.createListener(mh, (socket) => { - expect(socket).to.exist - socket.end() - tcp.close(() => { - done() - }) - }, (err, freshMultiaddrs) => { - expect(err).to.not.exist - expect(mh).to.deep.equal(freshMultiaddrs[0]) - const socket = net.connect({ host: '127.0.0.1', port: 9090 }) - socket.end() - }) - }) - - it('listen on addr with /ipfs/QmHASH', (done) => { - const mh = multiaddr('/ip4/127.0.0.1/tcp/14090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw') - tcp.createListener(mh, (socket) => { - expect(socket).to.exist - socket.end() - tcp.close(() => { - done() - }) - }, (err, freshMultiaddrs) => { - expect(err).to.not.exist - expect(mh).to.deep.equal(freshMultiaddrs[0]) - const socket = net.connect({ host: '127.0.0.1', port: 14090 }) - socket.end() - }) - }) - - it('dial', (done) => { - const server = net.createServer((socket) => { - expect(socket).to.exist - socket.end() - server.close(done) - }) - - server.listen(9090, () => { - const mh = multiaddr('/ip4/127.0.0.1/tcp/9090') - const socket = tcp.dial(mh) - socket.end() - }) - }) - - it('listen on several', (done) => { - const mh1 = multiaddr('/ip4/127.0.0.1/tcp/9090') - const mh2 = multiaddr('/ip4/127.0.0.1/tcp/9091') - const mh3 = multiaddr('/ip6/::/tcp/9092') - const tcp = new TCPlibp2p() - - tcp.createListener([mh1, mh2, mh3], (socket) => {}, () => { - tcp.close(done) - }) - }) - - it('dial ipv6', (done) => { - const mh = multiaddr('/ip6/::/tcp/9091') - var dialerObsAddrs - - tcp.createListener(mh, (conn) => { - expect(conn).to.exist - dialerObsAddrs = conn.getObservedAddrs() - conn.end() - }, () => { - const conn = tcp.dial(mh) - conn.on('end', () => { - expect(dialerObsAddrs.length).to.equal(1) - tcp.close() - done() - }) - }) - }) - - it('get observed addrs', (done) => { - const mh = multiaddr('/ip4/127.0.0.1/tcp/9090') - var dialerObsAddrs - var listenerObsAddrs - - tcp.createListener(mh, (conn) => { - expect(conn).to.exist - dialerObsAddrs = conn.getObservedAddrs() - conn.end() - }, () => { - const conn = tcp.dial(mh) - conn.on('end', () => { - listenerObsAddrs = conn.getObservedAddrs() - conn.end() - - tcp.close(() => { - expect(listenerObsAddrs[0]).to.deep.equal(mh) - expect(dialerObsAddrs.length).to.equal(1) - done() - }) - }) - }) - }) - - it('filter valid addrs for this transport', (done) => { - const mh1 = multiaddr('/ip4/127.0.0.1/tcp/9090') - const mh2 = multiaddr('/ip4/127.0.0.1/udp/9090') - const mh3 = multiaddr('/ip4/127.0.0.1/tcp/9090/http') - const mh4 = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw') - - const valid = tcp.filter([mh1, mh2, mh3, mh4]) - expect(valid.length).to.equal(2) - expect(valid[0]).to.deep.equal(mh1) - done() - }) - - it('filter a valid addr for this transport', (done) => { - const mh1 = multiaddr('/ip4/127.0.0.1/tcp/9090') - - const valid = tcp.filter(mh1) - expect(valid.length).to.equal(1) - expect(valid[0]).to.deep.equal(mh1) - done() - }) - - it('destroys after timeout', (done) => { - const server = new TCPlibp2p() - const mh = multiaddr('/ip4/127.0.0.1/tcp/9090') - server.createListener(mh, (conn) => { - const i = setInterval(() => { - conn.read() - conn.write('hi\n') - }, 100) - i.unref() - }, () => { - let connected = 0 - const connectHandler = () => { - connected++ - if (connected === 10) { - setTimeout(() => { - server.close(done) - }, 1) - } - } - const errorHandler = () => {} - - for (let i = 0; i < 10; i++) { - const client = net.connect(9090) - client.on('connect', connectHandler) - - // just ignore the resets - client.on('error', errorHandler) - } - }) - }) -}) diff --git a/test/libp2p-tcp.spec.js b/test/libp2p-tcp.spec.js new file mode 100644 index 0000000..003f897 --- /dev/null +++ b/test/libp2p-tcp.spec.js @@ -0,0 +1,474 @@ +/* eslint-env mocha */ +'use strict' + +const expect = require('chai').expect +const TCP = require('../src') +const net = require('net') +const multiaddr = require('multiaddr') +const Connection = require('interface-connection').Connection + +describe('instantiate the transport', () => { + it('create', (done) => { + const tcp = new TCP() + expect(tcp).to.exist + done() + }) + + it('create without new', (done) => { + const tcp = TCP() + expect(tcp).to.exist + done() + }) +}) + +describe('listen', () => { + let tcp + + beforeEach(() => { + tcp = new TCP() + }) + + it('listen, check for callback', (done) => { + const mh = multiaddr('/ip4/127.0.0.1/tcp/9090') + const listener = tcp.createListener((conn) => {}) + listener.listen(mh, () => { + listener.close(done) + }) + }) + + it('listen, check for listening event', (done) => { + const mh = multiaddr('/ip4/127.0.0.1/tcp/9090') + const listener = tcp.createListener((conn) => {}) + listener.on('listening', () => { + listener.close(done) + }) + listener.listen(mh) + }) + + it('listen, check for the close event', (done) => { + const mh = multiaddr('/ip4/127.0.0.1/tcp/9090') + const listener = tcp.createListener((conn) => {}) + listener.on('close', done) + listener.on('listening', () => { + listener.close() + }) + listener.listen(mh) + }) + + it('listen on addr with /ipfs/QmHASH', (done) => { + const mh = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw') + const listener = tcp.createListener((conn) => {}) + listener.listen(mh, () => { + listener.close(done) + }) + }) + + it('close listener with connections, through timeout', (done) => { + const mh = multiaddr('/ip4/127.0.0.1/tcp/9091/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw') + const listener = tcp.createListener((conn) => { + conn.pipe(conn) + }) + listener.listen(mh, () => { + const socket1 = net.connect(9091) + const socket2 = net.connect(9091) + socket1.write('Some data that is never handled') + socket1.end() + socket1.on('error', () => {}) + socket2.on('error', () => {}) + socket1.on('connect', () => { + listener.close(done) + }) + }) + }) + + it('listen on port 0', (done) => { + const mh = multiaddr('/ip4/127.0.0.1/tcp/0') + const listener = tcp.createListener((conn) => {}) + listener.listen(mh, () => { + listener.close(done) + }) + }) + + it('listen on IPv6 addr', (done) => { + const mh = multiaddr('/ip6/::/tcp/9090') + const listener = tcp.createListener((conn) => {}) + listener.listen(mh, () => { + listener.close(done) + }) + }) + + it('listen on any Interface', (done) => { + const mh = multiaddr('/ip4/0.0.0.0/tcp/9090') + const listener = tcp.createListener((conn) => {}) + listener.listen(mh, () => { + listener.close(done) + }) + }) + + it('getAddrs', (done) => { + const mh = multiaddr('/ip4/127.0.0.1/tcp/9090') + const listener = tcp.createListener((conn) => {}) + listener.listen(mh, () => { + listener.getAddrs((err, multiaddrs) => { + expect(err).to.not.exist + expect(multiaddrs.length).to.equal(1) + // multiaddrs.forEach((ma) => { + // console.log(ma.toString()) + // }) + expect(multiaddrs[0]).to.deep.equal(mh) + listener.close(done) + }) + }) + }) + + it('getAddrs on port 0 listen', (done) => { + const mh = multiaddr('/ip4/127.0.0.1/tcp/0') + const listener = tcp.createListener((conn) => {}) + listener.listen(mh, () => { + listener.getAddrs((err, multiaddrs) => { + expect(err).to.not.exist + expect(multiaddrs.length).to.equal(1) + // multiaddrs.forEach((ma) => { + // console.log(ma.toString()) + // }) + + listener.close(done) + }) + }) + }) + + it('getAddrs from listening on 0.0.0.0', (done) => { + const mh = multiaddr('/ip4/0.0.0.0/tcp/9090') + const listener = tcp.createListener((conn) => {}) + listener.listen(mh, () => { + listener.getAddrs((err, multiaddrs) => { + expect(err).to.not.exist + expect(multiaddrs.length > 0).to.equal(true) + expect(multiaddrs[0].toString().indexOf('0.0.0.0')).to.equal(-1) + // multiaddrs.forEach((ma) => { + // console.log(ma.toString()) + // }) + listener.close(done) + }) + }) + }) + + it('getAddrs from listening on 0.0.0.0 and port 0', (done) => { + const mh = multiaddr('/ip4/0.0.0.0/tcp/0') + const listener = tcp.createListener((conn) => {}) + listener.listen(mh, () => { + listener.getAddrs((err, multiaddrs) => { + expect(err).to.not.exist + expect(multiaddrs.length > 0).to.equal(true) + expect(multiaddrs[0].toString().indexOf('0.0.0.0')).to.equal(-1) + // multiaddrs.forEach((ma) => { + // console.log(ma.toString()) + // }) + listener.close(done) + }) + }) + }) + + it('getAddrs preserves IPFS Id', (done) => { + const mh = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw') + const listener = tcp.createListener((conn) => {}) + listener.listen(mh, () => { + listener.getAddrs((err, multiaddrs) => { + expect(err).to.not.exist + expect(multiaddrs.length).to.equal(1) + // multiaddrs.forEach((ma) => { + // console.log(ma.toString()) + // }) + expect(multiaddrs[0]).to.deep.equal(mh) + listener.close(done) + }) + }) + }) +}) + +describe('dial', () => { + let tcp + let listener + const ma = multiaddr('/ip4/127.0.0.1/tcp/9090') + + beforeEach((done) => { + tcp = new TCP() + listener = tcp.createListener((conn) => { + conn.pipe(conn) + }) + listener.on('listening', done) + listener.listen(ma) + }) + + afterEach((done) => { + listener.close(done) + }) + + it('dial on IPv4', (done) => { + const conn = tcp.dial(ma) + conn.write('hey') + conn.end() + conn.on('data', (chunk) => { + expect(chunk.toString()).to.equal('hey') + }) + conn.on('end', done) + }) + + it('dial on IPv6', (done) => { + const ma = multiaddr('/ip6/::/tcp/9066') + const listener = tcp.createListener((conn) => { + conn.pipe(conn) + }) + listener.listen(ma, dialStep) + + function dialStep () { + const conn = tcp.dial(ma) + conn.write('hey') + conn.end() + conn.on('data', (chunk) => { + expect(chunk.toString()).to.equal('hey') + }) + conn.on('end', () => { + listener.close(done) + }) + } + }) + + it('dial on IPv4 with IPFS Id', (done) => { + const ma = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw') + const conn = tcp.dial(ma) + conn.write('hey') + conn.end() + conn.on('data', (chunk) => { + expect(chunk.toString()).to.equal('hey') + }) + conn.on('end', done) + }) +}) + +describe('filter addrs', () => { + let tcp + + before(() => { + tcp = new TCP() + }) + + it('filter valid addrs for this transport', (done) => { + const mh1 = multiaddr('/ip4/127.0.0.1/tcp/9090') + const mh2 = multiaddr('/ip4/127.0.0.1/udp/9090') + const mh3 = multiaddr('/ip4/127.0.0.1/tcp/9090/http') + const mh4 = multiaddr('/ip4/127.0.0.1/tcp/9090/ipfs/Qmb6owHp6eaWArVbcJJbQSyifyJBttMMjYV76N2hMbf5Vw') + + const valid = tcp.filter([mh1, mh2, mh3, mh4]) + expect(valid.length).to.equal(2) + expect(valid[0]).to.deep.equal(mh1) + expect(valid[1]).to.deep.equal(mh4) + done() + }) + + it('filter a single addr for this transport', (done) => { + const mh1 = multiaddr('/ip4/127.0.0.1/tcp/9090') + + const valid = tcp.filter(mh1) + expect(valid.length).to.equal(1) + expect(valid[0]).to.deep.equal(mh1) + done() + }) +}) + +describe('valid Connection', () => { + let tcp + + beforeEach(() => { + tcp = new TCP() + }) + + const ma = multiaddr('/ip4/127.0.0.1/tcp/9090') + + it('get observed addrs', (done) => { + var dialerObsAddrs + var listenerObsAddrs + + const listener = tcp.createListener((conn) => { + expect(conn).to.exist + conn.getObservedAddrs((err, addrs) => { + expect(err).to.not.exist + dialerObsAddrs = addrs + conn.end() + }) + }) + + listener.listen(ma, () => { + const conn = tcp.dial(ma) + + conn.resume() + conn.on('end', () => { + conn.getObservedAddrs((err, addrs) => { + expect(err).to.not.exist + listenerObsAddrs = addrs + conn.end() + + listener.close(() => { + expect(listenerObsAddrs[0]).to.deep.equal(ma) + expect(dialerObsAddrs.length).to.equal(1) + done() + }) + }) + }) + }) + }) + + it('get Peer Info', (done) => { + const listener = tcp.createListener((conn) => { + expect(conn).to.exist + conn.getPeerInfo((err, peerInfo) => { + expect(err).to.exist + expect(peerInfo).to.not.exist + conn.end() + }) + }) + + listener.listen(ma, () => { + const conn = tcp.dial(ma) + + conn.resume() + conn.on('end', () => { + conn.getPeerInfo((err, peerInfo) => { + expect(err).to.exist + expect(peerInfo).to.not.exist + conn.end() + + listener.close(done) + }) + }) + }) + }) + + it('set Peer Info', (done) => { + const listener = tcp.createListener((conn) => { + expect(conn).to.exist + conn.setPeerInfo('batatas') + conn.getPeerInfo((err, peerInfo) => { + expect(err).to.not.exist + expect(peerInfo).to.equal('batatas') + conn.end() + }) + }) + + listener.listen(ma, () => { + const conn = tcp.dial(ma) + + conn.resume() + conn.on('end', () => { + conn.setPeerInfo('arroz') + conn.getPeerInfo((err, peerInfo) => { + expect(err).to.not.exist + expect(peerInfo).to.equal('arroz') + conn.end() + + listener.close(done) + }) + }) + }) + }) +}) + +describe.skip('turbolence', () => { + it('dialer - emits error on the other end is terminated abruptly', (done) => {}) + it('listener - emits error on the other end is terminated abruptly', (done) => {}) +}) + +describe('Connection wrap', () => { + let tcp + let listener + const ma = multiaddr('/ip4/127.0.0.1/tcp/9090') + + beforeEach((done) => { + tcp = new TCP() + listener = tcp.createListener((conn) => { + conn.pipe(conn) + }) + listener.on('listening', done) + listener.listen(ma) + }) + + afterEach((done) => { + listener.close(done) + }) + + it('simple wrap', (done) => { + const conn = tcp.dial(ma) + conn.setPeerInfo('peerInfo') + const connWrap = new Connection(conn) + connWrap.write('hey') + connWrap.end() + connWrap.on('data', (chunk) => { + expect(chunk.toString()).to.equal('hey') + }) + connWrap.on('end', () => { + connWrap.getPeerInfo((err, peerInfo) => { + expect(err).to.not.exist + expect(peerInfo).to.equal('peerInfo') + done() + }) + }) + }) + + it('buffer wrap', (done) => { + const conn = tcp.dial(ma) + const connWrap = new Connection() + connWrap.write('hey') + connWrap.end() + connWrap.on('data', (chunk) => { + expect(chunk.toString()).to.equal('hey') + }) + connWrap.on('end', done) + + connWrap.setInnerConn(conn) + }) + + it('overload wrap', (done) => { + const conn = tcp.dial(ma) + const connWrap = new Connection(conn) + connWrap.getPeerInfo = (callback) => { + callback(null, 'none') + } + conn.getPeerInfo((err, peerInfo) => { + expect(err).to.exist + }) + connWrap.getPeerInfo((err, peerInfo) => { + expect(err).to.not.exist + expect(peerInfo).to.equal('none') + }) + connWrap.write('hey') + connWrap.end() + connWrap.on('data', (chunk) => { + expect(chunk.toString()).to.equal('hey') + }) + connWrap.on('end', done) + }) + + it('matryoshka wrap', (done) => { + const conn = tcp.dial(ma) + const connWrap1 = new Connection(conn) + const connWrap2 = new Connection(connWrap1) + const connWrap3 = new Connection(connWrap2) + + conn.getPeerInfo = (callback) => { + callback(null, 'inner doll') + } + + connWrap3.write('hey') + connWrap3.end() + connWrap3.on('data', (chunk) => { + expect(chunk.toString()).to.equal('hey') + }) + connWrap3.on('end', () => { + connWrap3.getPeerInfo((err, peerInfo) => { + expect(err).to.not.exist + expect(peerInfo).to.equal('inner doll') + done() + }) + }) + }) +})